1. 五笔反查工具
也许你会说,反查什么的,直接一个MAP容器就搞定了,有什么好说的。的确,最简单的就是这样。但即使是这样,还要准备字,五笔码,字根图,以及它们的对应关系。而这些在网上是没有现成的,也就是说要自己准备。我准备的方法是写个小爬虫,到一个五笔反查网站上将这些东西一并弄下来(具体可参考上一篇文章)。准备好最重要的部分后,如果还要弄得像样一点,就要再花一番功夫。所以说,还是有点东西写的。
先放几张图,再来分享做这东西时遇到的难点。
2. 字根图
一共有6763张字根图,如果直接发布,显然不美观。因此有必要打包下,再发布。可惜的是,在网上没找到合适的打包工具(如果知道,还请告诉偶一下>_<)。剩下的就只有一条路了——自己打包。我用的打包方法看简单:做一个记录文件偏移的文件头,然后将图片按顺序放到文件头之后。
记录文件偏移用以下这个类表示:
struct FileIndex { //辅助类,打包文件中,单个文件的偏移信息
DWORD dwOffset; //离文件头偏移多少
DWORD dwSize; //图片本身多大
char szName[256]; //图片原本的名称
};
假如我将1.GIF,2.GIF打包到0.ZERO这个文件中,那么0.ZERO的文件结构应该是这样的
(2) (FileIndex) (FileIndex) (content of 1.GIF) (content of 2.GIF)
上面的2表示一共有两个文件或者两个FileIndex,第一个FileIndex就是第1.GIF的信息,第二个FileIndex是2.GIF的信息,接着的是1.GIF和2.GIF的内容
具体的打包代码如下:
//将strSrcDirectory目录下的全部文件打包到strDestFile中
BOOL CFilePack::MakePack(const std::string& strSrcDirectory, const std::string& strDestFile) {
std::vector<std::string> arFileList;
CString szDirectory;
szDirectory.Format("%s", strSrcDirectory.c_str());
GetAllFileNameInDirectory(szDirectory, arFileList); //将指定文件夹内的文件名都放到vector容器中
FILE* fp = fopen(strDestFile.c_str(), "wb");
if (!fp) {
return FALSE;
}
DWORD dwSize = arFileList.size();
fwrite(&dwSize, 1, 4, fp); //写入一共有几个文件
FileIndex* pFileList = new FileIndex[arFileList.size()];
fwrite(pFileList, 1, arFileList.size() * sizeof(FileIndex), fp); //先预留一块地方保存文件偏移信息
//按顺序打包文件
for (int i = 0; i < arFileList.size(); ++i)
{
strcpy(pFileList[i].szName, arFileList[i].c_str());
std::string filePath = strSrcDirectory + "\\" + arFileList[i];
FILE* fbuf = fopen(filePath.c_str(), "rb");
if (!fbuf)
{
fclose(fp);
delete pFileList;
return FALSE;
}
DWORD fsize = filelength(fileno(fbuf));
pFileList[i].dwSize = fsize;
pFileList[i].dwOffset = ftell(fp);
char* buf = new char[fsize];
fread(buf, 1, fsize, fbuf);
fclose(fbuf);
fwrite(buf, 1, fsize, fp);
delete buf;
}
//将文件的偏移信息写到之前预留的地方
fseek(fp, 4, SEEK_SET);
fwrite(pFileList, 1, arFileList.size() * sizeof(FileIndex), fp);
fclose(fp);
delete pFileList;
return TRUE;
}
打好包,要取出来也很简单:先读出全部的FileIndex,再根据文件名查找到相应的FileIndex,最后根据偏移值和文件大小就能将原来的内容读出来了。
3. 显示GIF
字根图全是GIF格式的,MFC自带的Picture控件不支持GIF格式,所以。。。不幸中的万幸,第三方图片显示支持很容易就能找到。经过一番比较后,最后选择的是PictureEx。原因是PictureEx很小,只有一个头文件和一个CPP文件。此外,使用也很方便:调用Load(图片路径),再调用Draw()就能将图显示出来了。不过前面已经将图片全部打包成一个文件了,难道使用时还要将图片内容读出来,再写到XX.GIF上吗?回答这个问题前,先来看下Load有哪些重载形式吧
// loads a picture from a file
// i.e. Load(_T("mypic.gif"));
BOOL Load(LPCTSTR szFileName);
// loads a picture from a global memory block (allocated by GlobalAlloc)
// Warning: this function DOES NOT free the global memory, pointed to by hGlobal
BOOL Load(HGLOBAL hGlobal, DWORD dwSize);
// loads a picture from a program resource
// i.e. Load(MAKEINTRESOURCE(IDR_MYPIC),_T("GIFTYPE"));
BOOL Load(LPCTSTR szResourceName,LPCTSTR szResourceType);
从以上描述看,能用的是第二个重载形式——将一个内存块的内容当图片内容显示。具体的做法是将图片内容从打包文件中读出来后,再拷贝到hGlobal所指的内存块,最后调用Load函数。代码如下:
//显示fileName指定的图片
void CWuBiFangCha_ZeroDlg::Display86ZiGen(CString fileName) {
std::string strFileName(fileName.GetBuffer(0));
char* buf = m_packFile.GetFile(strFileName); //从打包文件中,读取fileName指定文件的内容
if( buf ) {
int iFileSize = m_packFile.GetFileSize(strFileName); //得到fileName指定文件的大小
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, iFileSize);
if( hGlobal == NULL ) {
MessageBox(_T("内存不足"));
AfxPostQuitMessage(0); //退出程序
}
LPVOID pvData = GlobalLock(hGlobal);
if( pvData == NULL ) {
GlobalUnlock(hGlobal);
MessageBox(_T("无法锁定内存"));
AfxPostQuitMessage(0);
}
memcpy(pvData, buf, iFileSize);
GlobalUnlock(hGlobal);
m_86ZiGen.Load(hGlobal, iFileSize); //读图片内容
m_86ZiGen.Draw(); //显示图片
delete[] buf;
::GlobalFree(hGlobal);
}
fileName.ReleaseBuffer();
}
4. 资源
a. PictureEx
http://ishare.iask.sina.com.cn/f/22351801.html
b. 五笔反查_零式 V1.0
http://ishare.iask.sina.com.cn/f/22375411.html
c. MFC42D.DLL的介绍
http://www.cppblog.com/duqingwei/archive/2011/06/12/148545.html
//-----------------------------------------------------------------------------------------
2011.12.25下午更新
感谢lcs-帅提出的无法运行问题。缺少的MFC42D.DLL是DEBUG版才需要的DLL,现在改成了Release版本,并用了静态编译。
如果还有什么问题,请留言。