改自HeDaode 2007-12-28的代码

将之改为从硬盘读取后文件后,将MemoryStream放到内存中,提高后续查询速度

    ///<summary>
/// 提供从纯真IP数据库搜索IP信息的方法;
///</summary>
public class IPSearch
{
FileStream fileStream = null;
static long[] ipArray = null;
static long initPosition = ;
long ip;
static MemoryStream ipFile =null;
///<summary>
/// 构造函数
///</summary>
public IPSearch()
{
if (ipArray == null)
{
lock ("ip")
{
if (ipArray == null)
{
fileStream = new FileStream("f:/files/qqwry.dat", FileMode.Open, FileAccess.Read, FileShare.Read);
ipArray = BlockToArray(ReadIPBlock());
initPosition = fileStream.Position;
fileStream.Position = ;
byte[] bytes = new byte[fileStream.Length];
fileStream.Read(bytes, , bytes.Length); ipFile = new MemoryStream(bytes);
fileStream.Close();
fileStream = null;
bytes = null;
}
}
}
ipFile.Position = initPosition;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);//使.net core支持gb2312编码
} /// <summary>
/// 更新ip数据文件后,对内存中的流进行更新
/// </summary>
public void ClearStream()
{
ipFile.Close();
ipFile = null;
ipArray = null;
initPosition = ;
} ///<summary>
/// 获取指定IP所在地理位置
///</summary>
///<param name="strIP">要查询的IP地址</param>
///<returns></returns>
public string GetIPLocation(string strIP)
{
ip = IPToLong(strIP); long offset = SearchIP(ipArray, , ipArray.Length - ) * + ;
ipFile.Position += offset;//跳过起始IP
ipFile.Position = ReadLongX() + ;//跳过结束IP //IPLocation loc = new IPLocation();
int flag = ipFile.ReadByte();//读取标志
if (flag == )//表示国家和地区被转向
{
ipFile.Position = ReadLongX();
flag = ipFile.ReadByte();//再读标志
}
//long countryOffset = ipFile.Position;
string country = ReadString(flag); //if (flag == 2)
//{
// ipFile.Position = countryOffset + 3;
//}
//flag = ipFile.ReadByte();
//loc.area = ReadString(flag); //ipFile.Close();
//ipFile = null;
if (!country.Contains("市"))
{
return null;
}
if (!country.Contains("省"))
{
return country;
}
return country.Split("省")[];
}
///<summary>
/// 将字符串形式的IP转换位long
///</summary>
///<param name="strIP"></param>
///<returns></returns>
public long IPToLong(string strIP)
{
byte[] ip_bytes = new byte[];
string[] strArr = strIP.Split(new char[] { '.' });
for (int i = ; i < ; i++)
{
ip_bytes[i] = byte.Parse(strArr[ - i]);
}
return BitConverter.ToInt64(ip_bytes, );
}
///<summary>
/// 将索引区字节块中的起始IP转换成Long数组
///</summary>
///<param name="ipBlock"></param>
long[] BlockToArray(byte[] ipBlock)
{
long[] ipArray = new long[ipBlock.Length / ];
int ipIndex = ;
byte[] temp = new byte[];
for (int i = ; i < ipBlock.Length; i += )
{
Array.Copy(ipBlock, i, temp, , );
ipArray[ipIndex] = BitConverter.ToInt64(temp, );
ipIndex++;
}
return ipArray;
}
///<summary>
/// 从IP数组中搜索指定IP并返回其索引
///</summary>
///<param name="ipArray">IP数组</param>
///<param name="start">指定搜索的起始位置</param>
///<param name="end">指定搜索的结束位置</param>
///<returns></returns>
int SearchIP(long[] ipArray, int start, int end)
{
int middle = (start + end) / ;
if (middle == start)
return middle;
else if (ip < ipArray[middle])
return SearchIP(ipArray, start, middle);
else
return SearchIP(ipArray, middle, end);
}
///<summary>
/// 读取IP文件中索引区块
///</summary>
///<returns></returns>
byte[] ReadIPBlock()
{
long startPosition = StreamReadLongX();
long endPosition = StreamReadLongX();
long count = (endPosition - startPosition) / + ;//总记录数
fileStream.Position = startPosition;
byte[] ipBlock = new byte[count * ];
fileStream.Read(ipBlock, , ipBlock.Length);
fileStream.Position = startPosition;
return ipBlock;
}
///<summary>
/// 从IP文件中读取指定字节并转换位long
///</summary>
///<param name="bytesCount">需要转换的字节数,主意不要超过8字节</param>
///<returns></returns>
long StreamReadLongX(int bytesCount)
{
byte[] _bytes = new byte[];
fileStream.Read(_bytes, , bytesCount);
return BitConverter.ToInt64(_bytes, );
} ///<summary>
/// 从IP文件中读取指定字节并转换位long
///</summary>
///<param name="bytesCount">需要转换的字节数,主意不要超过8字节</param>
///<returns></returns>
long ReadLongX(int bytesCount)
{
byte[] _bytes = new byte[];
ipFile.Read(_bytes, , bytesCount); return BitConverter.ToInt64(_bytes, );
}
///<summary>
/// 从IP文件中读取字符串
///</summary>
///<param name="flag">转向标志</param>
///<returns></returns>
string ReadString(int flag)
{
if (flag == || flag == )//转向标志
ipFile.Position = ReadLongX();
else
ipFile.Position -= ; List<byte> list = new List<byte>();
byte b = (byte)ipFile.ReadByte();
while (b > )
{
list.Add(b);
b = (byte)ipFile.ReadByte();
} return Encoding.GetEncoding("gb2312").GetString(list.ToArray());
}
}

