Rover12421's Blog

The End.

纯真IP(CZ88)数据文件解析 for Java

        一直想看看纯真Ip数据文件格式,却拖了好几年,知道最近需要用到本地ip库,才真正的查资料看明白了。其实也挺简单的,发现我写的代码吧,Java写的,有写到数据库部分。因为用了我自己的公共类,不能直接使用,用到的公共代码我也一并贴上吧,我也懒得改了,懂点java的改起来也不难。

-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–-–

CZ88.java

001 package org.rscis.ip;
002
003 import java.io.FileNotFoundException;
004 import java.io.FileOutputStream;
005 import java.io.IOException;
006 import java.io.RandomAccessFile;
007 import java.sql.SQLException;
008
009 import org.apache.http.util.ByteArrayBuffer;
010 import org.rscis.utils.Endian;
011 import org.rscis.utils.RsStringUtils;
012 import org.rscis.utils.database.MySql;
013
014
015 public class CZ88 {
016     
017     /
018      * 文件的头4个字节是索引区第一个元素的偏移量,第二个4字节是索引区最后一个元素的偏移量
019      * ★数据区元素:
020      * 存放IP信息中的:结束IP(4字节),国家(不定长),地区(不定长)
021      * 排列顺序:无要求
022      * ★索引区元素:
023      * 存放IP信息中的:起始IP(4字节),索引值(3字节)
024      * 排列顺序:起始IP按升序排列
025      * ★IP为4字节,如"255.0.0.0"表示为0xFF000000,存在文件中则为00 00 00 FF(字节序原因)
026      * ★索引值为该IP消息的<结束IP、国家、地区>在文件中的位置。指向<结束IP>
027      * ★如果结束IP后的字节为0x01,则说明该IP消息的<国家、地区>与前面的IP信息重复,这时0x01后面的3个字节为国家、地区字符串的偏移量。可以根据这三个字节去前面找国家、地区。
028      * ★如果国家的第一个字节为0x02,说明该国家串与前面的国家或地区串重复,0x02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。
029      * ★如果地区的第一个字节为0x02,说明该地区串与前面的国家或地区串重复,0x02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。
030      * ★有可能在出现0x01的情况下出现0x02,这时需要跳转两次查找国家、地区字符串。
031      * ★正常的字符串以NULL做结尾。
032      * ★IP信息不允许有重复、覆盖
033      * ★使用索引是为了保证能以线性速度搜索
034      * ★新格式不允许为未知数据的IP消息,原格式中的未知数据已经都去掉了。如果有未知数据的IP信息,将大大增加文件长度。
035      */
036         
037     private static final String IP_FILE = “qqwry.dat”;
038     
039     /

040      * 从但前偏移读取一个以0结尾的字符串
041      * @param file
042      * @return
043      * @throws IOException
044      /
045     public static String readString(RandomAccessFile file) throws IOException {
046         String ret = “”;
047         ByteArrayBuffer buffer = new ByteArrayBuffer(100);
048         
049         byte b;
050         while ((b = file.readByte()) != ) {
051             buffer.append(b);
052         }
053         
054         ret = new String(buffer.toByteArray(), “GB18030”);
055         
056         return ret;
057     }
058     
059     /**
060      * 读取一个int类型的ipv4地址转换到字符串
061      * @param file
062      * @return
063      * @throws IOException
064     
/
065     public static String readIntIp(RandomAccessFile file) throws IOException {
066         return RsStringUtils.ipInt2String(Endian.convert(file.readInt()));
067     }
068     
069     /
070      * 读取一个3byte的Int
071      * @param file
072      * @return
073      * @throws IOException
074      */
075     public static int read3ByteInt(RandomAccessFile file) throws IOException {
076         byte[] b3 = new byte[3];
077         file.read(b3);
078         return ((b3[] & xFF) | ((b3[1] & xFF) << 8) | ((b3[2] & xFF) << 16)) & 0x00FFFFFF;
079     }
080     
081     /

082      * 读取一个区域字符串
083      * 如果首字节是0x2,说明该串与前面的字串有字符重复,后三字节表示重复的偏移位置
084      * @param file
085      * @return
086      * @throws IOException
087      /
088     public static String readAreaString(RandomAccessFile file) throws IOException {
089         int off = (int)file.getFilePointer();
090         byte b = file.readByte();
091         String ret = null;
092         if (b == 0x2) {
093             file.seek(read3ByteInt(file));
094             ret = readString(file);
095             file.seek(off+4);
096         }
097         else {
098             file.seek(off);
099             ret = readString(file);
100         }
101         
102         return ret;
103     }
104     
105     /**
106      * 读取国家和区域字符串
107      * @param file
108      * @return
109      * @throws IOException
110     
/
111     public static String[] readCountryArea(RandomAccessFile file) throws IOException {
112         String[] CountryArea = new String[2];
113         int off = (int)file.getFilePointer();
114         byte b = file.readByte();
115         if (b == 0x1) {
116             file.seek(read3ByteInt(file));
117             return readCountryArea(file);
118         }
119         else
120         {
121             file.seek(off);
122             CountryArea[] = readAreaString(file);
123             CountryArea[1] = readAreaString(file);
124         }
125         return CountryArea;
126     }
127     
128     public static String ipToStringNoDian(int ipInt) {
129         return “0x” + Integer.toHexString(ipInt);
130     }
131     
132     public static void main(String[] args) {
133         
134         try {
135             MySql mySql = new MySql(“rscis”, “Rscis”, “rscis”);
136             int curOff = ;
137             RandomAccessFile file = new RandomAccessFile(IP_FILE, “r”);
138             int indexStartOff = Endian.convert(file.readInt());
139             int indexEndOff = Endian.convert(file.readInt());
140             System.out.println(“indexStartOff : ” + indexStartOff);
141             System.out.println(“indexEndOff : ” + indexEndOff);
142             
143             int count = (indexEndOff - indexStartOff) / 7 + 1;
144             System.out.println(“Count : ” + count);
145             
146             mySql.startAutoBatchExecut(100);
147             
148             FileOutputStream fos = new FileOutputStream(“ip.txt”);
149             
150             curOff = indexStartOff;
151             file.seek(curOff);
152             for (int i = ; i < count; i++) {
153                 file.seek(curOff);
154                 curOff += 7;
155                 
156 //                String startIp = readIntIp(file);
157                 int startIp = Endian.convert(file.readInt());
158                 String strStartIp = ipToStringNoDian(startIp);
159                 int off = read3ByteInt(file);                
160                 file.seek(off);
161 //                String endIp = readIntIp(file);
162                 int endIp = Endian.convert(file.readInt());
163                 String strEndIp = ipToStringNoDian(endIp);
164                 
165                 String CountryArea[] = readCountryArea(file);
166                 String country = RsStringUtils.sqlStringConvert(CountryArea[]);
167                 String area = RsStringUtils.sqlStringConvert(CountryArea[1]);
168                 
169                 String sql = “INSERT INTO CZ88 (startIp, endIp, country, area) VALUES (” + strStartIp + “, ” + strEndIp + “, \”“ + country + ”\“, \”“ + area + ”\“);”;
170 //                System.out.println(i + “ : ” + startIp + “ >>> ” + endIp + “ ### ” + country + “ ### ” + area);
171                 System.out.println(sql);
172                 String sql2 = sql + “\n”;
173                 fos.write(sql2.getBytes(“GB18030”));
174                 fos.flush();
175                 mySql.addAutoBatch(sql);
176             }
177             
178             mySql.endAutoBatch();
179             mySql.close();
180             
181 //            System.out.println(StringUtils.ipInt2String(ip));
182 //            System.out.println(StringUtils.ipString2Int(“0.255.255.255”));
183             
184         } catch (FileNotFoundException e) {
185             // TODO Auto-generated catch block
186             e.printStackTrace();
187         } catch (IOException e) {
188             // TODO Auto-generated catch block
189             e.printStackTrace();
190         } catch (ClassNotFoundException e) {
191             // TODO Auto-generated catch block
192             e.printStackTrace();
193         } catch (SQLException e) {
194             // TODO Auto-generated catch block
195             e.printStackTrace();
196         }
197     }
198
199 }

下面是用到公共类里面的方法

RsStringUtils.java

01 package org.rscis.utils;
02
03 public class RsStringUtils {
04     
05     /
06      * 把一个int格式的ipv4地址转成一个ipv4字符串,如127.0.0.1
07      * @param ipInt
08      * @return
09      */
10     public static String ipInt2String(int ipInt) {
11         return ((ipInt >> 24) & xFF) + “.” + ((ipInt >> 16) & xFF) + “.” + ((ipInt >> 8) & xFF) + “.” + (ipInt & xFF);
12     }
13     
14     /

15      * 把一个ipv4的字符串转成int格式
16      */
17     public static int ipString2Int(String ipString) {
18         int ipInt = ;
19         String[] ipStrs = ipString.split(“\.”);
20         if (ipStrs.length != 4) {
21             ipInt = -1;
22         }
23         else {
24             ipInt = (Integer.parseInt(ipStrs[]) << 24) | (Integer.parseInt(ipStrs[1]) << 16) | (Integer.parseInt(ipStrs[2]) << 8) | Integer.parseInt(ipStrs[3]);
25         }
26         return ipInt;
27     }
28     
29     public static String sqlStringConvert(String str) {
30         String ret = str;
31         if (ret == null) {
32             ret = “”;
33         }
34         
35         ret = ret.replaceAll(“\\”, “\\\\”);
36         ret = ret.replace(“\”“, ”\\“”);
37         
38         return ret;
39     }
40 }

