Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение 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 не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!
Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.
Типы ссылок (эти должны быть проверены):
Типы значений (вы можете просто игнорировать эти):
Использование fscanf для чтения / tokenise файла всегда приводит к хрупкому коду или болью и страданиям. Чтение строки, а также токенизация или сканирование этой линии безопасны и эффективны. Ему нужно больше строк кода, а это значит, что требуется больше времени, чтобы ДУМАТЬ о том, что вы хотите сделать (и вам нужно обрабатывать размер конечного входного буфера), но после этого жизнь просто воняет меньше.
t бой fscanf. Просто не используйте его. Когда-либо.
Я думаю, что проблема с этим кодом заключается в том, что когда вы читаете с помощью% [^\n\r] s, на самом деле, вы читаете до достижения '\n' или '\r', но вы не читаете '\n' или '\r'. Таким образом, вам нужно получить этого персонажа, прежде чем читать с fscanf снова в цикле. Сделайте что-нибудь подобное:
do{
fscanf(f, "%[^\n\r]s", cLine) != EOF
/* Do something here */
}while(fgetc(file) != EOF)
Почти всегда плохой идеей использовать функцию 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()
похоже на то, что вы пытаетесь сделать, читаете строку, пока не встретите символ новой строки.
Мне кажется, что вы пытаетесь использовать операторы регулярных выражений в строке fscanf. Строка [^\n\r]
не означает ничего для fscanf, поэтому ваш код работает не так, как ожидалось.
Кроме того, fscanf () не возвращает EOF, если элемент не совпадает. Скорее, он возвращает целое число, которое указывает количество совпадений - что в вашем случае, вероятно, равно нулю. EOF возвращается только в конце потока или в случае ошибки. Итак, что происходит в вашем случае, так это то, что первый вызов fscanf () читает весь путь до конца файла, ища подходящую строку, а затем возвращает 0, чтобы вы знали, что совпадения не найдено. Второй вызов затем возвращает EOF, потому что весь файл был прочитан.
Наконец, обратите внимание, что оператор формата% s scanf захватывает только следующий пробельный символ, поэтому вам не нужно исключать\n или \r в любом случае.
Для получения дополнительной информации обратитесь к документации fscanf: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/
Если вы попробуете while( fscanf( f, "%27[^\n\r]", cLine ) == 1 )
, у вас может быть немного больше удачи. Три изменения из вашего оригинала:
27
здесь, и, к сожалению, семейство scanf()
требует ширины поля буквально в в строке формата и не может использовать механизм *
, который printf()
может передать значение в s
в строке формата - %[
- спецификатор формата для «всех символов, совпадающих или не соответствующих набору», и набор заканчивается ]
сам по себе При этом вы получите тот же результат с меньшей болью, используя fgets()
, чтобы читать столько строк, сколько поместится в вашем буфер.
В вашей петле есть несколько проблем. Вы писали:
while( fscanf( f, "%[^\n\r]s", cLine ) != EOF )
/* do something */;
Некоторые вещи для рассмотрения:
cLine
из успешно прочитанного. s
. f
в двоичном режиме, перевод строки заканчивается будет происходить в библиотеке, и вы, как правило, не увидите CR-символов и обычно не в текстовых файлах. Вероятно, вы хотите, чтобы в цикле больше было следующее:
while(fgets(cLine, N_CLINE, f)) {
/* do something */ ;
}
, где N_CLINE - количество байтов, доступных в буфере, начиная с cLine
.
Функция fgets()
является предпочтительным способом чтения строки из файла. Его вторым параметром является размер буфера, и он считывает до 1 байта размером меньше этого размера из файла в буфер. Он всегда завершает работу буфера с символом nul
, чтобы его можно было безопасно передать другим строковым функциям C.
Он останавливается в первом конце файла, новой строки или buffer_size-1
байта.
Он оставляет символ новой строки в буфере, и этот факт позволяет отличить одну строку дольше, чем ваш буфер, от строки, меньшей, чем буфер.
Он возвращает NULL, если нет байты были скопированы из-за конца файла или ошибки, а указатель на буфер в противном случае. Вы можете использовать feof()
и / или ferror()
, чтобы отличить эти случаи.
Если вы хотите прочитать файл по строкам (здесь разделитель строк == '\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:)