测试:

        [HttpPost("pt")]
public IActionResult PayTest([FromQuery]string ip)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
IPSearch iPSearch = new IPSearch();
string result = iPSearch.GetIPLocation(ip);
stopwatch.Stop();
return Ok(new { stopwatch.Elapsed,result});
}

第一次运行稍慢,以测试机的配置需要25~50毫秒之间

之后每次运行均在1毫秒以内

优化读取纯真IP数据库QQWry.dat获取地区信息的更多相关文章

  1. 纯真IP数据库(qqwry.dat)转换成最新的IP数据库格式(ipwry.dat)

    纯真IP数据库(qqwry.dat)转换成最新的IP数据库格式(ipwry.dat) 转载自:http://blog.cafeboy.org/2011/02/25/qqwry-to-ipwry/ ht ...

  2. lib-qqwry v1.0 发布 nodejs解析纯真IP库(qqwry.dat)

    lib-qqwry是当初学习node时用来练手的一个模块,用来解析纯真IP库的 现在发一个v1.0版本弥补我当时稚嫩的代码. 意外收获是,整理代码后发现,相比v0.x版本 急速模式下的效率提升大概20 ...

  3. 读取纯真IP数据库

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <stdli ...

  4. PHP获取IP及地区信息(纯真IP数据库)

    昨天在写程序的时候,发现在用户的时候记录IP和地区信息也许以后用得上,去网上找了找,发现实现的方式有好多好多,因为我用的ThinkPHP,后来又去TP官网找了找,最后采用了下面这种方法. <?p ...

  5. PHP利用纯真IP数据库在本地实现IP地址信息查询

    https://blog.csdn.net/myweishanli/article/details/45098693 准备工作: 建议本地IP地址数据库,请到http://www.cz88.net/这 ...

  6. 纯真IP数据库格式详解

    纯真版IP数据库,优点是记录多,查询速度快,它只用一个文件QQWry.dat就包含了所有记录,方便嵌入到其他程序中,也方便升级.缺点是你想要编辑它却是比较麻烦的,由于其文件格式的限制,你要直接添加IP ...

  7. 纯真IP数据库格式详解 附demo

    纯真版IP数据库,优点是记录多,查询速度快,它只用一个文件QQWry.dat就包含了所有记录,方便嵌入到其他程序中,也方便升级.缺点是你想要编辑它却是比较麻烦的,由于其文件格式的限制,你要直接添加IP ...

  8. python3通过纯真IP数据库查询IP归属地信息

    在网上看到的别人写的python2的代码,修改成了python3. 把纯真IP数据库文件qqwry.dat放到czip.py同一目录下. #! /usr/bin/env python # -*- co ...

  9. PHP调用纯真IP数据库返回具体地址

    function convertip($ip) { $ip1num = 0; $ip2num = 0; $ipAddr1 =""; $ipAddr2 =""; ...

随机推荐

  1. sync.Pool的使用

    一定要搞明白sync.Pool的正确用法,避免出现以下问题: kline := this.pool.Get() defer this.pool.Put(kline) kline.UnMarshal(d ...

  2. EM 算法最好的解释

    https://wenku.baidu.com/view/fcb6a52bf5335a8102d220e3.html

  3. Fiddler 抓包的基本介绍

    一.什么是Fiddler? Fiddler是一种常见的抓包分析软件,我们可以使用它对HTTP请求进行分析,并模拟对应的HTTP请求. 二.Fiddler抓包实现的原理 Fiddler就相当于某二手车直 ...

  4. mysql技巧:按条件筛选,然后替换

    1.按条件筛选,然后替换 select * from phome_ecms_tv where playurl like '%关键词%';update phome_ecms_tv set myorder ...

  5. ztree模糊筛选展开选中节点

    树呢是一个最简单的树,并没有做一异步加载,也就是一个筛选,然后跳到第一个符合删选的数据下,并且所有符合的都会被展开和选中.其中ztreeAry是一个模拟的本地数组json.在test.json中,如果 ...

  6. jsp的四个作用域page、request、session、application

    1.简单说 page指当前页面.在一个jsp页面里有效 2.request 指从http请求到服务器处理结束,返回响应的整个过程.在这个过程中使用forward方式跳转多个jsp.在这些页面里你都可以 ...

  7. PHP数据库连接mysql与mysqli的区别与用法

    一.mysql与mysqli的概念相关: 1.mysql与mysqli都是php方面的函数集,与mysql数据库关联不大. 2.在php5版本之前,一般是用php的mysql函数去驱动mysql数据库 ...

  8. Ubuntu18.04下make menuconfig缺少ncurses库

    kent@hu:~/work/03-kernel/linux-4.15.1$ make menuconfig *** Unable to find the ncurses libraries or t ...

  9. Windows环境下C++中关于文件结束符的问题

    参考资料:http://www.cnblogs.com/day-dayup/p/3572374.html 一.前言 在不同的OS环境下,程序中对应的文件结束符有所不一样,根据<C++ Prime ...

  10. transmission跳过文件校验功能实现

    贴吧链接:https://tieba.baidu.com/p/4839039900 GitHub:https://github.com/superlukia/transmission-2.92_ski ...