逆序儲存檔案(二)——使用c標準庫函式fopen,fseek,fread,fwrite
阿新 • • 發佈:2019-02-09
使用c標準庫函式實現小檔案逆序儲存邏輯是:
1.用fopen函式成功開啟原始檔和目標檔案,原始檔用只讀方式(r)開啟,目標檔案用追加寫入(a)的方式開啟;
2.迴圈使用fseek定位檔案指標(fopen的返回值),從SEEK_END(檔案末尾)位置開始,每次多向前偏移一個位元組,知道fseek返回一個非0值結束;(fseek能改變檔案指標)
3.每次定位成功後,讀取一個字元(fread)到緩衝區,讀取成功後將緩衝區資料寫入(fwrite)目標檔案指標。
這邊要注意4個函式的返回值,這些返回值是程式每一步執行的判斷標誌
fopen返回一個檔案指標,返回NULL表示檔案開啟失敗;
fseek返回一個整數,成功定位返回0,否則返回非0值;
fread返回讀取的字元數。
測試程式碼如下:
#include <iostream>
#include <ctime>
#include <fstream>
#include <tchar.h>
using namespace std;
void ReverseCopy(const char* pSrcFile, const char* pDestFile) { if(!pSrcFile || !pDestFile) return; time_t tBegin = time(0); FILE* pSrc = fopen(pSrcFile, "r"); if(!pSrc) return; FILE* pDest = fopen(pDestFile, "a"); if(!pDest) { fclose(pSrc); return; } TCHAR ch[1] = {0}; int nElementSize = sizeof(TCHAR); long lSeek = -nElementSize; while(fseek(pSrc, lSeek, SEEK_END) == 0) { if(fread(ch, nElementSize, 1, pSrc) > 0) { fwrite(ch, nElementSize, 1, pDest); } lSeek -= nElementSize; } fclose(pDest); fclose(pSrc); time_t tEnd = time(0); cout<<"File op time = "<<tEnd - tBegin<<endl; } int main() { ReverseCopy("in.txt", "c_out.txt"); return 0; }
經過測試,該方法的效能也存在問題,不適合大檔案.
修改:
以上方法只支援windows中ANSI編碼的純中文文字檔案,因為UNICODE編碼的工程中TCHAR為雙位元組,每次讀取兩個位元組作為一個字元,遇到英文字元就會出現亂碼,現在根據中文字元與英文字元的編碼特點做以下修改。ANSI編碼的檔案中,中文字元(2個位元組)高位元組最高位為1,英文字元(1個位元組)最高位為0,所以每次讀取兩個位元組,與0x00008000作與運算,結果為真時,則將兩個位元組作為一箇中文字元寫入目標檔案,否則,只寫入高位元組。ReverseCopy函式修改如下:
void ReverseCopy(const char* pSrcFile, const char* pDestFile) { if(!pSrcFile || !pDestFile) return; time_t tBegin = time(0); FILE* pSrc = fopen(pSrcFile, "r"); if(!pSrc) return; FILE* pDest = fopen(pDestFile, "a"); if(!pDest) { fclose(pSrc); return; } const int nflagChinese = 0x00008000; const int nWordSize = sizeof(WORD); const int nCharSize = 1; WORD ch[1] = {0}; long lSeek = -2; bool bLastIsEn = false;//記錄上次寫入的是中文字元函式英文字元,如果是英文,那檔案可能只剩一個位元組,沒法再向前seek兩個位元組 while(fseek(pSrc, lSeek, SEEK_END) == 0) { if(fread(ch, nWordSize, 1, pSrc) > 0) { WORD wCh = ch[0]; if(wCh & nflagChinese)//檢查是否為中文字元 { fwrite(ch, nWordSize, 1, pDest); lSeek -= 2; bLastIsEn = false; } else {//高位元組為英文,只寫入高位元組 BYTE b = HIBYTE(wCh); fwrite(&b, 1, 1, pDest); lSeek -= 1; bLastIsEn = true; } } } if(bLastIsEn && fseek(pSrc, ++lSeek, SEEK_END) == 0) {//讀取最後一個英文位元組 char lastCh[1] = {0}; if(fread(lastCh, 1, 1, pSrc) > 0) fwrite(lastCh, 1, 1, pDest); } fclose(pDest); fclose(pSrc); time_t tEnd = time(0); cout<<"File op time = "<<tEnd - tBegin<<endl; }