使用c庫函式fopen、fread進行檔案的操作,是大家都熟悉的處理方式。但最近在公司的實際專案中發現了乙個很奇怪也很有趣的現象。
公司的專案使用vc編寫前置程式,對客戶傳來的報文進行字符集轉碼操作。在轉碼過程中使用了臨時檔案。問題出現在讀取臨時檔案過程中。**很簡單:
file *fp;
fp = fopen(utf8file,"r");
if (fp == null)
char * utf8buff;
int nlen=0;
fseek(fp,0,seek_end);
nlen = ftell(fp);
printf("長度nlen[%d]\n",nlen);
if (nlen <1 )
fseek(fp,0,seek_set);
utf8buff = (char *) malloc(nlen + 1);
memset(utf8buff,0,nlen + 1);
int rtnlen = fread(utf8buff,sizeof(char),nlen,fp);
客戶上送的檔案為utf-8字符集的xml報文,內容為客戶的賬務處理資訊,有戶名等中文內容。由於客戶之前只傳來一條明細報文,這個程式執行多年都沒有問題。但最近客戶上送的明細報文增加為多條,就產生了一些奇妙的變化。
產生的現象是utf8buff這個緩衝區的內容最後總會是一段亂碼,亂碼內容就是檔案最後的內容。比如要讀出的檔案內容為「abc回車換行def回車換行gh」,緩衝區裡就會變成「abc回車def回車ghgh」。我能理解的原因是由於fopen時以字串方式開啟,回車換行自動轉換為了回車,少了兩個字元。但fread不負責將之後的內容清空嗎?為什麼原檔案的內容還留在原來的位置?就像海水退下去後,礁石顯露了出來。
網上搜了一回,發現有個同學碰到了和我類似的問題,總結得很好。具體內容請看這裡。
摘抄一部分:
貌似在這些承諾中,有兩點沒有被提及:1,將讀取到的內容存入指定記憶體區域後,是否要在結尾補上字串終結符
'\0'
?2,如果因為 eof 或者其他原因,讀到的位元組數 m 小於指定位元組數 count,
fread
是否僅僅對大小為 m 的記憶體區域做出修改呢?
首先可以確定第一點是否定的, fread 並非僅僅為文字流讀取設計,它不會自己去補這個終結符;第二點則是我遇到的奇怪問題所在:文件承諾對傳入指標引數指向的記憶體區域寫入,並且預期的寫入尺寸由 count 引數指定,而讀取到的實際尺寸小於預期尺寸一律視為一種異常情況,包括 eof 導致的截斷。異常情況下文件的約定**並沒有承諾**僅僅寫入 m 大小的區域,而不汙染 (m, count] 的區域。儘管 linux 下 glibc 做了這件沒承諾的事情,但 libc 並沒有這個責任。
所以,如果不考慮 eof 以外的讀取異常,用 fread 讀取乙個最終讀到尺寸可能小於預期尺寸的檔案,應該手動補上終結符 '\0' 以解決餘下的區域可能的汙染問題。
對c/c++庫函式、或作業系統的api函式,我們可以有乙個認知:它們只做它們承諾過的事,沒承諾過的事,會不會多做,不確定。
比如這個例子「abc回車換行def回車換行gh」,以字串方式fread,實際返回內容會比原檔案少兩個字元,那麼緩衝區中的這最後兩個位元組會是什麼值呢?編譯器或作業系統會把這兩個位元組賦值為0x00嗎?從此次事件來看,結論是,不一定。因為客戶上送的檔案,哪怕只改動其中的任何乙個位元組的內容的值,亂碼就消失了,空餘出來的結尾位元組的值就變成了0x00。
庫函式與作業系統api的實現方式的確比較詭異,也許我們深入到原始碼內部,才能最終找到問題的原因所在。在透徹地了解機理之前,我們還是老老實實的,用bin方式讀取檔案吧。
另:有的同學也遇到過fread的怪問題,擴充套件閱讀,也許能為大家提供更多的思路,以後遇到類似的問題,就不要再掉坑里了。請看這裡。
CVS使用 windows環境下
經驗 1,wincvs中執行checkout動作的單位是 module 即匯入cvs倉庫的專案名稱。import時有設定,要記好。2,cvsroot pserver username password localhost 倉庫名 3,初始化乙個cvs倉庫,並將乙個現有專案匯入到cvs中 1 通過cv...
windows環境下安裝及使用redis
介紹安裝redis之前先稍微的了解一下redis。使用redis之前需要考慮自己的業務邏輯符不符合redis快取,redis與memcache同為時下比較突出的快取機制,同為記憶體快取,但區別也是有的,redis不僅僅將資料存放於記憶體之中,它還支援快照 rdb 以及aof持久化,就是可以將記憶體中...
windows 下 gvim 使用 總結 環境搭建
1.gvim的特點 vim要求全部鍵盤操作,而gvim可以使用滑鼠進行視覺化操作,即gvim是vim的圖形化介面 5.在 vimrc中加入常用配置 vimrc在gvim的目錄下 set nu 設定行號 colorscheme desert 設定配色方案 syntax on 語法高亮 syntax e...