Перейти к содержимому

Как определить конец файла c

  • автор:

Тестирование на наличие конца файла

Функция ReadFile проверяет состояние конца файла (EOF) по-разному для синхронных и асинхронных операций чтения. Когда синхронная операция чтения достигает конца файла, ReadFile возвращает значение TRUE и задает переменную, на которую указывает параметр lpNumberOfBytesRead , равным нулю. Асинхронная операция чтения может столкнуться с окончанием файла во время инициации вызова ReadFile или во время последующих асинхронных операций, если указатель на файл программно расширен за пределами конца файла.

В следующем примере C++ показано, как проверить конец файла во время синхронной операции чтения.

 // Attempt a synchronous read operation. bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL); // Check for eof. if (bResult && nBytesRead == 0) < // at the end of the file >

Проверка завершения файла во время асинхронной операции чтения выполняется несколько чаще, чем для аналогичной синхронной операции чтения. Индикатор конца файла для асинхронных операций чтения — это когда GetOverlappedResult возвращает значение FALSE , а GetLastError возвращает ERROR_HANDLE_EOF.

В следующем примере C++ показано, как проверить конец файла во время асинхронной операции чтения.

#include #include #include #define BUF_SIZE (61) LPCTSTR ErrorMessage( DWORD error ) // Routine Description: // Retrieve the system error message for the last-error code < LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); return((LPCTSTR)lpMsgBuf); >void GoDoSomethingElse(void) // Routine Description: // Placeholder to demo when async I/O might want to do // other processing. < printf("Inside GoDoSomethingElse()\n"); >DWORD AsyncTestForEnd( HANDLE hEvent, HANDLE hFile ) // Routine Description: // Demonstrate async ReadFile operations that can catch // End-of-file conditions. Unless the operation completes // synchronously or the file size happens to be an exact // multiple of BUF_SIZE, this routine will eventually force // an EOF condition on any file. // Parameters: // hEvent - pre-made manual-reset event. // // hFile - pre-opened file handle, overlapped. // // inBuffer - the buffer to read in the data to. // // nBytesToRead - how much to read (usually the buffer size). // Return Value: // Number of bytes read. < char inBuffer[BUF_SIZE]; DWORD nBytesToRead = BUF_SIZE; DWORD dwBytesRead = 0; DWORD dwFileSize = GetFileSize(hFile, NULL); OVERLAPPED stOverlapped = ; DWORD dwError = 0; LPCTSTR errMsg = NULL; BOOL bResult = FALSE; BOOL bContinue = TRUE; // Set up overlapped structure event. Other members are already // initialized to zero. stOverlapped.hEvent = hEvent; // This is an intentionally brute-force loop to force the EOF trigger. // A properly designed loop for this simple file read would use the // GetFileSize API to regulate execution. However, the purpose here // is to demonstrate how to trigger the EOF error and handle it. while(bContinue) < // Default to ending the loop. bContinue = FALSE; // Attempt an asynchronous read operation. bResult = ReadFile(hFile, inBuffer, nBytesToRead, &dwBytesRead, &stOverlapped); dwError = GetLastError(); // Check for a problem or pending operation. if (!bResult) < switch (dwError) < case ERROR_HANDLE_EOF: < printf("\nReadFile returned FALSE and EOF condition, async EOF not triggered.\n"); break; >case ERROR_IO_PENDING: < BOOL bPending=TRUE; // Loop until the I/O is complete, that is: the overlapped // event is signaled. while( bPending ) < bPending = FALSE; // Pending asynchronous I/O, do something else // and re-check overlapped structure. printf("\nReadFile operation is pending\n"); // Do something else then come back to check. GoDoSomethingElse(); // Check the result of the asynchronous read // without waiting (forth parameter FALSE). bResult = GetOverlappedResult(hFile, &stOverlapped, &dwBytesRead, FALSE) ; if (!bResult) < switch (dwError = GetLastError()) < case ERROR_HANDLE_EOF: < // Handle an end of file printf("GetOverlappedResult found EOF\n"); break; >case ERROR_IO_INCOMPLETE: < // Operation is still pending, allow while loop // to loop again after printing a little progress. printf("GetOverlappedResult I/O Incomplete\n"); bPending = TRUE; bContinue = TRUE; break; >default: < // Decode any other errors codes. errMsg = ErrorMessage(dwError); _tprintf(TEXT("GetOverlappedResult failed (%d): %s\n"), dwError, errMsg); LocalFree((LPVOID)errMsg); >> > else < printf("ReadFile operation completed\n"); // Manual-reset event should be reset since it is now signaled. ResetEvent(stOverlapped.hEvent); >> break; > default: < // Decode any other errors codes. errMsg = ErrorMessage(dwError); printf("ReadFile GLE unhandled (%d): %s\n", dwError, errMsg); LocalFree((LPVOID)errMsg); break; >> > else < // EOF demo did not trigger for the given file. // Note that system caching may cause this condition on most files // after the first read. CreateFile can be called using the // FILE_FLAG_NOBUFFERING parameter but it would require reads are // always aligned to the volume's sector boundary. This is beyond // the scope of this example. See comments in the main() function. printf("ReadFile completed synchronously\n"); >// The following operation assumes the file is not extremely large, otherwise // logic would need to be included to adequately account for very large // files and manipulate the OffsetHigh member of the OVERLAPPED structure. stOverlapped.Offset += dwBytesRead; if ( stOverlapped.Offset < dwFileSize ) bContinue = TRUE; >return stOverlapped.Offset; > int __cdecl _tmain(int argc, TCHAR *argv[]) // To force an EOF condition, execute this application specifying a // zero-length file. This is because the offset (file pointer) must be // at or beyond the end-of-file marker when ReadFile is called. For // more information, see the comments for the AsyncTestForEnd routine. < HANDLE hEvent; HANDLE hFile; DWORD dwReturnValue; printf("\n"); if( argc != 2 ) < printf("ERROR:\tIncorrect number of arguments\n\n"); printf("%s \n", argv[0]); return; > hFile = CreateFile(argv[1], // file to open GENERIC_READ, // open for reading FILE_SHARE_READ, // share for reading NULL, // default security OPEN_EXISTING, // existing file only FILE_FLAG_OVERLAPPED, // overlapped operation NULL); // no attr. template if (hFile == INVALID_HANDLE_VALUE) < DWORD dwError = GetLastError(); LPCTSTR errMsg = ErrorMessage(dwError); printf("Could not open file (%d): %s\n", dwError, errMsg); LocalFree((LPVOID)errMsg); return; >hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEvent == NULL) < DWORD dwError = GetLastError(); LPCTSTR errMsg = ErrorMessage(dwError); printf("Could not CreateEvent: %d %s\n", dwError, errMsg); LocalFree((LPVOID)errMsg); return; >dwReturnValue = AsyncTestForEnd(hEvent, hFile); printf( "\nRead complete. Bytes read: %d\n", dwReturnValue); CloseHandle(hFile); CloseHandle(hEvent); > 

