QQWry.dat 数据写入
来源:互联网 发布:万方数据库下载器 编辑:程序博客网 时间:2024/06/03 21:14
纯真IP库 数据多,更新及时,很多同学在用,网上关于其读取的帖子也有不少(当然其中有一些是有BUG的),但却很少有关于其写入的帖子。OK,下面分享下写QQWry.dat。
QQWry.dat 分三个部分 :文件头,记录区,索引区。
一:首先写文件头,文件头的内容只有8个字节,首四个字节是第一条索引的绝对偏移,后四个字节是最后一条索引的绝对偏移。但是一开始我们还不知道这两个偏移量,那么就先随便写点啥,占个位吧,等后面索引写完了再回来修改。
二:写记录区
记录区的写入最复杂,分析QQwry.dat的数据我们可以很容易发现,一个“国家”下面有N多个“地址”,一个“地址”下面有N多个IP段。很明显这个有三个对象,并且层级关系。
记录区的数据格式不定,数据主要有以下类型:
A:结束IP(4个字节)
B:国家记录 (以0x 00结束,不定长 )
C: 地区记录 (以0x 00结束 ,不定长)
D:重定向模式标记(1或者2,1个字节)
E:绝对偏移量(3个字节)
每条记录的组成结构可能有三种情况:
第一种(最简单): [结束IP][国家]0[地址]0
使用场景:在该记录所对应的“国家”和“地址”之前都没有写入时。
读取时,找到这条记录的位置依次读下去就OK了
第二种:[结束IP]1[绝对偏移量]
使用场景:在该记录所对应的“国家”和“地址”之前都已经写入过。
读取时根据绝对偏移量跳转到指定位置,(可能会跳转两次,因为可能跳到的下一个位置是重定向模式2)然后就和第一种情况类似。
第三种:[结束IP]2[绝对偏移量][地址]0
使用场景:在该记录之前“国家”已经出现,但“地址”没有出现过。
读取时先按指定偏移跳转到指定位置读取到“国家”,然后读取该记录本身后面的“地址”
其就是为了使字符串尽量重用,同一个国家名称只写入一次,同一个地址名称也只写入一次
三:写索引区
索引区的结构:[起始IP][绝对偏移量]
其实在写记录区的时候需要把每条IP记录的绝对偏移量记下来。
写入索引时需要记录第一条索引和最后一条索引的绝对偏移量,用来更新前面提到的文件头。
写索引时必须将IP从小到大顺序写入,或者说我们提供数据源是按照IP从小到大的顺序排列的,这样我们在查找时才能使用二分法查找。
下面看一个完整的代码段:
public class IPEntity{ public string StartIP { get; set; } public string EndIP { get; set; } public long Offset { get; set; }}public class AdressEntity{ public AdressEntity() { IPS = new List<IPEntity>(); } public string Address { get; set; } public List<IPEntity> IPS { get; set; }}public class CountryEntity{ public CountryEntity() { Addrs = new List<AdressEntity>(); } public string Country { get; set; } public List<AdressEntity> Addrs { get; set; }}public class QQWryWriter{ /// <summary> /// 写文件 /// </summary> /// <param name="data">数据源,这个要事先准备好</param> public void Write(List<CountryEntity> data) { string path = HttpContext.Current.Server.MapPath("~/App_Data/QQWry.dat"); using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite)) { #region 写文件头 //文件头,8个字节。暂时都为0,先占个位,预留着,等索引全部写好了,再回过头来修改。 long firstIPIndexOffset = 0; //第一条索引的绝对偏移量 long lastIPIndexOffset = 0; //最后一条索引的绝对偏移量 byte[] head = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; fs.Write(head, 0, head.Length); #endregion #region 写记录区 //每条记录的组成结构有三种:[结束IP][重定向模式标记][国家][地址] //第一种(最简单): [结束IP][国家]0[地址]0 //第二种: [结束IP]1[绝对偏移量] //第三种: [结束IP]2[绝对偏移量][地址]0 //其就是为了使字符串尽量重用,同一个国家名称只写入一次,同一个地址名称也只写入一次 for (int i = 0; i < data.Count; i++) { string country = data[i].Country;//国家名 for (int j = 0; j < data[i].Addrs.Count; j++) { string addr = data[i].Addrs[j].Address;//地址名 for (int k = 0; k < data[i].Addrs[j].IPS.Count; k++) { var ipEntity = data[i].Addrs[j].IPS[k]; //记下IP记录的绝对偏移,方便后面索引区的写入 ipEntity.Offset = fs.Position; //写结束IP long intEndIP = IpToInt(ipEntity.EndIP); byte[] byEndIP = BitConverter.GetBytes(intEndIP); fs.Write(byEndIP, 0, 4); //重定向模式1 if (k > 0) { fs.WriteByte(1); byte[] byOffset = BitConverter.GetBytes((data[i].Addrs[j].IPS[0].Offset + 4)); fs.Write(byOffset, 0, 3); } else { //第一条记录肯定是最简单的,直接以第一种结构写入。 if (j == 0) { //写国家 byte[] byCountry = System.Text.Encoding.GetEncoding("GB2312").GetBytes(country); fs.Write(byCountry, 0, byCountry.Length); fs.WriteByte(0); //写地址 byte[] byAdress = System.Text.Encoding.GetEncoding("GB2312").GetBytes(addr); fs.Write(byAdress, 0, byAdress.Length); fs.WriteByte(0); } //重定向模式2 else { fs.WriteByte(2); byte[] byOffset = BitConverter.GetBytes((data[i].Addrs[0].IPS[0].Offset + 4)); fs.Write(byOffset, 0, 3); //写地址 byte[] byAdress = System.Text.Encoding.GetEncoding("GB2312").GetBytes(addr); fs.Write(byAdress, 0, byAdress.Length); fs.WriteByte(0); } } } } } #endregion #region 写索引区 for (int i = 0; i < data.Count; i++) { for (int j = 0; j < data[i].Addrs.Count; j++) { for (int k = 0; k < data[i].Addrs[j].IPS.Count; k++) { var ipEntity = data[i].Addrs[j].IPS[k]; long intStartIP = IpToInt(ipEntity.StartIP); if (i == 0 && j == 0 && k == 0) { firstIPIndexOffset = fs.Position; } if (i == data.Count - 1 && j == data[i].Addrs.Count - 1 && k == data[i].Addrs[j].IPS.Count - 1) { lastIPIndexOffset = fs.Position; } byte[] byStartIP = BitConverter.GetBytes(intStartIP); fs.Write(byStartIP, 0, 4); byte[] byOffset = BitConverter.GetBytes(ipEntity.Offset); fs.Write(byOffset, 0, 3); } } } #endregion #region 更新文件头 fs.Position = 0; byte[] byFirstIPIndexOffset = BitConverter.GetBytes(firstIPIndexOffset); fs.Write(byFirstIPIndexOffset, 0, 4); fs.Position = 4; byte[] bylastIPIndexOffset = BitConverter.GetBytes(lastIPIndexOffset); fs.Write(bylastIPIndexOffset, 0, 4); #endregion fs.Flush(); fs.Close(); } } /// <summary> /// IP地址转换成Int数据 /// </summary> /// <param name="ip"></param> /// <returns></returns> private long IpToInt(string ip) { char[] dot = new char[] { '.' }; string[] ipArr = ip.Split(dot); if (ipArr.Length == 3) ip = ip + ".0"; ipArr = ip.Split(dot); long ip_Int = 0; long p1 = long.Parse(ipArr[0]) * 256 * 256 * 256; long p2 = long.Parse(ipArr[1]) * 256 * 256; long p3 = long.Parse(ipArr[2]) * 256; long p4 = long.Parse(ipArr[3]); ip_Int = p1 + p2 + p3 + p4; return ip_Int; }}
- QQWry.dat 数据写入
- QQWry.dat 数据写入
- 关于QQWry.dat格式
- asp读取QQwry.dat
- asp qqwry.dat
- qqwry.dat的数据结构解释
- QQWry.dat文件结构分析(zz)
- 用来读取QQwry.dat的java代码
- 通过IP地址获取城市 QQWry.Dat
- java ip 查询定位 QQwry.dat
- QQWry.Dat纯真IP数据库解析
- 使用QQWry.dat获取ip地址
- 纯真IP数据库QQWry.dat格式详解
- 纯真IP数据库QQWry.dat格式详解
- opencv将图像数据写入二进制(.dat)文件
- 利用 QQWry.Dat 实现 IP 地址高效检索(PHP)
- .NET读取QQWry.Dat 纯真版ip数据库格式数据源
- java读取纯真IP数据库QQwry.dat的源代码
- 分类与聚类的区别
- 那些年Android黑科技②:欺骗的艺术
- 内容提供器访问Android数据库时候必须版本对应一致
- 设计模式之适配器模式的学习思考
- 芯片巨头英特尔放弃竞争迷你计算机市场
- QQWry.dat 数据写入
- 控件easyui-combobox动态设置默认值
- 5、面向对象的设计思想
- 机器学习C++库:dlib
- springmvc控制器
- DB2 的REORG_学习(1)_REORG INDEXES/TABLE Command
- 【怎样写代码】向现有类型“添加”方法 -- 扩展方法(二):扩展方法的实现与调用
- 回归算法学习笔记(二)局部加权线性回归
- 我的第一篇博客