电话归属地查询之Android解决方案

2024-03-31 08:38

本文主要是介绍电话归属地查询之Android解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.zhourunsheng.com

最近接触的一个项目中,其中有一个功能需求就是号码归属地的查询,乍一看确实挺简单,反正数据库也都有了,只不过就是查询一下而已嘛!到了实际程序设计的时候才发现,5M的数据库光要加载起来就得1分多钟,放在android手机上跑的太慢了,没办法,只好另辟蹊径了!!!

本文的基本思路如下:

1.  先把数据进行分组,即每一个地区一个组,例如

1898742 1898743 1898744 :云南曲靖

1894380 1894381 1894382 :吉林松原

2.  把电话号码进行排序,目的就是为了找到电话号码的区间,例如

1894815 --> 1899819 :广东珠海,

找到了一个区段,这样就不用把每个电话号码读存储下来, 只要存储一个区间就好,

这样可以大大节省存储空间

3. 设计新的存储格式,本文的程序采用如下方式存储

第一条电话记录在文件中的位置偏移 最后一条电话记录在文件中的位置偏移
电话归属地的字符串(例如:辽宁大连,湖北武汉,广东珠海,广东深圳..., 字符串之间以逗号分隔)
第一条电话记录 (例如:1894815{代表号码段起始值} 5{代表连续的号码个数} 2{代表该归属地字符串在所有归属地字符串中的偏移量})
第二条电话记录
...
最后一条电话记录

4. 归属地查询

根据用户输入的电话号码,利用二分查找法可快速定位到该记录在文件中的位置偏移,读出该记录中位置字符串的偏移值,进而查表即可找到归属地

程序设计,源码如下:

package com.carey.tel;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;public class JavaTelArea {private static JavaTelArea jta = null;private static final String INDEXDATAFILE = "tel.bin";private static Hearder cacheHearder = null;private static AreaCode cacheAreaCode = null;private static String cacheIndexFilePath = null;public static void main(String[] args) {JavaTelArea ctc = JavaTelArea.getInstance();//ctc.genIndexFile("e:\", "e:\telphone.txt");String indexPath = "e:\";long startTime = new Date().getTime();String searchNum = "13889650920";String res = ctc.searchTel(indexPath, searchNum);System.out.println(searchNum + " : " + res);System.out.println(System.currentTimeMillis() - startTime + "ms");startTime = new Date().getTime();searchNum = "+8613659867583";res = ctc.searchTel(indexPath, searchNum);System.out.println(searchNum + " : " + res);System.out.println(System.currentTimeMillis() - startTime + "ms");startTime = new Date().getTime();searchNum = "1301815";res = ctc.searchTel(indexPath, searchNum);System.out.println(searchNum + " : " + res);System.out.println(System.currentTimeMillis() - startTime + "ms");startTime = new Date().getTime();searchNum = "1301816";res = ctc.searchTel(indexPath, searchNum);System.out.println("没有预测");System.out.println(searchNum + " : " + res);System.out.println(System.currentTimeMillis() - startTime + "ms");startTime = new Date().getTime();searchNum = "1301816";res = ctc.searchTel(indexPath, searchNum, true);System.out.println("根据号码连贯性原理预测");System.out.println(searchNum + " : " + res);System.out.println(System.currentTimeMillis() - startTime + "ms");startTime = new Date().getTime();searchNum = "1301817";res = ctc.searchTel(indexPath, searchNum);System.out.println(searchNum + " : " + res);System.out.println(System.currentTimeMillis() - startTime + "ms");}private HashMap<Long, String> generateTestData() {HashMap<Long, String>telToAreaCode = new HashMap<Long, String>();telToAreaCode.put(1310944l, "新疆伊犁州");telToAreaCode.put(1301263l, "新疆伊犁州");telToAreaCode.put(1301264l, "新疆伊犁州");telToAreaCode.put(1301260l, "新疆伊犁州");telToAreaCode.put(955L, "海南");telToAreaCode.put(1320955l, "海南");telToAreaCode.put(1320957l, "海南");telToAreaCode.put(1300561L, "陕西商州");telToAreaCode.put(1300562L, "陕西商州");return telToAreaCode;}public static synchronized JavaTelArea getInstance() {if (jta == null) {jta = new JavaTelArea();}return jta;}/**	 * Generate Index File (tel.bin)	 * */private void genIndexFile(String indexFilePath, String souceFile) {ArrayList<String> strs = readFileToList(souceFile);HashMap<Long, String>telToArea = createTel2AreaHashMap(strs);writeDate(indexFilePath + INDEXDATAFILE, telToArea);}/**	 * read file content to String array list, every line one string.	 * */private ArrayList<String> readFileToList(String filePath) {final ArrayList<String>strLists = new ArrayList<String>();BufferedReader bReader = null;try {bReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));String str = bReader.readLine();while (str != null) {strLists.add(str);str = bReader.readLine();}} catch (Exception e) {e.printStackTrace();} finally {if (bReader != null) {try {bReader.close();} catch (IOException e) {e.printStackTrace();}}}return strLists;}/**	 * create telephone number to area hash map.	 * */HashMap<Long, String> createTel2AreaHashMap(ArrayList<String> strs) {final HashMap<Long, String>telToArea = new HashMap<Long, String>();String[] tels = null;int len = 0;String strArea = null;for (String string : strs) {tels = string.split(" ");len = tels.length;strArea = tels[len - 1].substring(1);for (int i = 0; i < len - 1; i++) {telToArea.put(Long.valueOf(tels[i]), strArea);}}return telToArea;}/**	 * combined the adjacent Records.	 * */private void combinedRecords(ArrayList<Record> records, Record newRecord) {int size = records.size();if (size > 0&& records.get(size - 1).areacodeIndex == newRecord.areacodeIndex) {// combinedRecord lastRecord = records.get(size - 1);lastRecord.numCnt = (int) (newRecord.baseTelNum - lastRecord.baseTelNum) + newRecord.numCnt;} else {records.add(newRecord);}}/**	 * write index info to file.	 * */private void writeDate(String filePath, HashMap<Long, String> telToAreaCode) {// 1. get all area infoArrayList<String> tmpAreaCodes = new ArrayList<String>(telToAreaCode.values());ArrayList<String> strAreaCodes = new ArrayList<String>();for (String str : tmpAreaCodes) {if (!strAreaCodes.contains(str)) {strAreaCodes.add(str);}}tmpAreaCodes.clear();tmpAreaCodes = null;StringBuffer sb = new StringBuffer();for (String str : strAreaCodes) {sb.append(str + ",");}sb.deleteCharAt(sb.length() - 1);AreaCode areaCode = new AreaCode(sb.toString());areaCode.print();// 2. Sort HashMap and combined the adjacent telephone numberArrayList<Long> telNunms = new ArrayList<Long>(telToAreaCode.keySet());Collections.sort(telNunms);ArrayList<Record> records = new ArrayList<Record>();long baseNum = 0;String baseArea = null;int numCnt = 0;for (Long tm : telNunms) {if (numCnt == 0) {baseNum = tm;baseArea = telToAreaCode.get(tm);numCnt = 1;} else {if (tm == baseNum + numCnt && baseArea.equals(telToAreaCode.get(tm))) {numCnt++;} else {combinedRecords(records, new Record(baseNum, numCnt,	strAreaCodes.indexOf(baseArea)));baseNum = tm;baseArea = telToAreaCode.get(tm);numCnt = 1;}}}combinedRecords(records,	new Record(baseNum, numCnt, strAreaCodes.indexOf(baseArea)));		// for (Record record : records) {// record.print();// }// 3. Write data to the fileRandomAccessFile raf = null;try {raf = new RandomAccessFile(filePath, "rw");raf.seek(0);Hearder hearder = new Hearder();hearder.firstRecordOffset = hearder.Size() + areaCode.Size();hearder.lastRecordOffset = hearder.firstRecordOffset	+ (records.size() - 1) * records.get(0).Size();hearder.print();hearder.write(raf);areaCode.write(raf);for (Record record : records) {record.write(raf);}} catch (Exception e) {e.printStackTrace();} finally {if (raf != null) {try {raf.close();} catch (IOException e) {e.printStackTrace();}}}}class Hearder {int firstRecordOffset;int lastRecordOffset;public int Size() {return (Integer.SIZE + Integer.SIZE) / Byte.SIZE;}public void write(RandomAccessFile raf) throws IOException {raf.writeInt(this.firstRecordOffset);raf.writeInt(this.lastRecordOffset);}public void read(RandomAccessFile raf) throws IOException {this.firstRecordOffset = raf.readInt();this.lastRecordOffset = raf.readInt();}public void print() {System.out.println("===== Hearder ===== ");System.out.println("[" + firstRecordOffset + " , "	 + lastRecordOffset + "]");}}class AreaCode {private String areacode;private String[] codes;public AreaCode() {this("");}public AreaCode(String areacode) {this.areacode = areacode;this.codes = null;}public int Size() {return areacode.getBytes().length + (Integer.SIZE / Byte.SIZE);}public void print() {System.out.println("===== AreaCode ===== ");System.out.println("[" + areacode.getBytes().length + "]" + areacode);}public void write(RandomAccessFile raf) throws IOException {raf.writeInt(areacode.getBytes().length);raf.write(this.areacode.getBytes());}public void read(RandomAccessFile raf) throws IOException {byte[] bytes = new byte[raf.readInt()];raf.read(bytes);this.areacode = new String(bytes);}public String getCodeByIndex(int index) {if (this.codes == null) {this.codes = this.areacode.split(",");}return (index < 0 || this.codes == null || index >= this.codes.length) ? null	 : this.codes[index];}
}class Record {long baseTelNum;int numCnt;int areacodeIndex;public Record() {this(0, 0, 0);}public Record(long baseTelNum, int numCnt, int areacodeIndex) {this.baseTelNum = baseTelNum;this.numCnt = numCnt;this.areacodeIndex = areacodeIndex;}public void print() {System.out.println("===== Record ===== ");System.out.println("<" + baseTelNum + "> <" + numCnt + "> <" + areacodeIndex + ">");}public int Size() {return (Long.SIZE + Integer.SIZE) / Byte.SIZE;}public void write(RandomAccessFile raf) throws IOException {raf.writeLong(this.baseTelNum);int tmp = this.numCnt << 16;tmp += 0xFFFF & this.areacodeIndex;raf.writeInt(tmp);}public void read(RandomAccessFile raf) throws IOException {this.baseTelNum = raf.readLong();int tmp = raf.readInt();this.numCnt = tmp >> 16;this.areacodeIndex = 0xFFFF & tmp;}public int inWhich(long telNum) {if (telNum < this.baseTelNum) {return -1;} else if (telNum >= this.baseTelNum + this.numCnt) {return 1;} else {return 0;}}}public String searchTel(String indexFilePath, String telNum) {return searchTel(indexFilePath, telNum, false);}/**	 * search	 * */public String searchTel(String indexFilePath, String telNum, boolean forecast) {StringBuffer sb = new StringBuffer(telNum);// +if (sb.charAt(0) == '+') {sb.deleteCharAt(0);}// 86if (sb.charAt(0) == '8' && sb.charAt(1) == '6') {sb.delete(0, 2);}// 以0开头,是区号if (sb.charAt(0) == '0') {sb.deleteCharAt(0);// 首先按照4位区号查询,若查询为空,再按3位区号查询if (sb.length() >= 3) {sb.delete(3, sb.length());} String dial = searchTel(indexFilePath, Long.valueOf(sb.toString()),false);if (dial != null) {return dial;}if (sb.length() >= 2) {sb.delete(2, sb.length());}}// 以1开头,是手机号或者服务行业号码else if (sb.charAt(0) == '1') {// 首先按照手机号码查询,若查询为空,再按特殊号码查询if (sb.length() > 7) {String dial = searchTel(indexFilePath, Long.valueOf(sb.substring(0, 8)),false);if (dial != null) {return dial;}dial = searchTel(indexFilePath, Long.valueOf(sb.toString()),false);if (dial != null) {return dial;}// 只需要保留7位号码就ok了,多余的删掉if (sb.length() > 7) {sb.delete(7, sb.length());}} else {//小于7位,最有可能是服务号码//do nothing.}}// 以其他数字开头,这也不知道是啥号码了else {//do nothing.}return searchTel(indexFilePath, Long.valueOf(sb.toString()), forecast);}private String searchTel(String indexFilePath, long telNum, boolean forecast) {RandomAccessFile raf = null;try {raf = new RandomAccessFile(indexFilePath + INDEXDATAFILE, "r");if (cacheIndexFilePath == null || !cacheIndexFilePath.equals(indexFilePath)) {cacheIndexFilePath = indexFilePath;cacheHearder = new Hearder();cacheHearder.read(raf);cacheHearder.print();cacheAreaCode = new AreaCode();cacheAreaCode.read(raf);cacheAreaCode.print();}int index = lookUP(raf, cacheHearder.firstRecordOffset, cacheHearder.lastRecordOffset, telNum, forecast);return cacheAreaCode.getCodeByIndex(index);} catch (Exception e) {e.printStackTrace();} finally {if (raf != null) {try {raf.close();} catch (IOException e) {e.printStackTrace();}}}return null;}private int lookUP(RandomAccessFile raf, long startpos, long endpos, long looknum, boolean forecast) throws IOException {Record record = new Record();long seekpos = 0;do {seekpos = startpos + (endpos - startpos) / record.Size() / 2 * record.Size();raf.seek(seekpos);record.read(raf);if (record.inWhich(looknum) > 0) {startpos = seekpos + record.Size();} else if (record.inWhich(looknum) < 0) {endpos = seekpos - record.Size();} else {return record.areacodeIndex;}} while (startpos <= endpos);if (forecast) {return record.areacodeIndex;} else {return -1;}}
}

程序运行情况如下:

==== Hearder =====
[4554 , 605622]
===== AreaCode ===== 
[4542]北福建南平,福建三明,海果洛,青海海南,...
13889650920 : 辽宁大连
20ms
+8613659867583 : 湖北武汉
2ms
1301815 : 四川泸州
2ms
没有预测1301816 : null
2ms
根据号码连贯性原理预测1301816 : 四川泸州
1ms
1301817 : 四川宜宾
2ms

可以看到,除了第一次查询的时候要加载索引文件大约耗时20ms,以后的查询基本都在1ms,速度非常快了!!!

本程序的测试data文件下载telinfo


这篇关于电话归属地查询之Android解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863902

相关文章

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

ural 1026. Questions and Answers 查询

1026. Questions and Answers Time limit: 2.0 second Memory limit: 64 MB Background The database of the Pentagon contains a top-secret information. We don’t know what the information is — you

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Mybatis中的like查询

<if test="templateName != null and templateName != ''">AND template_name LIKE CONCAT('%',#{templateName,jdbcType=VARCHAR},'%')</if>