Выходные данные этого примера кода выводятся следующим образом.

ReadFile operation is pending Inside GoDoSomethingElse() GetOverlappedResult I/O Incomplete ReadFile operation is pending Inside GoDoSomethingElse() ReadFile operation completed Complete. Bytes read: 541 

feof

Функция feof возвращает ненулевое значение, если операция чтения пытается продолжить чтение файла после того, как он закончился; в противном случае возвращается значение 0. Если указатель потока указан NULL , функция вызывает обработчик недопустимых параметров, как описано в разделе «Проверка параметров». Если выполнение может быть продолжено, параметр errno принимает значение EINVAL , а функция feof возвращает значение 0.

Дополнительные сведения о кодах возврата см. в разделе errno , _doserrno _sys_errlist и _sys_nerr .

Замечания

Подпрограмма feof (реализованная и как функция, и как макрос) определяет, пройден ли конец stream . Если конец файла пройден, операции чтения возвращают индикатор окончания файла, пока поток не будет закрыт или пока для него не будет вызвана функция rewind , fsetpos , fseek или clearerr .

Например, если файл содержит 10 байтов, и вы читаете 10 байт из файла, feof возвращается значение 0, так как, несмотря на то, что указатель файла находится в конце файла, вы не пытались прочитать за пределами конца. Функция feof вернет ненулевое значение только после того, как вы попытаетесь прочитать 11-й байт.

По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это поведение, см . статью «Глобальное состояние» в CRT.

Требования

