接上篇Android 来电归属地 Jni 使用C++对二进制文件查询(一)


1. 二进制文件第二版


通过上篇文章提到的压缩方式,我们得到了一个二进制的文件。格式如图1


// -------------------------------------------------------//  Name:         ChangeTxtToBinary//  Description:  Read every line in txt file, convert it to special customized format//                binary file.//                binary file content: count of total records, records, cities  //  Arguments:    txt file name, binary file name//  Return Value: true means success// -------------------------------------------------------bool ChangeTxtToBinary(const char* inFileName,const char* outFileName){FILE* inFile = 0;inFile = fopen(inFileName,"rb");if(inFile == 0)return false;FILE* outFile = 0;outFile = fopen(outFileName,"wb");if(outFile == 0)return false;int phoneInfoCompressCount = 0;//firstly, write the count of total phoneInfoCompress.fwrite(&phoneInfoCompressCount,sizeof(int),1,outFile);//secondly, write all the recordsWriteRecords(inFile, phoneInfoCompressCount, outFile);//thirdly, rewrite the phoneInfoCompressCount.fseek(outFile,0,SEEK_SET);fwrite(&phoneInfoCompressCount,sizeof(int),1,outFile);//last, write all the citiesWriteCities(outFile);fclose(inFile);fclose(outFile);return true;}



2. 一些C语言中的文件操作函数解释


因为要考虑到放在Android中编译,就没有使用stl标准库,而是用C语言中的文件操作函数。如果不熟悉的话可以看下下面的例子。

FILE* inFile = 0;inFile = fopen("test.txt","rb"); //使用fopen打开一个文件,rb表示以二进制方式读if(inFile == 0)return false;FILE* outFile = 0;outFile = fopen("test.dat","wb");//使用fopen打开一个文件,wb表示以二进制方式写入if(outFile == 0)return false;int num = 0;fwrite(&num,sizeof(int),1,outFile);//使用fwrite往文件中写入数据,这是一个写入int的例子int count = 0;fread(&count,sizeof(int),1,outFile);//使用fread读取文件中的值,这是一个读int的例子int number;char* firstCityName = new char[256];fscanf(inFile,"%d,%s",&number,firstCityName);//使用fscanf读取文件中的一行,分别存到两个变量中fseek(outFile,0,SEEK_SET);//使用fseek来改变读写位置,SEEK_SET表示文件开始处。SEEK_END表示文件尾部                          //比如,我想跳到文件头部写了一个int,想直接跳到文件尾部写其他  //内容,就可以这样使用  //fseek(outFile,0,SEEK_SET);  //fwrite...  //fseek(outFile,0,SEEK_END);  //fwrite...fclose(inFile); //使用fclose来关闭读取文件fclose(outFile);//使用fclose来关闭写入文件

3.对文件进行二分查找

下面是最简单的二分查找。

/** * search n in a[], return the index, if not find, return -1. */  template <class T>  int BSearch(T a[],const int& length,const int& n){      int left = 0, right = length - 1;      while(left <= right){          int middle = (left + right) / 2;          if(n < a[middle]){              right = middle - 1;          }else if(n > a[middle]){              left = middle + 1;          }else{              return middle;          }      }      return -1;  }  

我们这次要对文件进行二分查找,还好我们存的每条压缩记录大小都是一样的。假设我们要得到中间的值,我们就可以用fseek到中间记录,再用fread,读取到记录。

NumberInfoCompress infoMiddle;

int middle = (left + right) / 2;

fseek(file,sizeof(int) + middle * sizeof(NumberInfoCompress),SEEK_SET);
fread(&infoMiddle,sizeof(NumberInfoCompress),1,file);

原来的二分查找是比中间值小就去前半部分搜索,如果比中间值大就去后半部分搜索。这次我们记录的是一个区间,就是如果查找值比区间最小部分还要小的话,去前半部分搜索,如果查找值比区间最大值还要大的话,去后半部分搜索。其他情况就表示找到记录了。


