C# 号码归属地查询算法(根据Android来电归属地二进制文件查询修改)

前言

近期有个项目需要用到号码归属查询,归属地数据库可能比不上ip138,淘宝上也有卖的-,-! 文本提供一个279188条记录并压缩成562KB的归属地数据。
我在互联网上搜索了相关文章,要不是数据库查询或者是访问网上的api,到底有没有更好的方式,我想各大手机软件的归属地都是属于本地查询的。
当我发现了Android Jni 使用C++对二进制文件查询 这篇文章,发现效率真是高,作者的算法也相当出色。
于是直接把它用C#来实现了一个版本,并且加上号码的类型,效率上没相差太多,起码我们的项目已经够用了。


这是原文的一段话:

随便去网络上搜索一个号码归属地数据库下载,你可能会找到各种格式,access,txt,db等。除了用insert sql语句外,你还可以用CSV文件格式来互相转换。因为SQLite Expert 支持CSV文件导入,导出。
数据最佳存放方式如上图中的表1CallerLoc和表2LocationInfo。这样用一条连表sql语句查询即可。类似这样的sql语句:select number, area from CallerLoc join LocationInfo on CallerLoc.location = LocationInfo.location。
假设你有了这样的xx.db文件,可以把该文件放在Android项目的assets文件下,然后在自定义的ContentProvider中的query方法中,尝试把xx.db 复制到手机的/data/data/你的项目包名/databases中,查询用上面提到的sql语句就行了。
这是一个解决方案,但是db文件太大了,280,000条记录差不多有8MB大小。 别人解压你的apk,dat文件一下子就被别人窃取走了。
有什么方式可以解决这个问题?分析表1,感觉数据还可以压缩(用自定义的格式),把数据写入到一个文件中,通过打开文件来搜索,写入方式用二进制的话,别人就窃取不了了。Java处理速度慢的话,还可以改用C++,通过JNI桥梁来处理。

相关技术和理论请参考原作者地址:

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

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

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

提供本文所修改过的源代码下载

简单说下修改过的类库

areacode.dat(562KB)
内嵌的资源文件,此文件是根据areacode.txt(9,522KB)生成而来。(279188条数据)

NumberInfoCompress

号码压缩的结构体,和原文C++版本的基本一致,只是增加了号码类型的储存;(占用8个字节)

PhoneInfo

号码的结构信息,分别有号码段、地区、类型。

PhoneWriter

压缩号码归属地并生成二进制文件。

public void DoWriter(Stream stream, Encoding encoding) {
if (_data == null || _data.Count == 0)
return; BinaryWriter bw = new BinaryWriter(stream, encoding); //设置偏移量在开头预留写入NumberInfoCompress的总数
this.WriteCount(bw, 0, _phoneInfoCompressCount); //设置偏移量在开头预留号码类型的总数
this.WriteCount(bw, 4, 0); //先读取第一条号码数据
var enumerator = this._data.GetEnumerator(); if (!enumerator.MoveNext())
return; //为什么要预先读取一条数据呢?获取第一条数据是为了和下一条进行对比
var phoneInfo = enumerator.Current; //增加城市信息,并且返回集合所在索引位置
var cityIdx = this.AddCity(phoneInfo.City);
//增加号码类型信息,并且返回集合所在索引位置
var cardIdx = this.AddCard(phoneInfo.CardType); //构造一个8字节存储的结构体
var pre = new NumberInfoCompress(phoneInfo.Code, 0, cityIdx, cardIdx); while (enumerator.MoveNext()) {
//读取下一条数据,准备和上一条比较
phoneInfo = enumerator.Current;
cityIdx = this.AddCity(phoneInfo.City);
cardIdx = this.AddCard(phoneInfo.CardType); //和上个号码对比是否连续的,比如 1370875 1370876 1370877。
//1370875开头有3个,表示13708 375:从75开始有3个连续的号码
if (phoneInfo.Code - (pre.GetBegin() + pre.GetSkip()) == 1 && cityIdx == pre.GetCityIndex()) {
//设置号码段连续位置
pre.SetSkip((ushort)(phoneInfo.Code - pre.GetBegin()));
} else {
//递增一个
++_phoneInfoCompressCount; //写入13708号码段的数据
this.Write(bw, pre); //继续构造一个8字节存储的结构体等待下次循环比较
pre = new NumberInfoCompress(phoneInfo.Code, 0, cityIdx, cardIdx);
}
} //写入最后的号码数据
this.Write(bw, pre);
++_phoneInfoCompressCount;//记录总数 //写入NumberInfoCompress的总数
this.WriteCount(bw, 0, _phoneInfoCompressCount); //写入号码类型的总数
this.WriteCount(bw, 4, (uint)(_listCard.Count)); //结尾写入城市地区数据
this.WriteCity(bw, encoding); //结尾写入号码类型数据
this.WriteCard(bw, encoding); bw.Close();
bw.Dispose();
}