Функция Обязательный заголовок
feof

Дополнительные сведения о совместимости см. в разделе Совместимость.

Пример

// crt_feof.c // This program uses feof to indicate when // it reaches the end of the file CRT_FEOF.TXT. It also // checks for errors with ferror. // #include #include int main( void ) < int count, total = 0; char buffer[100]; FILE *stream; fopen_s( &stream, "crt_feof.txt", "r" ); if( stream == NULL ) exit( 1 ); // Cycle until end of file reached: while( !feof( stream ) ) < // Attempt to read in 100 bytes: count = fread( buffer, sizeof( char ), 100, stream ); if( ferror( stream ) ) < perror( "Read error" ); break; >// Total up actual bytes read total += count; > printf( "Number of bytes read = %d\n", total ); fclose( stream ); > 

Входные данные: crt_feof.txt

Line one. Line two. 

Выходные данные

Number of bytes read = 19 

Правильное использование проверки конца файла

в C++ получаются неприятности — лишняя считанная строка, например. Почему? Как правильно проверить, что достигнут конец файла?

Отслеживать
задан 27 мая 2018 в 4:12
219k 15 15 золотых знаков 119 119 серебряных знаков 230 230 бронзовых знаков
связанный вопрос Читать строки из файла, пока они есть
27 мая 2018 в 6:04

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Признак достижения конца файла выставляется только после неудачной попытки чтения за его концом. Поэтому, если в теле цикла нет проверки, успешно ли выполнено чтение из файла — последнее чтение окажется именно тем неудачным чтением, которое выставит признак достигнутого конца файла (а для вас это будет выглядеть как, например, еще раз считанная последняя строка, если она находилась в буфере для чтения).

Лучше в заголовке цикла while выполнять само чтение с проверкой — например, в программе на C это могло бы выглядеть как

while(fread(&data,sizeof(data),1,file)==1) < // Обработка считанных данных >/* Или, скажем, while(fgets(buf, bufsize, file)) < . >*/ if (feof(file)) // Достигнут конец файла puts("Ошибка чтения: достигнут конец файла"); else if (ferror(file)) < puts("Ошибка чтения файла"); 

или на C++ - наподобие

for(int n; file >> n; ) < // Обработка считанного значения n >if (file.bad()) std::cout  

Отслеживать
ответ дан 27 мая 2018 в 4:12
219k 15 15 золотых знаков 119 119 серебряных знаков 230 230 бронзовых знаков

@ДжонниКэтсвилл А вы что, не понимаете, что c>>n выполняет попытку считать дальше этой 1 - ну, вдруг там 123 или 11 🙂 - т.е. он таки пытается читать за концом файла. Замените int на char и убедитесь, что выводится 000 . Так что, простите, мимо кассы.

Использование feof()

Как утверждалось ранее, файловая система С может оперировать двоичными данными. Когда файл открыт для двоичного ввода, может быть прочитано целочисленное значение, равное EOF. В результате getc() будет отображать состояние наличия конца файла, хотя физический конец еще не достигнут. Для разрешения данной проблемы в С имеется функция feof(), используемая для определения конца файла при чтении двоичных данных. Она имеет следующий прототип:

где fp идентифицирует файл. Функция feof() возвращает не 0, если достигнут конец файла, в противном случае она возвращает 0. Следовательно, следующий код читает двоичный файл, пока не встретится метка конца файла:

while(!feof(fp)) ch = getc(fp);

Данный метод может применяться не только к двоичным, но и к текстовым файлам.

Следующая программа копирует файл любого типа. Следует обратить внимание, что файлы открываются в двоичном режиме, и feof() используется для проверки наличия конца файла:

/* данная программа копирует один файл в другой */
#include
int main(int argc, char *argv[])
FILE *in, out;
char ch;
if (argc!=3) printf("You forgot to enter a filename.");
return 1;
>
if( (in=fopen(argv[1] , "rb")) == NULL)>
printf ("Cannot open source file.");
return 1;
>
if((out=fopen(argv[ 2 ] , "wb")) == NULL) printf ("Cannot open destination file.");
return 1;
>

/* копирование файла */
while (!feof (in)) сh = getc(in);
if ( !feof(in)) putc(ch, out);
>
fclose(in);
fclose(out);
return 0;
>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *