1. 程式人生 > >逆序儲存檔案(二)——使用c標準庫函式fopen,fseek,fread,fwrite

逆序儲存檔案(二)——使用c標準庫函式fopen,fseek,fread,fwrite

使用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;
}