改自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. Spring源码学习笔记2

    1.默认标签的解析 对四种不同标签的解析 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate dele ...

  2. jQuery-4.动画篇---上卷下拉效果

    jQuery中下拉动画slideDown 对于隐藏的元素,在将其显示出来的过程中,可以对其进行一些变化的动画效果.之前学过了show方法,show方法在显示的过程中也可以有动画,但是.show()方法 ...

  3. OO第三次博客总结

    一. 规格发展历史 从20世纪60年代开始,就存在着许多不同的形式规格说明语言和软件开发方法.在形式规格说明领域一些最主要的发展过程列举如下: 1969-1972 C.A.R Hoare撰写了&quo ...

  4. STM32的USART应用问题(不定时添加)

    ST应用的问题:串口一直在用.不检测会导致一直中断要规避. 规避范例: void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; u8 DataCount= ...

  5. Android Historian安装使用

    1.先安装docker 2.安装Historian,可使用如下docker镜像: sudo docker run -p 9000:9999 registry.cn-hangzhou.aliyuncs. ...

  6. BOM模型中常用对象 定义计数器 网页跳转 网页前进后退

    今天上午学了的BOM模型中常用对象,了解了一部分的属性 For循环的规律 外层循环控制行 内层循环控制列 <!doctype html> <html> <head> ...

  7. C++ Boost库分类总结

    c# 程序员写c++,各种不适应.尤其是被内存操作和几十种字符串类型的转换,简直疯了,大小写转换竟然要手动写代码实现. Boost看介绍不错,也不知道能不能跨平台.过几天要上linux写c++, 也不 ...

  8. 游戏编程算法与技巧 Game Programming Algorithms and Techniques (Sanjay Madhav 著)

    http://gamealgorithms.net 第1章 游戏编程概述 (已看) 第2章 2D图形 (已看) 第3章 游戏中的线性代数 (已看) 第4章 3D图形 (已看) 第5章 游戏输入 (已看 ...

  9. Docker切换国内镜像

    本人是Ubuntu系统 Ubuntu 18.04 安装 Docker-ce 1.更换国内软件源,推荐中国科技大学的源,稳定速度快(可选) sudo cp /etc/apt/sources.list / ...

  10. day02代码

    1.while.....else.....语句,break,continue,if,while,用户交互,输出,运算符 # 用户有三次登录机会 _username = 'tom' _password ...