// -------------------------------------------------------//  Name:         GetCityNameByNumber//  Description:  input the phone number, find the city in the binary file //  Arguments:    bFileName:the binary file name,number: the phone number//  Return Value: city name,not find return ""// -------------------------------------------------------char* GetCityNameByNumber(const char* bFileName,const int& number){        FILE* file = 0;file = fopen(bFileName,"rb");if(file == 0)return (char*)"";int phoneInfoCompressCount = 0;//get total phoneInfoCompress countfread(&phoneInfoCompressCount,sizeof(int),1,file);int left = 0, right = phoneInfoCompressCount - 1;NumberInfoCompress infoMiddle;//begin binary searchwhile(left <= right){int middle = (left + right) / 2;//put the write point in the  middle phoneInfoCompress fseek(file,sizeof(int) + middle * sizeof(NumberInfoCompress),SEEK_SET);fread(&infoMiddle,sizeof(NumberInfoCompress),1,file);if(number < infoMiddle.getBegin()){right = middle - 1;}else if(number > (infoMiddle.getBegin() + infoMiddle.getSkip())){left = middle + 1;}else{// find the resultreturn DoFindResultThing(file, phoneInfoCompressCount, infoMiddle);}}        fclose(file);return (char*)"";}char* DoFindResultThing( FILE* file,const int& phoneInfoCompressCount,const NumberInfoCompress &infoMiddle ) {//put the read point at the beginning of the citiesfseek(file,sizeof(int) + phoneInfoCompressCount*sizeof(NumberInfoCompress),SEEK_SET);int length = 0;unsigned short searchIndex = 0;//read every citywhile(!feof(file)){fread(&length,sizeof(int),1,file);char* location = new char[length];fread(location,length,1,file);if(searchIndex == infoMiddle.getCityIndex()){//find the city stringfclose(file);return location;}else{//keep reading the file               ++searchIndex;}}return (char*)"";}

4提高查询速度


查看上面的二分查找,速度其实非常快了,但是查询城市的时候却用了类似链表的方式,如果查询第400个城市,要从第0个城市开始读,一个个读,直到读到第400个城市。有没有什么快的方式?把所有城市按照一样的长度存储!这样我们就可以像在数组中查询城市一样,查询任何城市都只需要一次。可以把所有城市按照最长的城市长度存储。查了下最长的是"湖北江汉(天门/仙桃/潜江)",加上"\0"是25。



所有城市都按照最长城市存储可能会增大存储空间。试了下,文件从417KB涨到422KB,完全可以接受。就是后面如果有更长的城市名,记得要修改这个25魔鬼数字。



5.效果图



C++转换txt到二进制文件和查询都已经处理完毕,可以下载这个项目查看,用的是vs2005。下一步就是通过JNI整合到Android中。


http://www.waitingfy.com/?p=541


相关文章:

Android 号码, 来电归属地 Jni 使用C++对二进制文件查询(一) 理论篇

Android 号码, 来电归属地 Jni 使用C++对二进制文件查询(二) C++实现篇

Android 号码, 来电归属地 Jni 使用C++对二进制文件查询(三) APK 实现篇

项目下载:

CallHelper


更多相关文章

  1. 快应用 QuickApp:配置开发环境 和 第一个工程 HelloWorld
  2. android framework下载学习
  3. phoneGap 基于android 实例 一
  4. Android(安卓)操纵File查看文件大小,清理文件
  5. Android使用Handler实现下载文件功能
  6. ClipDrawable的使用
  7. Android(安卓)Animation学习(六) View Animation介绍
  8. Android动画效果translate、scale、alpha、rotate的 xml文件写法
  9. build/envsetup.sh简记Android

随机推荐

  1. Android(安卓)NFC 对sim卡读写注意事项
  2. Android虚拟机与Java虚拟机——两种虚拟
  3. android与html5的交互——数据库操作,UI操
  4. android app签名(debug和release)
  5. PHP基础 -(六)重载/命名空间/自动加载
  6. IntelliJ iDEA激活码 | 2022 IntelliJ iD
  7. mysql:1. 实例演示数据库的CURD操作 2.
  8. 数组函数实例演示
  9. 文件上传学习小结
  10. 文件包含和类与对象实例演示