PhoneReader

用来读取areacode.dat,比如查询号码归属地。

public PhoneInfo GetPhoneInfo(Stream stream, Encoding encoding, int number) {
PhoneInfo result = new PhoneInfo(); result.Code = number; BinaryReader br = new BinaryReader(stream, encoding); //获取索引总数
int phoneInfoCompressCount = br.ReadInt32();
//号码类型总数
int cardCount = br.ReadInt32();
int left = 0, right = phoneInfoCompressCount - 1; var per = new NumberInfoCompress();
var perSize = Marshal.SizeOf(per); //使用折半查询(二分法)
while (left <= right) {
//折半
int middle = (left + right) / 2;
//索引总数8字节 + middle * NumberInfoCompress字节数
stream.Position = sizeof(int) * 2 + middle * perSize; //读取NumberInfoCompress数据
per.Before = br.ReadUInt16();
per.After = br.ReadUInt16();
per.CityIndex = br.ReadUInt16();
per.CardIndex = br.ReadUInt16(); //判断号码是否匹配
if (number < per.GetBegin()) {
right = middle - 1;//在左半区间找
} else if (number > (per.GetBegin() + per.GetSkip())) {
left = middle + 1;//在右半区间找
} else {
//已找到,直接查询城市和号码类型
result.City = DoFindCityThing(br, phoneInfoCompressCount, per);
result.CardType = DoFindCardThing(br, cardCount, per);
return result;
}
}
br.Close();
br.Dispose();
return result;
} private string DoFindCityThing(BinaryReader br, int phoneInfoCompressCount, NumberInfoCompress infoMiddle) {
//计算城市区域信息位置
//sizeof(int) * 2 开头位置储存了一个4字节的NumberInfoCompress总数和类型总数
//phoneInfoCompressCount NumberInfoCompress总数
//Marshal.SizeOf(infoMiddle) NumberInfoCompress占用空间
//infoMiddle.GetCityIndex() 城市的所在位置
//_maxCityLength 城市总数
//偏移量 = 索引总数8字节 + 索引总数 * NumberInfoCompress字节数 + 城市的所在位置 * 城市大小
long totalOffset = sizeof(int) * 2 + phoneInfoCompressCount * Marshal.SizeOf(infoMiddle)
+ infoMiddle.GetCityIndex() * this._maxCityLength; br.BaseStream.Position = totalOffset;//设置偏移量
char[] charCity = br.ReadChars(this._maxCityLength);
return new string(charCity, 0, Array.IndexOf(charCity, '\0'));
} private string DoFindCardThing(BinaryReader br, int cardCount, NumberInfoCompress infoMiddle) {
//号码类型存储在尾端
//所以偏移量 = (流的总长度 - 类型总数 * 类型大小) + 所在位置 * 类型大小
long totalOffset = (br.BaseStream.Length - cardCount * this._maxCardLength) + infoMiddle.GetCardIndex() * this._maxCardLength; br.BaseStream.Position = totalOffset;//设置偏移量
char[] charCard = br.ReadChars(this._maxCardLength);
return new string(charCard, 0, Array.IndexOf(charCard, '\0'));
}

AreaCode

封装了手机归属地查询函数。

FrmAreaCode

用来演示如何查询电话号码归属地以及把文本文件生成为压缩过的二进制文件(areacode.dat)。

结语

原作者的压缩算法我们也可以稍作改变,但是用这种算法的前提条件是必须有序且有规律,最后用二分法才会提高查询速度。
项目资源里面的文本文件是每行一个号码段,如:号码,区域,类型;读者可以自行存储到任何数据库等地方,方便日后管理。

作者:JohnWu
出处:http://www.cnblogs.com/johnwu/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接
如有问题,可以通过v.la@Live.cn 联系我,非常感谢。

 
分类: C#
标签: C#手机归属地

