Как читать файл построчно в C? - PullRequest
1 голос
/ 03 декабря 2009

У меня есть текстовый файл, содержащий до 100 IP-адресов, по 1 на строку. Мне нужно прочитать каждый адрес, как строку, в массив под названием «список». Во-первых, я предполагаю, что «список» должен быть двумерным массивом символов. Каждый IP-адрес имеет длину 11 символов, 12, если вы включаете «\ 0», поэтому я объявил список следующим образом:

char list[100][12];

Далее я пытаюсь использовать fgets для чтения потока:

  for (i = 0; i < 100; i++)  
  {  
      if (feof(stream))  
          break;  
          for (j = 0; j < 12; j++)  
          fgets(&list[i][j], 12, stream);  
      count++;  
  }

Чтобы проверить, правильно ли прочитаны строки, я пытаюсь вывести их:

  for (i = 0; i < 5; i++)  
  {  
      for (j = 0; j < 11; j++)  
          printf("%c", list[i][j]);  
      printf("\n");  
  }

После запуска программы становится ясно, что что-то не так. Будучи новичком, я не уверен, что, но я предполагаю, что я читаю файл неправильно. Там нет ошибок. Компилируется, но печатает странный адрес в две строки.

Edit:

Я заменил код fgets следующим:

for (i = 0; i < 100; i++)
  {
      if (feof(stream))
          break;
      fgets(list[i], 12, stream);
      count++;
  }

Теперь он печатает пять строк, но это «случайные» символы из памяти.

Ответы [ 7 ]

6 голосов
/ 03 декабря 2009

Сначала читаем:

      for (j = 0; j < 12; j++)  
      fgets(&list[i][j], 12, stream);  

У вас тут большая проблема. Это попытка прочитать строку в каждый последующий символ в вашем массиве.

В общем, я думаю, что вы делаете это намного сложнее, чем нужно. Думайте о вашем массиве как о 100 строках, и fgets будет работать со строкой одновременно. Это означает, что чтение может выглядеть примерно так:

for (i=0; i<100 && fgets(list[i], 11, string); i++)
    ;

Есть еще одна второстепенная деталь: fgets() обычно сохраняет новую строку в конце каждой строки. Таким образом, вам может потребоваться оставить место для 13 символов (11 для адреса, 1 для новой строки, 1 для терминатора NUL), или же вы можете захотеть прочитать данные во временный буфер и скопировать их в ваш list только после того, как вы сняли новую строку.

В вашем текущем коде для печати строк вы работаете по одному символу за раз, что может работать, но излишне сложно. Несколько человек предложили использовать% s printf, что само по себе хорошо. Однако для этого вам необходимо немного упростить индексирование. Печать первых шести адресов будет выглядеть примерно так:

for (i=0; i<6; i++)
    printf("%s", list[i]);
4 голосов
/ 03 декабря 2009

Ваш вызов fgets считывает до 11 символов из потока в массив. Таким образом, вы не хотите вызывать это один раз для каждого символа каждой строки.

Только подумайте об этих циклах: при i = 0 и j = 0 он читает до 11 символов до &list[0][0]. Затем при i = 0 и j = 1 он читает еще 11 символов в &list[0][1]. Это неверно по двум причинам: он перезаписывает результат последнего вызова и потенциально записывает больше байтов, чем может удержать list [0].

1 голос
/ 03 декабря 2009

Не используйте feof() в качестве условия цикла; он не вернет true, пока вы не попытаетесь прочитать за концом файла, то есть ваш цикл будет выполняться слишком много раз. Проверьте результат вашего входного вызова (независимо от того, используете ли вы fgets() или fscanf()), чтобы убедиться, что он успешен, , а затем проверьте feof(), если у вас возникла ошибка.

