В зависимости от фактического формата вашего файла данных, вы можете усложнять (и более подвержены ошибкам), чем это должно быть. Как отмечает @ JonathanLeffler в своем комментарии, ваш файл данных должен (в идеале) содержать одну запись учетной записи на строку. Не так, как вы показали с разрывами строк, разбросанными по каждой записи.
Если вы столкнулись с файлом с разбросанным разрывом строки (а также с пробелами, окружающими разделители), вам может быть лучше переформатировать файл данных.
Несмотря на то, что у вас есть несколько ответов, обсуждающих поляризованный ввод для обработки разбросанных разрывов строк, общий подход, когда каждая запись представляет собой одну строку, заключается в чтении файла данных по одной строке за раз (a линейно-ориентированный ) подход. Это позволяет читать с fgets
(или POSIX getline
).
Читая строку за раз, вы должны воспользоваться отдельной проверкой (1) чтения самой строки; и (2) анализ необходимой вам информации в каждой строке.
Подход довольно прост. Читайте строку за раз в фиксированном буфере достаточного размера или пусть getline
динамически выделяет столько памяти, сколько требуется. После считывания каждой строки в буфер вы можете проанализировать буфер по отдельным полям с помощью (1) используя форматированный анализ с sscanf
; (2) использование strtok
для доступа к каждому полю (пустые поля не допускаются); (3) использование strsep
аналогично strtok
, но учитывает пустые поля; и (4) вы всегда можете пройтись по паре указателей вниз по буферу, проверяя каждый символ и обрабатывая анализ по мере необходимости.
Здесь, поскольку вы пытаетесь использовать fscanf
, мы будем использовать sscanf
, что является близким следствием. (вы можете как читать, так и анализировать с помощью одного вызова fscanf
, но вы теряете преимущество раздельной проверки чтения и анализа - это ваше дело)
При использовании sscanf
вы анализируете информацию так же, как и при fscanf
, за исключением того, что вы читаете информацию из заполненного буфера, а не из файла. Выбор строки формата является критическим и должен защищать любые границы массива, которые вы используете, с помощью модификатора width width .
( примечание: пробел, окружающий каждый разделитель, был странным и неясным, было ли это намеренным или случайным. Приведенное ниже чтение не предполагает ничего, но будет использовать любые начальные пробелы, оставляя вам возможность обрезать любые пробелы в конце между концом поля и разделителем для символьных полей)
Имея это в виду, вы можете читать и анализировать информацию из вашего файла данных следующим образом:
char buf[MAXC] = ""; /* buffer to read line at a time */
size_t n = 0; /* line index */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
...
if (!fgets (buf, MAXC, fp)) { /* read/discard heading row */
fprintf (stderr, "error: failed to read 1st line.\n");
return 1;
}
...
n++;
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
veiculos tmp = { .lote = 0 }; /* temp struct to fill */
/* validate the return of sscanf to validate parse/conversions */
if (sscanf (buf, "%d; %49[^;]; %9[^;]; %49[^;]; %d; %99[^;]; %d; "
"%99[^;]; %99[^;]; %99[^;]; %f",
&tmp.lote, tmp.placa, tmp.uf, tmp.motor, &tmp.renavam,
tmp.chassi, &tmp.ano, tmp.marca, tmp.proprietario,
tmp.financeira, &tmp.valor) != 11) {
/* if less than 11 returned, failed read, handle error */
fprintf (stderr, "read-error: line %zu.\n", n);
continue; /* get next line */
}
/* ==> add tmp to binary tree here <==
* (note: you will need to trim trailing whitespace from strings)
*/
printf ("line[%3zu]: %5d %10s %4s %24s ...\n",
n, tmp.lote, tmp.placa, tmp.uf, tmp.motor);
n++;
}
Не используйте магические числа в вашем коде. (например, [10] [50] [100]
и т. д.) Вместо этого, если вам нужны константы для вашего кода #define
их или используйте глобальный enum
для их определения.
Всегда проверять все входы (особенно пользовательский ввод). Ошибка проверки возврата ваших функций ввода является приглашением для неопределенного поведения и бесконечных циклов , так как у вас нет способа обнаружить вход или совпадение сбой или обнаружение EOF
. Если вам не удается обнаружить какое-либо из этих условий и вслепую продолжать пытаться читать из потока в состоянии ошибки, вы будете вызывать Неопределенное поведение .
Сложив эти фрагменты, вы можете приблизиться к чтению информации о вашей учетной записи, аналогично следующему (добавление этой информации к вашему btree вам), например,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* don't put 'magic numbers' in your code, if you need constants,
* #define them, or use an enum as below:
*/
enum { MAXU = 10, MAXP = 50, MAXI = 100, MAXC = 1024 };
typedef struct { /* typedef for struct */
int lote;
char placa[MAXP];
char uf[MAXU];
char motor[MAXP];
int renavam;
char chassi[MAXI];
int ano;
char marca[MAXI];
char proprietario[MAXI];
char financeira[MAXI];
float valor;
} veiculos;
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* buffer to read line at a time */
size_t n = 0; /* line index */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!fgets (buf, MAXC, fp)) { /* read/discard heading row */
fprintf (stderr, "error: failed to read 1st line.\n");
return 1;
}
printf ("header line stripped\n\n");
n++;
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
veiculos tmp = { .lote = 0 }; /* temp struct to fill */
/* validate the return of sscanf to validate parse/conversions */
if (sscanf (buf, "%d; %49[^;]; %9[^;]; %49[^;]; %d; %99[^;]; %d; "
"%99[^;]; %99[^;]; %99[^;]; %f",
&tmp.lote, tmp.placa, tmp.uf, tmp.motor, &tmp.renavam,
tmp.chassi, &tmp.ano, tmp.marca, tmp.proprietario,
tmp.financeira, &tmp.valor) != 11) {
/* if less than 11 returned, failed read, handle error */
fprintf (stderr, "read-error: line %zu.\n", n);
continue; /* get next line */
}
/* ==> add tmp to binary tree here <==
* (note: you will need to trim trailing whitespace from strings)
*/
printf ("line[%3zu]: %5d %10s %4s %24s ...\n",
n, tmp.lote, tmp.placa, tmp.uf, tmp.motor);
n++;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
( примечание: хранение строк в массивах фиксированной длины (например, placa, chassis
и т. Д.) Очень неэффективно, поскольку вы будете хранить большую часть пробелов (или неопределенных значений) в числе Лучше всего было бы выделить память для каждой строки на основе strlen
каждого поля (+1
для символа nul-terminatng )
Пример использования / Вывод
Выполнение вышеизложенного во входном файле с 1-полной записью на строку даст следующее:
$ ./bin/readaccts dat/accounts.txt
header line stripped
line[ 1]: 1 LJG6509 DF BA042893 ...
line[ 2]: 2 HBR7108 DF 9BD17206G83400203 ...
line[ 3]: 3 JJB0059 DF 9BD17201B53124230 ...
line[ 4]: 4 JGG0484 DF 93HGD17404Z120657 ...
line[ 5]: 5 LJQ8142 RJ LB8ABK60592 ...
line[ 6]: 6 JDQ0675 DF 9BG5TC11UFC158987 ...
line[ 7]: 7 JGQ8447 DF 9BFZF10B678103244 ...
line[ 8]: 8 JDS8539 GO BA170068 ...
line[ 9]: 9 JFT5423 DF 9BGSD19401C189366 ...
line[ 10]: 10 JJD1340 DF 9BD178237T0079169 ...
line[ 11]: 11 KDR1120 DF 9BGLK19BRRB315479 ...
line[ 12]: 12 KAW6325 GO LB4DPA78820 ...
line[ 13]: 13 JEQ3930 DF BS012252 ...
line[ 14]: 14 HYS6690 CE 9BGRD48X04G134684 ...
line[ 15]: 15 JGA2435 DF 9BGSC68Z01B167794 ...
line[ 16]: 301 JHH8778 DF 8BCLDRFJ48G521910 ...
line[ 17]: 302 JKJ5612 DF 9BWAA05W5DP091431 ...
line[ 18]: 303 JHY8370 DF 8AGCN48X0BR142232 ...
line[ 19]: 304 LNN3808 DF 93UMA48L714010797 ...
line[ 20]: 305 LOM1229 DF 9BMMF33E43A047599 ...
<snip next 50 lines>
line[ 71]: 360 JIK3300 DF 8AP372111C6010504 ...
line[ 72]: 362 JHC2944 DF 93HGD17607Z203068 ...
line[ 73]: 363 HAN9315 DF 9BD19240T53030666 ...
line[ 74]: 364 JHI3665 DF 9BD17106G72863905 ...
line[ 75]: 365 JIB7544 DF 9BWAA05Z494144655 ...
line[ 76]: 366 JKN0585 DF 9BFZF55A4E8041900 ...
line[ 77]: 367 KGZ8961 DF 9BFZF55P9A8027280 ...
line[ 78]: 368 JHZ6417 DF 9BD17164LA5464946 ...
line[ 79]: 369 JHY3246 DF 9BWCA05W38T142835 ...
line[ 80]: 370 JGR0169 DF 8AFDZZFHA4J327237 ...
line[ 81]: 371 JHX2575 DF 9BWCA05W28P074052 ...
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.