电话归属地查询之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

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I

在MySQL执行UPDATE语句时遇到的错误1175的解决方案

《在MySQL执行UPDATE语句时遇到的错误1175的解决方案》MySQL安全更新模式(SafeUpdateMode)限制了UPDATE和DELETE操作,要求使用WHERE子句时必须基于主键或索引... mysql 中遇到的 Error Code: 1175 是由于启用了 安全更新模式(Safe Upd

Python安装时常见报错以及解决方案

《Python安装时常见报错以及解决方案》:本文主要介绍在安装Python、配置环境变量、使用pip以及运行Python脚本时常见的错误及其解决方案,文中介绍的非常详细,需要的朋友可以参考下... 目录一、安装 python 时常见报错及解决方案(一)安装包下载失败(二)权限不足二、配置环境变量时常见报错及

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Java下载文件中文文件名乱码的解决方案(文件名包含很多%)

《Java下载文件中文文件名乱码的解决方案(文件名包含很多%)》Java下载文件时,文件名中文乱码问题通常是由于编码不正确导致的,使用`URLEncoder.encode(filepath,UTF-8... 目录Java下载文件中文文件名乱码问题一般情况下,大家都是这样为了解决这个问题最终解决总结Java下

Idea实现接口的方法上无法添加@Override注解的解决方案

《Idea实现接口的方法上无法添加@Override注解的解决方案》文章介绍了在IDEA中实现接口方法时无法添加@Override注解的问题及其解决方法,主要步骤包括更改项目结构中的Languagel... 目录Idea实现接China编程口的方法上无法添加@javascriptOverride注解错误原因解决方

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

查询SQL Server数据库服务器IP地址的多种有效方法

《查询SQLServer数据库服务器IP地址的多种有效方法》作为数据库管理员或开发人员,了解如何查询SQLServer数据库服务器的IP地址是一项重要技能,本文将介绍几种简单而有效的方法,帮助你轻松... 目录使用T-SQL查询方法1:使用系统函数方法2:使用系统视图使用SQL Server Configu

MYSQL关联关系查询方式

《MYSQL关联关系查询方式》文章详细介绍了MySQL中如何使用内连接和左外连接进行表的关联查询,并展示了如何选择列和使用别名,文章还提供了一些关于查询优化的建议,并鼓励读者参考和支持脚本之家... 目录mysql关联关系查询关联关系查询这个查询做了以下几件事MySQL自关联查询总结MYSQL关联关系查询