Я открываю файл и помещаю его содержимое в строковый буфер, чтобы выполнить лексический анализ для каждого символа. Делая это таким образом, синтаксический анализ завершается быстрее, чем использование следующего числа 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.
Сбор комментариев в ответ …
Вы теряете память (потенциально много памяти), когда не можете прочитать.
Вы не допустили нулевого терминатора в конце прочитанной строки.
Нет смысла обнулять память, когда она собирается быть перезаписана данными из файла.
Ваш тестовый цикл обращается к памяти вне границ; 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
преобразован в
charint
,Если индикатор конца файла для потока установлен, или если поток находится в конце файла, конец конца файла
Индикатор файла для потока установлен и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 или более, но общий эффект почти такой же.
Других решений пока нет …