Проблемы с чтением строки с использованием fscanf () - PullRequest
6 голосов
/ 14 мая 2009

Я пытаюсь прочитать строку, используя следующий код:

while(fscanf(f, "%[^\n\r]s", cLine) != EOF )
{
    /* do something with cLine */
}

Но почему-то я каждый раз получаю только первую строчку. Это плохой способ прочитать строку? Что я должен исправить, чтобы заставить это работать как ожидалось?

Ответы [ 7 ]

19 голосов
/ 14 мая 2009

Это почти всегда плохая идея использовать функцию fscanf(), так как она может оставить указатель файла в неизвестном месте при ошибке.

Я предпочитаю использовать fgets() для ввода каждой строки, а затем sscanf(). Затем вы можете продолжить читать прочитанную строку, как считаете нужным. Что-то вроде:

#define LINESZ 1024
char buff[LINESZ];
FILE *fin = fopen ("infile.txt", "r");
if (fin != NULL) {
    while (fgets (buff, LINESZ, fin)) {
        /* Process buff here. */
    }
    fclose (fin);
}

fgets() кажется тем, что вы пытаетесь делать, читая строку, пока не встретите символ новой строки.

3 голосов
/ 15 июня 2011

Если вы хотите читать файл построчно (здесь, разделитель строк == '\ n'), просто сделайте это:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
        FILE *fp;
        char *buffer;
        int ret;

        // Open a file ("test.txt")
        if ((fp = fopen("test.txt", "r")) == NULL) {
                fprintf(stdout, "Error: Can't open file !\n");
                return -1;
        }
        // Alloc buffer size (Set your max line size)
        buffer = malloc(sizeof(char) * 4096);
        while(!feof(fp))
        {
                // Clean buffer
                memset(buffer, 0, 4096);
                // Read a line
                ret = fscanf(fp, "%4095[^\n]\n", buffer);
                if (ret != EOF) {
                        // Print line
                        fprintf(stdout, "%s\n", buffer);
                }
        }
        // Free buffer
        free(buffer);
        // Close file
        fclose(fp);
        return 0;
}

Наслаждайтесь:)

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

Использование fscanf для чтения / токенизации файла всегда приводит к хрупкому коду или боли и страданиям. Чтение строки, токенизация или сканирование этой строки безопасны и эффективны. Ему нужно больше строк кода - это означает, что требуется больше времени, чтобы ДУМАТЬ о том, что вы хотите сделать (и вам нужно обрабатывать конечный размер входного буфера), - но после этого жизнь просто воняет меньше.

Не боритесь с fscanf. Просто не используйте это. Когда-либо.

1 голос
/ 14 мая 2009

Если вы попробуете while( fscanf( f, "%27[^\n\r]", cLine ) == 1 ), возможно, вам повезет больше. Три изменения от вашего оригинала:

  • ограничение по длине, что читается - я использовал 27 здесь в качестве примера, и, к сожалению, семейство scanf() требует ширину поля буквально в строке формата и не может использовать механизм *, который printf() банка для передачи значения в
  • избавиться от s в строке формата - %[ - это спецификатор формата для "всех символов, совпадающих или не совпадающих с набором", и набор завершается ] самостоятельно
  • сравните возвращаемое значение с числом ожидаемых конверсий (и для простоты управления убедитесь, что это число равно 1)

Тем не менее, вы получите тот же результат с меньшими затратами, если использовать fgets(), чтобы прочитать столько строк, сколько уместится в вашем буфере.

0 голосов
/ 01 мая 2017

Я думаю, что проблема с этим кодом в том, что когда вы читаете с% [^ \ n \ r] s, на самом деле вы читаете, пока не достигнете '\ n' или '\ r', но вы не читаете '\ n' или '\ r' также. Поэтому вам нужно получить этот символ, прежде чем снова читать с помощью fscanf в цикле. Сделайте что-то подобное:

do{
    fscanf(f, "%[^\n\r]s", cLine) != EOF

    /* Do something here */

}while(fgetc(file) != EOF)
0 голосов
/ 14 мая 2009

Ваш цикл имеет несколько проблем. Вы написали:

while( fscanf( f, "%[^\n\r]s", cLine ) != EOF ) 
    /* do something */;

Некоторые вещи, которые следует учитывать:

  1. fscanf () возвращает количество сохраненных элементов. Он может вернуть EOF, если он читает после конца файла или если дескриптор файла имеет ошибку. Необходимо отличить действительный возврат от нуля, в этом случае в буфере cLine нет нового содержимого от успешно прочитанного.

  2. У вас есть проблема, когда происходит сбой сопоставления, потому что трудно предсказать, где указатель файла теперь указывает в потоке. Это делает восстановление после неудачного матча труднее, чем можно было бы ожидать.

  3. Шаблон, который вы написали, вероятно, не выполняет то, что вы хотели. Он соответствует любому количеству символов, которые не являются CR или LF, и затем ожидает найти литерал s.

  4. Вы не защитили свой буфер от переполнения. Любое количество символов может быть прочитано из файла и записано в буфер, независимо от размера, выделенного этому буферу. К сожалению, это распространенная ошибка, которая во многих случаях может быть использована злоумышленником для запуска произвольного кода, выбранного злоумышленниками.

  5. Если вы специально не запросили, чтобы f был открыт в двоичном режиме, в библиотеке произойдет перевод конца строки, и вы, как правило, никогда не увидите символы CR, обычно не в текстовых файлах.

Вы, вероятно, хотите цикл, похожий на следующий:

while(fgets(cLine, N_CLINE, f)) {
    /* do something */ ;
}

где N_CLINE - количество байтов, доступных в буфере, начиная с cLine.

Функция fgets() является наиболее предпочтительным способом чтения строки из файла. Его вторым параметром является размер буфера, и он считывает до 1 байта этого размера из файла в буфер. Он всегда завершает буфер символом nul, чтобы его можно было безопасно передать другим строковым функциям C.

Останавливается на первом из конца файла, чтения новой строки или buffer_size-1 байтов.

Он оставляет символ новой строки в буфере, и этот факт позволяет вам отличить одну строку длиннее вашего буфера от строки, короче буфера.

Возвращает NULL, если байты не были скопированы из-за конца файла или ошибки, и указатель на буфер в противном случае. Возможно, вы захотите использовать feof() и / или ferror() для различения этих случаев.

0 голосов
/ 14 мая 2009

Мне кажется, вы пытаетесь использовать операторы регулярных выражений в своей строке fscanf. Строка [^\n\r] ничего не значит для fscanf, поэтому ваш код не работает должным образом.

Кроме того, fscanf () не возвращает EOF, если элемент не совпадает. Скорее, он возвращает целое число, которое указывает количество совпадений, которое в вашем случае, вероятно, равно нулю. EOF возвращается только в конце потока или в случае ошибки. Итак, что происходит в вашем случае, так это то, что первый вызов fscanf () читает до конца файла в поисках подходящей строки, а затем возвращает 0, чтобы сообщить, что совпадение не найдено. Затем второй вызов возвращает EOF, поскольку весь файл был прочитан.

Наконец, обратите внимание, что оператор формата% s scanf захватывает только следующий символ пробела, поэтому вам не нужно исключать \ n или \ r в любом случае.

Обратитесь к документации fscanf для получения дополнительной информации: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/

...