代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | public class CZ_INDEX_INFO { public UInt32 IpSet; public UInt32 IpEnd; public UInt32 Offset; public CZ_INDEX_INFO() { IpSet = 0; IpEnd = 0; Offset = 0; } } //读取纯真IP数据库类 public class PHCZIP { protected bool bFilePathInitialized; protected string FilePath; protected FileStream FileStrm; protected UInt32 Index_Set; protected UInt32 Index_End; protected UInt32 Index_Count; protected UInt32 Search_Index_Set; protected UInt32 Search_Index_End; protected CZ_INDEX_INFO Search_Set; protected CZ_INDEX_INFO Search_Mid; protected CZ_INDEX_INFO Search_End; public PHCZIP() { bFilePathInitialized = false ; } public PHCZIP( string dbFilePath) { bFilePathInitialized = false ; SetDbFilePath(dbFilePath); } //使用二分法查找索引区,初始化查找区间 public void Initialize() { Search_Index_Set = 0; Search_Index_End = Index_Count - 1; } //关闭文件 public void Dispose() { if (bFilePathInitialized) { bFilePathInitialized = false ; FileStrm.Close(); //FileStrm.Dispose(); } } public bool SetDbFilePath( string dbFilePath) { if (dbFilePath == "" ) { return false ; } try { FileStrm = new FileStream(dbFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); } catch { return false ; } //检查文件长度 if (FileStrm.Length < 8) { FileStrm.Close(); //FileStrm.Dispose(); return false ; } //得到第一条索引的绝对偏移和最后一条索引的绝对偏移 FileStrm.Seek(0, SeekOrigin.Begin); Index_Set = GetUInt32(); Index_End = GetUInt32(); //得到总索引条数 Index_Count = (Index_End - Index_Set) / 7 + 1; bFilePathInitialized = true ; return true ; } public string GetAddressWithIP( string IPValue) { if (!bFilePathInitialized) return "" ; Initialize(); UInt32 ip = IPToUInt32(IPValue); while ( true ) { //首先初始化本轮查找的区间 //区间头 Search_Set = IndexInfoAtPos(Search_Index_Set); //区间尾 Search_End = IndexInfoAtPos(Search_Index_End); //判断IP是否在区间头内 if (ip >= Search_Set.IpSet && ip <= Search_Set.IpEnd) return ReadAddressInfoAtOffset(Search_Set.Offset); //判断IP是否在区间尾内 if (ip >= Search_End.IpSet && ip <= Search_End.IpEnd) return ReadAddressInfoAtOffset(Search_End.Offset); //计算出区间中点 Search_Mid = IndexInfoAtPos((Search_Index_End + Search_Index_Set) / 2); //判断IP是否在中点 if (ip >= Search_Mid.IpSet && ip <= Search_Mid.IpEnd) return ReadAddressInfoAtOffset(Search_Mid.Offset); //本轮没有找到,准备下一轮 if (ip < Search_Mid.IpSet) //IP比区间中点要小,将区间尾设为现在的中点,将区间缩小1倍。 Search_Index_End = (Search_Index_End + Search_Index_Set) / 2; else //IP比区间中点要大,将区间头设为现在的中点,将区间缩小1倍。 Search_Index_Set = (Search_Index_End + Search_Index_Set) / 2; } } private string ReadAddressInfoAtOffset(UInt32 Offset) { string country = "" ; string area = "" ; UInt32 country_Offset = 0; byte Tag = 0; //跳过4字节,因这4个字节是该索引的IP区间上限。 FileStrm.Seek(Offset + 4, SeekOrigin.Begin); //读取一个字节,得到描述国家信息的“寻址方式” Tag = GetTag(); if (Tag == 0x01) { //模式0x01,表示接下来的3个字节是表示偏移位置 FileStrm.Seek(GetOffset(), SeekOrigin.Begin); //继续检查“寻址方式” Tag = GetTag(); if (Tag == 0x02) { //模式0x02,表示接下来的3个字节代表国家信息的偏移位置 //先将这个偏移位置保存起来,因为我们要读取它后面的地区信息。 country_Offset = GetOffset(); //读取地区信息(注:按照Luma的说明,好像没有这么多种可能性,但在测试过程中好像有些情况没有考虑到, //所以写了个ReadArea()来读取。 area = ReadArea(); //读取国家信息 FileStrm.Seek(country_Offset, SeekOrigin.Begin); country = ReadString(); } else { //这种模式说明接下来就是保存的国家和地区信息了,以'\0'代表结束。 FileStrm.Seek(-1, SeekOrigin.Current); country = ReadString(); area = ReadArea(); } } else if (Tag == 0x02) { //模式0x02,说明国家信息是一个偏移位置 country_Offset = GetOffset(); //先读取地区信息 area = ReadArea(); //读取国家信息 FileStrm.Seek(country_Offset, SeekOrigin.Begin); country = ReadString(); } else { //这种模式最简单了,直接读取国家和地区就OK了 FileStrm.Seek(-1, SeekOrigin.Current); country = ReadString(); area = ReadArea(); } string Address = country + " " + area; return Address; } private UInt32 GetOffset() { byte [] TempByte4 = new byte [4]; TempByte4[0] = ( byte )FileStrm.ReadByte(); TempByte4[1] = ( byte )FileStrm.ReadByte(); TempByte4[2] = ( byte )FileStrm.ReadByte(); TempByte4[3] = 0; return BitConverter.ToUInt32(TempByte4, 0); } protected string ReadArea() { byte Tag = GetTag(); if (Tag == 0x01 || Tag == 0x02) { FileStrm.Seek(GetOffset(), SeekOrigin.Begin); return ReadString(); } else { FileStrm.Seek(-1, SeekOrigin.Current); return ReadString(); } } protected string ReadString() { UInt32 Offset = 0; byte [] TempByteArray = new byte [256]; TempByteArray[Offset] = ( byte )FileStrm.ReadByte(); while (TempByteArray[Offset] != 0x00) { Offset += 1; TempByteArray[Offset] = ( byte )FileStrm.ReadByte(); } return System.Text.Encoding.Default.GetString(TempByteArray).TrimEnd( '\0' ); } protected byte GetTag() { return ( byte )FileStrm.ReadByte(); } protected CZ_INDEX_INFO IndexInfoAtPos(UInt32 Index_Pos) { CZ_INDEX_INFO Index_Info = new CZ_INDEX_INFO(); //根据索引编号计算出在文件中在偏移位置 FileStrm.Seek(Index_Set + 7 * Index_Pos, SeekOrigin.Begin); Index_Info.IpSet = GetUInt32(); Index_Info.Offset = GetOffset(); FileStrm.Seek(Index_Info.Offset, SeekOrigin.Begin); Index_Info.IpEnd = GetUInt32(); return Index_Info; } public UInt32 IPToUInt32( string IpValue) { string [] IpByte = IpValue.Split( '.' ); Int32 nUpperBound = IpByte.GetUpperBound(0); if (nUpperBound != 3) { IpByte = new string [4]; for (Int32 i = 1; i <= 3 - nUpperBound; i++) IpByte[nUpperBound + i] = "0" ; } byte [] TempByte4 = new byte [4]; for (Int32 i = 0; i <= 3; i++) { //'如果是.Net 2.0可以支持TryParse。 //'If Not (Byte.TryParse(IpByte(i), TempByte4(3 - i))) Then //' TempByte4(3 - i) = &H0 //'End If if (IsNumeric(IpByte[i])) TempByte4[3 - i] = ( byte )(Convert.ToInt32(IpByte[i]) & 0xff); } return BitConverter.ToUInt32(TempByte4, 0); } protected bool IsNumeric( string str) { if (str != null && System.Text.RegularExpressions.Regex.IsMatch(str, @"^-?\d+$" )) return true ; else return false ; } protected UInt32 GetUInt32() { byte [] TempByte4 = new byte [4]; FileStrm.Read(TempByte4, 0, 4); return BitConverter.ToUInt32(TempByte4, 0); } } |
测试用时:
1 2 3 4 | string path = Server.MapPath( "QQWry.Dat" ); PHCZIP zip = new PHCZIP(path); string address = zip.GetAddressWithIP( "125.70.76.85" ); |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理