对WORD文档的关键字搜索
?
在網(wǎng)上找了很久,基本上對OFFICE編程都是用的OFFICE的OLE對象,但是關(guān)鍵字搜索這個問題,感覺用OLE對象顯得臃腫,而且不夠靈活,于是便寫直接讀取文件內(nèi)容進行搜索的辦法。
首先涉及的問題就是字符編碼的問題。先是在網(wǎng)上稍稍補充了一下關(guān)于UNICODE的基礎(chǔ)知識,呵呵,不過講到的東西有點多,GB、UNICODE、UTF……,也沒空細細捋順它們的關(guān)系,知道了個大概。最重要的就是了解了如何在VC下用UNICODE,呵呵。主要有:
1.為工程添加UNICODE和_UNICODE預處理選項,在VC.net中就是 項目 -> 屬性 -> c/c++ -> 預處理器 在“預處理定義”中加入這兩個宏定義(vc6中 project -> settings -> c/c++ -> general 中的 Preprocessor definitions)。
2.Include <TCHAR.h>(一般在stdafx.h中)然后把所有使用char*定義變量的地方換為LPTSTR/TCHAR*或LPCTSTR/const TCHAR*(對應(yīng)于const char*)。
3.把所有的字符串常量用_T()宏包起來,比如 TCHAR* szText = _T("我的Text")。
至于說把所有的C庫字符串操作函數(shù)也做相應(yīng)的替換,在這里我倒是沒用上多少。
下面說說我的程序。
首先,通過用ultraedit對幾個word文檔的實驗觀察,發(fā)現(xiàn)它是這樣的:WORD中如果內(nèi)容都是英文的,則用ASCII碼存儲,一旦出現(xiàn)漢字,則都用unicode存儲。
一開始所以我在掃描文件的時候,同時用兩個緩沖區(qū)保存兩種編碼方式讀到的內(nèi)容,一個是WORD型的(wUBuffer),一個是TCHAR型的(cABuffer[2]),(這里為了敘述方便,假定了緩沖區(qū)的大小)然后兩個都與待查內(nèi)容比較。
這里算法是這樣的:
1、讀入一個WORD(兩個字節(jié),低位在前,高位在后)到wUBuffer。
2、將讀到的兩個字節(jié)的低位賦給cABuffer[1]。
3、wUBuffer的高位若為0,則不處理;高位若不為0,將cABuffer左移一個元素,即aABuffer[0]=aABuffer[1],然后將wUBuffer的高位賦給aABuffer[1]。
實際上當然不會只用這么小的緩沖區(qū)。這兩個緩沖區(qū)的開辟是根據(jù)待查字符串的長度。
后來忽然想到,按照上面的方法對文件一路讀下去,是兩個字節(jié)兩個字節(jié)的讀,并未實現(xiàn)真正的逐字節(jié)掃描,于是做了如下改進:
對WORD型和TCHAR型分別開辟兩個緩沖區(qū)wUBuffer[0]、wUBuffer[1]、aABuffer[0]、aABuffer[1],下標為0的記錄掃描第偶數(shù)個字節(jié)時的內(nèi)容,下標為1的記錄第奇數(shù)個字節(jié)時的內(nèi)容,這樣在用i做循環(huán)對文件掃描時,只要wUBuffer[i % 2]和aABuffer[i % 2]就行了。而這四個緩沖區(qū)又分別是記錄讀取內(nèi)容的數(shù)組,即實際上都是二維數(shù)組。在用i做文件字節(jié)數(shù)的循環(huán)時,每次循環(huán)都把文件指針指向偏移i的位置即可。
下面是代碼。算法都實現(xiàn)在這了,應(yīng)用時可根據(jù)實際需要改進。比如這里是找到即停止,實際上可以繼續(xù)查找,把所有找到的地方都記錄下來,最后一起顯示,也可以做成像搜索引擎那樣把找到的關(guān)鍵字前后的一些內(nèi)容一塊顯示出來。
int?SearchInDoc()...{
????CString?strToFind=_T("牧場");
????????????
????DWORD?l=strToFind.GetLength();
????????????
????//開辟緩沖區(qū)。unicode和ASNI各用兩個,[0]用于掃描偶數(shù)字節(jié)時的,[1]用于掃描奇數(shù)字節(jié)時
????WORD*?wUBuffer[2];
????wUBuffer[0]=new?WORD[l+1];????//unicode?編碼時的查找緩沖區(qū)
????wUBuffer[1]=new?WORD[l+1];????//unicode?編碼時的查找緩沖區(qū)
????LPTSTR?cABuffer[2];
????cABuffer[0]=new?TCHAR[l+1];????//與wUBuffer等長的ANSI編碼時的查找緩沖區(qū)
????cABuffer[1]=new?TCHAR[l+1];????//與wUBuffer等長的ANSI編碼時的查找緩沖區(qū)
????CString?strUBuffer[2],strABuffer[2];?//字符串緩沖區(qū),便于操作
????DWORD?i,j;
????
????//初始化4個緩沖區(qū)
????for(i=0;i<l;i++)????
????...{
????????wUBuffer[0][i]=0xffff;
????????cABuffer[0][i]=(TCHAR)0xff;
????????wUBuffer[1][i]=0xffff;
????????cABuffer[1][i]=(TCHAR)0xff;
????}
????//最后一個元素保存字符串結(jié)束符(實際上是賦值為結(jié)束符,但是這里好像會被當成實際的結(jié)束符,導致后面的代碼消失,故用0暫時代替)
????wUBuffer[0][l]=0;
????cABuffer[0][l]=0;
????wUBuffer[1][l]=0;
????cABuffer[1][l]=0;
????HANDLE?hFile;
????hFile?=?CreateFile(_T("f:/玩在北京.doc"),?GENERIC_READ,?FILE_SHARE_READ,?NULL,?OPEN_EXISTING,?FILE_ATTRIBUTE_NORMAL,?0);
????????
????if(hFile?==?INVALID_HANDLE_VALUE)...{
????????//TRACE("read?error:%s?%d ",?pFilePath,?GetLastError());
????????return?0;
????}
????DWORD?dwReadLen,dwFileLen=GetFileSize(hFile,NULL);
????
????for(i=0;i<dwFileLen;i++)????//掃描文件
????...{
????????SetFilePointer(hFile,?i,?NULL,?FILE_BEGIN);??//設(shè)置文件指針位置
????????//兩個緩沖區(qū)都左移一個元素
????????for(j=0;j<l-1;j++)????
????????...{
????????????wUBuffer[i?%?2][j]=wUBuffer[i?%?2][j+1];
????????????cABuffer[i?%?2][j]=cABuffer[i?%?2][j+1];
????????}
????????????
????????//向Unicode緩沖區(qū)最后一個元素讀入兩個字節(jié)
????????ReadFile(hFile,?&wUBuffer[i?%?2][l-1],?2,?&dwReadLen,?NULL);
????????//將讀到的兩個字節(jié)處理:
????????//將低位賦給cABuffer[l-1]
????????//高位若為0,則不處理;
????????//高位若不為0,將cABuffer再左移一個元素,賦給cABuffer[l-1]
????????
????????cABuffer[i?%?2][l-1]=(wUBuffer[i?%?2][l-1]?&?0x00ff);
????????if((wUBuffer[i?%?2][l-1]?&?0xff00)>0x0000)
????????...{
????????????for(j=0;j<l-1;j++)????
????????????????cABuffer[i?%?2][j]=cABuffer[i?%?2][j+1];
????????????cABuffer[i?%?2][l-1]=((wUBuffer[i?%?2][l-1]?&?0xff00)>>8);
????????}
????????
????????strUBuffer[i?%?2]=(CString)wUBuffer[i?%?2];
????????strABuffer[i?%?2]=(CString)cABuffer[i?%?2];
????????strToFind.AnsiToOem();
????????strUBuffer[i?%?2].AnsiToOem();
????????strABuffer[i?%?2].AnsiToOem();
????????if(strUBuffer[i?%?2]!=""?&&?(strUBuffer[i?%?2].Find(strToFind)>-1))
????????...{
????????????cout<<"Found!"<<endl;
????????????cout<<"Unicode"<<endl;
????????????delete?[]wUBuffer[0];
????????????delete?[]wUBuffer[1];
????????????delete?[]cABuffer[0];
????????????delete?[]cABuffer[1];
????????????return?i-l;
????????}
????????if(strABuffer[i?%?2]!=""?&&?(strABuffer[i?%?2].Find(strToFind)>-1))
????????...{
????????????cout<<"Found!"<<endl;
????????????cout<<"ANCI"<<endl;
????????????delete?[]wUBuffer[0];
????????????delete?[]wUBuffer[1];
????????????delete?[]cABuffer[0];
????????????delete?[]cABuffer[1];
????????????return?i-l;
????????}
????}
????delete?[]wUBuffer[0];
????delete?[]wUBuffer[1];
????delete?[]cABuffer[0];
????delete?[]cABuffer[1];
????cout<<"Not?Found!"<<endl;
????CloseHandle(hFile);
????return?-1;
} 調(diào)試過程中還出了個插曲,VC莫名其妙不認識UNICODE預編譯了,調(diào)用AnsiToOem()的時候硬是說'CharToOemA' : is not a member of 'CString' ,后來又莫名其妙好了,真是FT,耗了一天的時間。詳見 http://community.csdn.net/Expert/topic/5410/5410681.xml?temp=.88081,呵呵。
總結(jié)
以上是生活随笔為你收集整理的对WORD文档的关键字搜索的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。