Endian.java

01 package org.rscis.utils;
02 /
03
字节序转换
04
@author Rover12421
05
06
/
07 public class Endian {
08     public static long convert(long l) {
09         return (
10                     ((l >> 56) & 0x00000000000000FFL)
11                 | ((l << 56) & xFF00000000000000L)
12                 | ((l >> 40) & 0x000000000000FF00L)
13                 | ((l << 40) & 0x00FF000000000000L)
14                 | ((l >> 24) & 0x0000000000FF0000L)
15                 | ((l << 24) & 0x0000FF0000000000L)
16                 | ((l >> 8& 0x00000000FF000000L)
17                 | ((l << 8& 0x000000FF00000000L)
18                 );
19     }
20     
21     public static int convert(int l) {
22         return (
23                   ((l >> 24) & 0x000000FF)
24                 | ((l << 24) & xFF000000)
25                 | ((l >> 8& 0x0000FF00)
26                 | ((l << 8& 0x00FF0000)
27                 );
28     }
29     
30     public static short convert(short l) {
31         return (short)(
32                   ((l >> 8& 0x00FF)
33                 | ((l << 8& xFF00)
34                 );
35     }
36     
37 }

        数据库部分就不贴了,用的是自己写的封装,代码量还是有一些的,呵呵~~~

Comments