在网上找了很久,基本上对OFFICE编程都是用的OFFICE的OLE对象,但是关键字搜索这个问题,感觉用OLE对象显得臃肿,而且不够灵活,于是便写直接读取文件内容进行搜索的办法。
首先涉及的问题就是字符编码的问题。先是在网上稍稍补充了一下关于UNICODE的基础知识,呵呵,不过讲到的东西有点多,GB、UNICODE、UTF……,也没空细细捋顺它们的关系,知道了个大概。最重要的就是了解了如何在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*(对应于const char*)。
3.把所有的字符串常量用_T()宏包起来,比如 TCHAR* szText = _T("我的Text")。
至于说把所有的C库字符串操作函数也做相应的替换,在这里我倒是没用上多少。
下面说说我的程序。
首先,通过用ultraedit对几个word文档的实验观察,发现它是这样的:WORD中如果内容都是英文的,则用ASCII码存储,一旦出现汉字,则都用unicode存储。
一开始所以我在扫描文件的时候,同时用两个缓冲区保存两种编码方式读到的内容,一个是WORD型的(wUBuffer),一个是TCHAR型的(cABuffer[2]),(这里为了叙述方便,假定了缓冲区的大小)然后两个都与待查内容比较。
这里算法是这样的:
1、读入一个WORD(两个字节,低位在前,高位在后)到wUBuffer。
2、将读到的两个字节的低位赋给cABuffer[1]。
3、wUBuffer的高位若为0,则不处理;高位若不为0,将cABuffer左移一个元素,即aABuffer[0]=aABuffer[1],然后将wUBuffer的高位赋给aABuffer[1]。
实际上当然不会只用这么小的缓冲区。这两个缓冲区的开辟是根据待查字符串的长度。
后来忽然想到,按照上面的方法对文件一路读下去,是两个字节两个字节的读,并未实现真正的逐字节扫描,于是做了如下改进:
对WORD型和TCHAR型分别开辟两个缓冲区wUBuffer[0]、wUBuffer[1]、aABuffer[0]、aABuffer[1],下标为0的记录扫描第偶数个字节时的内容,下标为1的记录第奇数个字节时的内容,这样在用i做循环对文件扫描时,只要wUBuffer[i % 2]和aABuffer[i % 2]就行了。而这四个缓冲区又分别是记录读取内容的数组,即实际上都是二维数组。在用i做文件字节数的循环时,每次循环都把文件指针指向偏移i的位置即可。
下面是代码。算法都实现在这了,应用时可根据实际需要改进。比如这里是找到即停止,实际上可以继续查找,把所有找到的地方都记录下来,最后一起显示,也可以做成像搜索引擎那样把找到的关键字前后的一些内容一块显示出来。
... {
CString strToFind=_T("牧场");
DWORD l=strToFind.GetLength();
//开辟缓冲区。unicode和ASNI各用两个,[0]用于扫描偶数字节时的,[1]用于扫描奇数字节时
WORD* wUBuffer[2];
wUBuffer[0]=new WORD[l+1]; //unicode 编码时的查找缓冲区
wUBuffer[1]=new WORD[l+1]; //unicode 编码时的查找缓冲区
LPTSTR cABuffer[2];
cABuffer[0]=new TCHAR[l+1]; //与wUBuffer等长的ANSI编码时的查找缓冲区
cABuffer[1]=new TCHAR[l+1]; //与wUBuffer等长的ANSI编码时的查找缓冲区
CString strUBuffer[2],strABuffer[2]; //字符串缓冲区,便于操作
DWORD i,j;
//初始化4个缓冲区
for(i=0;i<l;i++)
...{
wUBuffer[0][i]=0xffff;
cABuffer[0][i]=(TCHAR)0xff;
wUBuffer[1][i]=0xffff;
cABuffer[1][i]=(TCHAR)0xff;
}
//最后一个元素保存字符串结束符(实际上是赋值为结束符,但是这里好像会被当成实际的结束符,导致后面的代码消失,故用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); //设置文件指针位置
//两个缓冲区都左移一个元素
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缓冲区最后一个元素读入两个字节
ReadFile(hFile, &wUBuffer[i % 2][l-1], 2, &dwReadLen, NULL);
//将读到的两个字节处理:
//将低位赋给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;
}