Как заставить fscanf остановиться, если он попадает в новую строку? [Дубликат]

Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение NullReferenceException.

Пример:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

Вы можно просто избежать этого, проверив, является ли переменная не нулевой:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

Чтобы полностью понять, почему выбрано исключение NullReferenceException, важно знать разницу между типами значений и ссылочные типы .

Итак, если вы имеете дело со типами значений, NullReferenceExceptions не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!

Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.

Типы ссылок (эти должны быть проверены):

  • динамический
  • объект
  • string

Типы значений (вы можете просто игнорировать эти):

  • Числовые типы
  • Интегральные типы
  • Типы с плавающей запятой
  • decimal
  • bool
  • Пользовательские структуры

6
задан unwind 14 May 2009 в 08:36
поделиться

7 ответов

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

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

1
ответ дан ashleigh 1 September 2018 в 10:39
поделиться

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

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

    /* Do something here */

}while(fgetc(file) != EOF)
0
ответ дан Eduardo Dalapicola 1 September 2018 в 10:39
поделиться

Почти всегда плохой идеей использовать функцию 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() похоже на то, что вы пытаетесь сделать, читаете строку, пока не встретите символ новой строки.

19
ответ дан Jason Coco 1 September 2018 в 10:39
поделиться
  • 1
    Как я могу использовать функцию sscanf для чтения только строки (BTY - 1024 размера строки?) Спасибо! – user 14 May 2009 в 08:02
  • 2
    fgets читает одну строку "или меньше". fgets (buffer, 1024, file) будет читать строку, как есть в файле, или 1024 символа. Если вы читаете целую строку, тогда буфера [strlen (buffer)] == '\n'. Если вы достигнете EOF, он вернет null, и в противном случае на линии появится больше текста. – Tordek 14 May 2009 в 08:11
  • 3
    [Д0] stackoverflow.com/questions/865335/… – Rob Kam 15 May 2009 в 07:04

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

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

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

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

0
ответ дан JSBձոգչ 1 September 2018 в 10:39
поделиться
  • 1
    [^ a-z] действительно исключает a-z в scanf. Хотя строка, указанная выше, ищет «пару символов», первая из которых не является разрывом строки, а вторая является «s», – Tordek 14 May 2009 в 08:16
  • 2
    Документация fscanf на cplusplus.com является неполной. Google fscanf scanset. – Dingo 15 May 2009 в 02:46

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

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

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

1
ответ дан mlp 1 September 2018 в 10:39
поделиться
  • 1
    Это все равно оставит его с его первоначальной проблемой только чтения первой строки. Лучше было бы «% 27 [^\n\r]% * [\n\r]". поэтому потребляется несогласованный символ. – Dingo 15 May 2009 в 03:08

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

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
ответ дан RBerteig 1 September 2018 в 10:39
поделиться
  • 1
    спасибо, я сделал это, но мне интересно, что, если моя строка больше, чем размер, который я установил, он отрежет часть следующей строки или может вызвать другие проблемы – user 14 May 2009 в 10:24
  • 2
    Если строка ввода длиннее, чем буфер, переданный fgets (), он прекратит чтение до конца строки ввода и даст вам то, что он читал до сих пор в буфере. Вы знаете, это произошло потому, что в конце буфера нет\n. Каждый вызов функции fgets () будет продолжать чтение, поэтому вы можете обрабатывать длинную строку буфера за раз, зацикливая, пока буфер не закончится с\n. Единственная проблема заключается в разумном анализе вашего ввода, когда он сломан в произвольном месте. – RBerteig 15 May 2009 в 01:25

Если вы хотите прочитать файл по строкам (здесь разделитель строк == '\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;
}

Enjoy:)

3
ответ дан user 1 September 2018 в 10:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: