Отмена эффектов ungetc(): "Как" это делают fseek(),rewind() и fsetpos()? Заполняется ли буфер каждый раз?
Ха! Как мне поставить все это в ясный вопрос!! Позвольте мне попробовать:
Я знаю, что файлы открываются с помощью fopen()
мы буферизуем в память. Мы используем буфер для эффективности и простоты. Во время чтения из файла содержимое файла сначала читается в буфер, и мы читаем из этого буфера. Аналогично, при записи в файл, сначала содержимое записывается в буфер, а затем в файл.
Но что с fseek()
,fsetpos()
а также rewind()
отбрасывая эффект предыдущих звонков ungetc()
? Можете ли вы сказать мне, как это делается? Я имею в виду, если мы открыли файл для чтения и он скопирован в буфер. Теперь с помощью ungetc()
мы изменили некоторые символы в буфере. Вот то, что я просто не могу понять даже после долгих усилий:
Вот что сказано о
ungetc()
-"При вызове fseek, fsetpos или rewind on stream все символы, ранее вставленные в него с помощью этой функции, будут отброшены". - Как можно отбросить символы, уже помещенные в буфер? Один из подходов состоит в том, что удаленные исходные символы "запоминаются", и каждый новый введенный символ идентифицируется и заменяется исходным символом. Но это кажется очень неэффективным. Другой вариант - загрузить копию исходного файла в буфер и поместить указатель файла в предполагаемую позицию. Какой из этих подходов выполняет команду fseek, fsetpos или rewind take, чтобы отбросить символы, введенные с использованиемungetc()
?Для текстовых потоков, как определяется наличие непрочитанных символов в потоке, символов, которые были вставлены с использованием
ungetc()
, повлиять на возвращаемое значениеftell()
"Моя путаница возникает из следующей строки оftell()
а такжеungetc()
по этой ссылке оftell
( ИСТОЧНИК)
"Для текстовых потоков числовое значение может быть не значимым, но все же может использоваться для восстановления позиции на той же позиции позже с использованием fseek (если есть символы, возвращенные с использованием ungetc, все еще ожидающие чтения, поведение не определено)".
- Сосредоточив внимание на последней строке вышеупомянутого абзаца, что имеет
pending of being read
что делать с "ungetc()- полученным" персонажем, от которого отказываются? Каждый раз, когда мы читаем символ, который был помещен в поток, используяungetc()
это сбрасывается после прочтения?
2 ответа
Хорошая ментальная модель отложенного персонажа заключается в том, что это просто лишнее свойство, которое свисает с FILE *
объект. Представь, что у тебя есть:
typedef struct {
/* ... */
int putback_char;
/* ... */
} FILE;
Представить putback_char
инициализируется значением EOF
что указывает на то, что "нет возврата", и ungetc
просто сохраняет персонажа этому участнику.
Представьте, что каждая операция чтения проходит getc
и что getc
делает что-то вроде этого:
int getc(FILE *stream)
{
int ret = stream->putback_char;
if (ret != EOF) {
stream->putback_char = EOF;
if (__is_binary(stream))
stream->current_position--;
return ret;
}
return __internal_getc(stream); /* __internal_getc doesn't know about putback_char */
}
Функции, которые очищают отжим, просто назначают EOF
в putback_char
,
Другими словами, символ возврата (и только один должен поддерживаться) может фактически быть миниатюрным буфером, который отделен от обычной буферизации. (Учтите, что даже небуферизованный поток поддерживает ungetc
: такой поток должен где-то поместить байт или символ.)
Что касается индикатора положения, то стандарт C99 гласит:
Для текстового потока значение его индикатора положения файла после успешного вызова
ungetc
функция не указана, пока все символы сдвига не будут прочитаны или отброшены. Для двоичного потока его индикатор положения файла уменьшается при каждом успешном обращении кungetc
функция; если его значение было равно нулю до вызова, оно не определено после вызова. [7.19.7.11ungetc
функция]
Таким образом, ссылка на www.cplusplus.com, которую вы используете, неверна; поведение ftell
не является неопределенным, если есть ожидающие символы, сдвинутые назад ungetc
, Для текстовых потоков значение не указано. Доступ к неопределенному значению не является неопределенным поведением, поскольку неопределенное значение не может быть представлением прерывания. Неопределенное поведение существует для двоичных потоков, если откат происходит в нулевой позиции, потому что тогда позиция становится неопределенной. Неопределенный означает, что это неопределенное значение, которое может быть представлением ловушки. Доступ к нему может привести к остановке программы с сообщением об ошибке или вызвать другое поведение.
Лучше получать спецификации языка программирования и библиотеки из уст в уста, а не от случайных сайтов.
Давайте начнем с самого начала,
int ungetc(int c, FILE *stream);
Функция ungetc() должна сдвинуть байт, указанный с помощью c (преобразованный в беззнаковый символ), обратно во входной поток, на который указывает stream.Символ виртуально возвращается во входной поток, уменьшая его внутреннюю позицию в файле, как если бы предыдущий getc операция была отменена. Это влияет только на дальнейшие операции ввода в этом потоке, а не на содержимое физического файла, связанного с ним, который не изменяется никакими вызовами этой функции.
int fseek(FILE *stream, long offset, int whence);
Новая позиция, измеряемая в байтах от начала файла, должна быть получена путем добавления смещения к позиции, указанной в Fromce. Указанная точка является началом файла для SEEK_SET, текущим значением индикатора положения файла для SEEK_CUR или концом файла для SEEK_END.fseek, либо сбрасывает любой буферизованный вывод перед установкой позиции файла, либо запоминает его, чтобы будет написано позже в нужном месте в файле
int fsetpos(FILE *stream, const fpos_t *pos);
Функция fsetpos() устанавливает индикаторы положения и состояния файла для потока, на который указывает поток, в соответствии со значением объекта, на который указывает pos, которое должно быть значением, полученным из более раннего вызова fgetpos() в том же потоке.
void rewind(FILE *stream);
Функция перемотки перемещает указатель файла, связанный с потоком, в начало файла. Призыв перемотки похож на
(void) fseek( stream, 0L, SEEK_SET);
Итак, как вы видите ungetc(), нажатие на символы назад не изменяет файл; затрагивается только внутренняя буферизация для потока. Ваш второй комментарий "Другой вариант - загрузить копию исходного файла в буфер и поместить указатель файла в предполагаемую позицию" - это правильно.
Теперь отвечая на ваш второй вопрос - Успешный промежуточный вызов (с потоком, на который указывает поток) функции позиционирования файла отбрасывает любые символы возврата для потока. Внешнее хранилище, соответствующее потоку, не изменяется