日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言标准io中可读可写,C语言标准IO: [先读再feof] VS [先feof再读]

發布時間:2024/9/30 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言标准io中可读可写,C语言标准IO: [先读再feof] VS [先feof再读] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

剛學習C語言讀取文件的時候,可能都遇到過這個“bug”,讀到末尾時數據有重復。

解決方案也是五花八門,甚至有人把數據先緩存了,再忽略掉最后一組....

不妨看一段代碼,兩種解決方案,猜猜看,究竟哪一個方案是正確的。

/*

*方案一:先判斷,后讀取

*/

while(!feof(fp))?{

fread(buf,?1,?100,?fp);

//do_some_thing

}

/*

*方案二:先讀取,后判斷

*/

while(1)?{

fread(buf,?1,?100,?fp);

if(feof(fp))

break;

//do_some_thing

}

/*

*方案一:先判斷,后讀取

*/

while (!feof(fp)) {

fread(buf, 1, 100, fp);

//do_some_thing

}

/*

*方案二:先讀取,后判斷

*/

while (1) {

fread(buf, 1, 100, fp);

if (feof(fp))

break;

//do_some_thing

}

曾經,我毫不猶豫的選擇一,過了不久又毫不猶豫的選擇二。然而,非常遺憾,沒有

一個是正確的。暫不解釋why,來一段MSDN上的代碼,關于feof的一個example:

#include?

#include?

intmain(void)

{

intcount,?total?=?0;

charbuffer[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?);

}

#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 );

}

不要驚訝,你沒看錯,MSDN是先使用feof,然后再讀取,跟網上的看法有些出入。

到底是為什么呢?我覺得,還是從feof和fread的源碼開始分析比較好,實現可能不同,用法應該是一致的。以GLIBC 2.10的源碼為例。不管是 unlocked 還是 非unlocked,feof最終還是調

用了unlocked的那個_IO_feof_unlocked“函數”,所以分析該“函數”即可。

#define?_IO_feof_unlocked(__fp)?(((__fp)->_flags?&?_IO_EOF_SEEN)?!=?0)

#define _IO_feof_unlocked(__fp) (((__fp)->_flags & _IO_EOF_SEEN) != 0)

現在可以得到一個結論:

feof的返回值,僅僅取決于文件結構是否被打上 _IO_EOF_SEEN 標志。

feof本身并不影響這個標志,因此可以斷定,標志是在讀取的時候被修改的,于是

去追尋fread的源碼。經歷了宏的泥淖,欣賞了純C填虛函數表模擬C++多態的壯

烈,終于找到了fread的背后黑手。fread實際上可能調用不同的函數,mmap文件

和普通文件的處理,是不一樣的,不過知道其中一個便可,因為底層的差異,對程

序員是透明的,只要展示給程序員看的一面是一致的就OK。且看關鍵部分代碼:

count?=?_IO_SYSREAD?(fp,?s,?count);

if(count?<=?0)

{

if(count?==?0)

fp->_flags?|=?_IO_EOF_SEEN;

else

fp->_flags?|=?_IO_ERR_SEEN;

break;

}

count = _IO_SYSREAD (fp, s, count);

if (count <= 0)

{

if (count == 0)

fp->_flags |= _IO_EOF_SEEN;

else

fp->_flags |= _IO_ERR_SEEN;

break;

}

看到_IO_SYSREAD有什么感覺呢? 哦,你可以把它當成表現跟read系統調用一樣

的東西,返回值大于0,就是實際讀取字節數;等于0,就是文件之前已被讀完(或者

是文件根本就是空的),本次沒讀到數據;小于0就是失敗,有錯誤發生。

總結代碼中的流程和細節,整理出來就是:

標準IO是帶有緩存的,每一次請求,未必對應一次系統調用

緩存剩余字節數大于等于請求的,直接使用緩存,不產生系統調用

fread當且僅當,系統調用讀取到的字節數為0時,才會打上結束標志

feof只檢查_IO_EOF_SEEN標志位,不做其它影響返回值的判斷

根據這些,不難得出以下結論:

文件全部讀完,即使緩存用盡且SYS_READ讀盡,feof未必返回真值

SYS_READ實際讀取到0字節的事情發生后,feof一定返回真值

fread實際讀取的字節數少于預期時,feof一定返回真值

feof可以只檢查標志就做出判斷,而fread可能多一次系統調用才知道結束

feof返回0的時候,上一次讀取的數據,一定是有效數據

至此,已經不難理解,為什么最上面貼的兩種方案都是錯的。因為它們都沒有對

fread的返回值做出反應,如果對這個返回值加以處理,無論是先讀后判斷還是先

判斷后讀取,都是沒有問題的,都能得到正確的結果。當然,這得有個前提,就是

除了feof外,有別的方法判斷是否結束,倘若如fgetc那般,讀取的字符作為函數返

回值,讀二進制文件時,就無法判斷了,因為二進制文件本身也可能含有EOF字符。

這種情況,只有一種方案,就是先讀取,再用feof判斷是否結束。

做了那么長時間的鋪墊,現在回到本文的核心。是先讀取還是先判斷?我認為:

在決定用哪一種方案前,首先要考慮用于讀取的那個函數的返回值的意義

如果你不想了解細節,也不屑于些許性能,一律先讀取后判斷,肯定不會有錯

如果讀取函數的返回值蘊含是否讀取成功,那么先判斷后讀取,可能更加高效

但是有一點,不管是哪種方式,如果用于讀取的函數,返回值包含讀取是否成功或者實際 讀取字節數等信息,這個信息是一定要考慮的。同樣,寫入操作也好,其它操作也好,只 要函數帶有返回值,且未注明這個返回值沒有意義,都應該認真推敲下這個返回值的意義, 然后決定是否需要處理這個返回值。

總結

以上是生活随笔為你收集整理的c语言标准io中可读可写,C语言标准IO: [先读再feof] VS [先feof再读]的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。