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

相关文章

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

mysql查询使用_rowid虚拟列的示例

《mysql查询使用_rowid虚拟列的示例》MySQL中,_rowid是InnoDB虚拟列,用于无主键表的行ID查询,若存在主键或唯一列,则指向其,否则使用隐藏ID(不稳定),推荐使用ROW_NUM... 目录1. 基本查询(适用于没有主键的表)2. 检查表是否支持 _rowid3. 注意事项4. 最佳实

SpringSecurity显示用户账号已被锁定的原因及解决方案

《SpringSecurity显示用户账号已被锁定的原因及解决方案》SpringSecurity中用户账号被锁定问题源于UserDetails接口方法返回值错误,解决方案是修正isAccountNon... 目录SpringSecurity显示用户账号已被锁定的解决方案1.问题出现前的工作2.问题出现原因各

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局