if (fgets(buffer, sizeof buffer, stream) != NULL)
{
  // process the input buffer
}
else if (feof(stream)
{
  // handle end of file
}
else
{
  // handle read error other than EOF
}

fgets() читает целые строки, а не отдельные символы, поэтому вы не хотите передавать адрес каждого отдельного символа в вашей строке. Назовите это так вместо этого:

if (fgets(list[i], sizeof list[i], stream) != NULL)
{
  // process input address
}

А теперь, для обычной болтовни Боде о массивах и указателях ...

Когда выражение массива появляется в большинстве контекстов, тип выражения неявно преобразуется из «массива N-элемента T» в «указатель на T», а значением выражения является адрес первого элемента массив. Исключениями из этого правила являются случаи, когда выражение массива является операндом операторов sizeof или & или это строковый литерал, который используется в качестве инициализатора в объявлении. Когда вы слышите, как люди говорят «массивы и указатели - это одно и то же», они используют это правило. Массивы и указатели - совершенно разные животные, но в некоторых контекстах они могут использоваться взаимозаменяемо.

Обратите внимание, что в приведенном выше коде я передал list[i] в качестве первого аргумента функции fgets () без каких-либо украшений (например, оператора &). Хотя тип list[i] является «массивом из 12 элементов char», в этом контексте он неявно преобразуется в тип «pointer to char», а значением будет адрес list[i][0]. Обратите внимание, что я также передал это же выражение оператору sizeof. В этом случае тип выражения массива - , а не , преобразованный в тип указателя, и оператор sizeof возвращает количество байтов в типе массива (12).

Просто прибить это:

Expression      Type             Implicitly converted to
----------      ----             ----
list            char [100][12]   char (*)[12] (pointer to 12-element array of char)
list[i]         char [12]        char *
list[i][j]      char             N/A

Все это означает, что fgets() будет считывать до следующих 12 символов (при условии, что он не попадет на новую строку или EOF в первую очередь) и будет сохранять его, начиная с list[i][0]. Обратите внимание, что fgets() запишет завершающий нулевой символ (0) в конец вашей строки. Также обратите внимание, что если fgets() встречает символ новой строки и , в целевом массиве есть место для него и завершающего nul, fgets() будет хранить завершающий символ новой строки перед нулевым символом. Так что, если ваш входной файл имеет строку типа

1.1.1.1\n

тогда содержимое вашего входного буфера после чтения будет "1.1.1.1\n\0xxx", где x - какое-то случайное значение. Если вы не хотите, чтобы новая строка находилась там, вы можете использовать функцию strchr(), чтобы найти ее, а затем заменить ее на 0:

char *newline;
...
if ((newline = strchr(input[i], '\n')) != NULL)
{
  *newline = 0;
}

Поскольку fgets() останавливается на следующей новой строке, и поскольку ваш входной буфер имеет размер 12 символов, вы можете столкнуться с ситуацией, когда у вас есть новая строка в качестве следующего входного символа в файле; в этом случае fgets() будет записывать только эту новую строку во входной буфер, поэтому у вас будет несколько пустых записей, что, вероятно, не то, что вам нужно. Возможно, вы захотите добавить дополнительный байт во входной буфер, чтобы избежать этой ситуации.

Собираем все вместе:

char list[100][13];
...
for (i = 0; i < 100; ++)
{
  if (fgets(list[i], sizeof list[i], stream) != NULL)
  {
    char *newline = strchr(list[i], '\n');
    if (newline != NULL)
      *newline = 0;
    printf("Read address \"%s\"\n", list[i]);
    count++;
  }
  else if (feof(stream))
  {
    printf("Reached end of file\n");
    break;
  }
  else
  {
    printf("Read error on input; aborting read loop\n");
    break;
  }
}
1 голос
/ 03 декабря 2009

Я написал функцию для чтения строк. Я думаю, что это должно быть безопасно.

Проверка: io_readline

https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c

1 голос
/ 03 декабря 2009

для (i = 0; i <100; i ++) {</p>

   if (feof(fp))
       break;

   fscanf(fp,"%s\n",list[i]);

}

1 голос
/ 03 декабря 2009

Второй цикл не нужен, и он портит вашу память. Вы должны сделать что-то вроде этого,

for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
fgets(&list[i][j], 12, stream);
count++;
}

To check to see if the strings were read properly, I attempt to output them:

for (i = 0; i < 5; i++)
{
printf("%s\n", list[i]);
}
1 голос
/ 03 декабря 2009

Символ новой строки заставляет fgets перестать читать, но он считается допустимым символом и поэтому включен в строку, скопированную в str.

Возможно, вы читаете первые 12 символов в первом вызове fgets, затем второй вызов перехватывает новую строку, затем третий вызов получает следующую строку.

Попробуйте использовать fgets с ограничением в 15 символов и расширить буфер.

...