Чтение файла в строковый буфер и обнаружение EOF

Я открываю файл и помещаю его содержимое в строковый буфер, чтобы выполнить лексический анализ для каждого символа. Делая это таким образом, синтаксический анализ завершается быстрее, чем использование следующего числа Fread () вызовов, и поскольку исходный файл всегда будет не больше пары МБ, я могу быть уверен, что все содержимое файла всегда будет прочитано.

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


Вот как я открываю файл и считываю его в строковый буфер:

FILE *fp = NULL;
errno_t err = _wfopen_s(&fp, m_sourceFile, L"rb, ccs=UNICODE");
if(fp == NULL || err != 0) return FALSE;
if(fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
fp = NULL;
return FALSE;
}

LONG fileSize = ftell(fp);
if(fileSize == -1L) {
fclose(fp);
fp = NULL;
return FALSE;
}
rewind(fp);

LPSTR s = new char[fileSize];
RtlZeroMemory(s, sizeof(char) * fileSize);
DWORD dwBytesRead = 0;
if(fread(s, sizeof(char), fileSize, fp) != fileSize) {
fclose(fp);
fp = NULL;
return FALSE;
}

Кажется, это всегда работает отлично. Далее следует простой цикл, который проверяет содержимое буфера строки по одному символу за раз, например, так:

char c = 0;
LONG nPos = 0;
while(c != EOF && nPos <= fileSize)
{
c = s[nPos];
// do something with 'c' here...
nPos++;
}

Конечные байты файла обычно представляют собой серию ý (-3) а также « (-85) символы, и, следовательно, EOF никогда не обнаруживается. Вместо этого цикл просто продолжается до НСС в конечном итоге имеет большую ценность, чем размер файла — Что нежелательно для правильного лексического анализа, потому что вы часто заканчиваете тем, что пропускаете последний токен в потоке, который пропускает символ новой строки в конце.

Можно ли предположить, что в наборе символов Basic Latin предполагается, что EOF char — это любой символ с отрицательным значением? Или, может быть, есть лучший способ сделать это?


#РЕДАКТИРОВАТЬ: Я только что попытался реализовать feof () функция в моем цикле, и все же, он, похоже, также не обнаруживает EOF.

0

Решение

Сбор комментариев в ответ …

  • Вы теряете память (потенциально много памяти), когда не можете прочитать.

  • Вы не допустили нулевого терминатора в конце прочитанной строки.

  • Нет смысла обнулять память, когда она собирается быть перезаписана данными из файла.

  • Ваш тестовый цикл обращается к памяти вне границ; nPos == fileSize один за пределами памяти, которую вы выделили.

    char c = 0;
    LONG nPos = 0;
    while(c != EOF && nPos <= fileSize)
    {
    c = s[nPos];
    // do something with 'c' here...
    nPos++;
    }
    
  • Есть другие проблемы, не упомянутые ранее, с этим. Вы спросили, «безопасно ли предположить, что символ EOF — это любой символ с отрицательным значением», на что я ответил нет. Здесь есть несколько проблем, которые затрагивают как C, так и C ++ код. Во-первых, это равнина char может быть подписанным типом или неподписанным типом. Если тип без знака, то вы никогда не сможете сохранить в нем отрицательное значение (или, точнее, если вы попытаетесь сохранить отрицательное целое число в беззнаковом знаке, оно будет усечено до наименее значимого 8).* биты и будут рассматриваться как положительные.

  • В приведенном выше цикле может возникнуть одна из двух проблем. Если char является типом со знаком, то есть символ (ÿ, y-umlaut, U + 00FF, LATIN SMALL LETTER Y WITH DIAERESIS, 0xFF в наборе кода Latin-1), который имеет то же значение, что и EOF (который всегда отрицателен и обычно -1). Таким образом, вы можете обнаружить EOF преждевременно. Если char это тип без знака, тогда никогда не будет символа, равного EOF. Но проверка EOF на символьной строке в корне неверна; EOF — это индикатор состояния операций ввода-вывода, а не символ.

  • Во время операций ввода / вывода вы обнаружите EOF только тогда, когда попытаетесь прочитать данные, которых там нет. fread() не будет сообщать EOF; Вы попросили прочитать, что было в файле. Если вы пытались getc(fp) после fread(), вы получите EOF, если файл не вырос, так как вы измерили, как долго это будет. поскольку _wfopen_s() это нестандартная функция, это может повлиять на то, как ftell() ведет себя и значение, которое он сообщает. (Но вы позже установили, что это не так.)

  • Обратите внимание, что такие функции, как fgetc() или же getchar() определены для возврата символов в виде положительных целых чисел и EOF в качестве отдельного отрицательного значения.

    Если указатель конца файла для входного потока указывает на stream не установлен и
    следующий персонаж присутствует, fgetc функция получает этот символ как unsigned
    char
    преобразован в int,

    Если индикатор конца файла для потока установлен, или если поток находится в конце файла, конец конца файла
    Индикатор файла для потока установлен и fgetc функция возвращает EOF. В противном случае
    fgetc функция возвращает следующий символ из входного потока, на который указывает stream,
    Если происходит ошибка чтения, устанавливается индикатор ошибки для потока и fgetc функция
    возвращает EOF.289)

    289) Конец файла и ошибка чтения могут быть различены с помощью feof а также ferror функции.

    Это указывает на то, что EOF отделен от любого допустимого символа в контексте операций ввода-вывода.

Вы комментируете:

Что касается любой потенциальной утечки памяти … На данном этапе в моем проекте утечки памяти являются одной из многих проблем с моим кодом, которые на данный момент меня не беспокоят. Даже если он не пропустил память, с самого начала он даже не работает, так какой в ​​этом смысл? Функциональность на первом месте.

На начальном этапе кодирования легче предотвратить утечки памяти в путях ошибок, чем вернуться назад и исправить их — потому что вы можете их не обнаружить, потому что вы не можете вызвать состояние ошибки. Однако степень, в которой это имеет значение, зависит от целевой аудитории программы. Если это одноразовый курс по кодированию, у вас все будет хорошо. Если вы единственный человек, который будет использовать его, вы можете быть в порядке. Но если он будет установлен миллионами, у вас будут проблемы с модернизацией чеков повсюду.

Я поменял местами _wfopen_s () с fopen (), и результат от ftell () такой же. Однако после изменения соответствующих строк на LPSTR s = new char [fileSize + 1], RtlZeroMemory (s, sizeof (char) * fileSize + 1); (что также должно завершать его с нулевым символом, кстати), и добавление if (nPos == fileSize) в начало цикла, теперь оно выходит чисто.

ХОРОШО. Вы могли бы использовать только s[fileSize] = '\0'; обнулить данные тоже, но используя RtlZeroMemory() достигает того же эффекта (но будет медленнее, если размер файла будет много мегабайт). Но я рад, что различные комментарии и предложения помогли вам вернуться на правильный путь.


* Теоретически, CHAR_BITS может быть больше 8; на практике это почти всегда 8, и для простоты я предполагаю, что здесь 8 бит. Обсуждение должно быть более нюансированным, если CHAR_BITS равно 9 или более, но общий эффект почти такой же.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]