C# 号码归属地查询算法的更多相关文章

  1. C# 号码归属地查询算法(根据Android来电归属地二进制文件查询修改)

    前言 近期有个项目需要用到号码归属查询,归属地数据库可能比不上ip138,淘宝上也有卖的-,-! 文本提供一个279188条记录并压缩成562KB的归属地数据.我在互联网上搜索了相关文章,要不是数据库 ...

  2. [android] 手机卫士号码归属地查询

    使用小米号码归属地数据库,有两张表data1和data2 先查询data1表,把手机号码截取前7位 select outkey from data1 where id=”前七位手机号” 再查询data ...

  3. WebService学习------小实例开发(号码归属地查询)

    1.WebService简介: WebService是一种跨平台,跨语言的,可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术.通过SOAP在Web上提 ...

  4. [android] 手机卫士号码归属地查询完成

    正则表达式完成号码验证, ^以某开头,[] 字符集(匹配中括号里面的任意字符),\d是任意一个数字,{n}表示出现了多少次,$结尾 手机号的正则 ^1[34568]\d{9}$,以1开头,第二个数字是 ...

  5. 本地的手机号码归属地查询-oracle数据

    最近做的项目中,有个功能是手机归属地查询,因为项目要在内网下运行,所以不能用提供的webservice,只好在网上找手机归属地的数据,很多都是access的,我们的项目是用oracle,只好自己转吧, ...

  6. 百度手机号码归属地查询api与返回json处理

    前天无意间在网上看到百度ApiStore,然后好奇就进去看了看.正好最近在某博培训Android,刚学到java基础.抱着锻炼的心态选择手机号码归属地查询api进行练手.api地址 (http://a ...

  7. android134 360 07 归属地查询

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  8. 手机归属地查询-IP地址查询-身份证查询-域名备案查询--Api接口

    使用这些接口是需要密钥的 公共密钥 appkey: 10003  secret: d1149a30182aa2088ef645309ea193bf  生成后sign: b59bc3ef6191eb9f ...

  9. C# Winform实现手机号码归属地查询工具

    摘要:本文介绍使用C#开发基于Winform的手机号码归属地查询工具,并提供详细的示例代码供参考. 一.需求描述 输入正确的手机号码,查询该号码的归属地和其他相关信息. 二.需求分析 1.实现手机号码 ...

随机推荐

  1. JS编程

    JS编程常识   一.UI层的松耦合 松耦合就是要求各层遵循“最少知识原则”,或者说是各层各司其职,不要越权: HTML:结构层 CSS:表现层 JS:行为层 对于各层的职能,有一句比较贴切的解释:H ...

  2. 【高德地图API】如何获得行政区域?如何制作行政规划图?

    原文:[高德地图API]如何获得行政区域?如何制作行政规划图? 什么是行政规划图?如何获得每个行政区域的边界轮廓图?举例:重庆市 江北区.如图: 官方类参考:http://developer.amap ...

  3. java学习笔记2——Eclipse的安装及汉化图解

    Eclipse的安装 有了JDK,你可以编译Java源码,运行Java程序,但是还没有代码编辑器,没有版本管理工具,也不能方便的管理工程文件,不能与团队协作.安装Eclipse,你才能完成这些工作. ...

  4. 网站静态化处理—web前端优化—中(12)

    网站静态化处理—web前端优化—中(12) Web前端很多优化原则都是从如何提升网络通讯效率的角度提出的,但是这些原则使用的时候还是有很多陷阱在里面,如果我们不能深入理解这些优化原则背后所隐藏的技术原 ...

  5. vi使用高级

    下面的命令在vi命令模式被激活 h 光标左移一个字符 l 光标向右移动一个字符 j 光标下移一行 k 移动光标线 一.对整行操作 1.复制光标所在行 yy 2.删除光标所在行 dd 3.选中光标所在行 ...

  6. 基于HTTP/2和protobuf的RPC框架:GRPC

    谷歌发布的首款基于HTTP/2和protobuf的RPC框架:GRPC Google 刚刚开源了grpc,  一个基于HTTP2 和 Protobuf 的高性能.开源.通用的RPC框架.Protobu ...

  7. NotePad++ for PHP

    原文:NotePad++ for PHP 一.安装设置 1.首先根据你的系统下载相应的安装文件.http://notepad-plus-plus.org/ Notepad++插件:http://sou ...

  8. Android Bluetooth Stack: Bluedroid(五岁以下儿童):The analysis of A2DP Source

    1. A2DP Introduction The Advanced Audio Distribution Profile (A2DP) defines the protocols and proced ...

  9. 日积月累系列之国籍控件(js源码)

    一直苦于没有好的国籍控件可以用,于是抽空写了一个国籍控件,现分享给大家. 主要功能和界面介绍 国籍控件主要支持中文.英文过滤以及键盘上下事件. 源码介绍 国籍控件核心是两个文件,navtionalit ...

  10. Google Summer of Code 建议被接收的5个技巧

    Google Summer of Code 建议被接收的5个技巧 本文翻译自:http://www.di.ens.fr/~baghdadi/TXT_blog/5_advices_to_get_your ...