Created
May 26, 2012 08:52
-
-
Save jhjguxin/2792965 to your computer and use it in GitHub Desktop.
FindCity by sina city
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#require 'iconv' | |
class IpSearch | |
#initialize(file='qqwry.dat') | |
def initialize(file="#{Rails.root}/lib/iplocation/qqwry.dat") | |
filename = file | |
@file = File.open(filename,"r") | |
@index_first,@index_last = @file.read(8).unpack('L2') | |
@index_total = (@index_last - @index_first)/7 + 1 | |
@location = {} | |
end | |
#把IP转换为长整形 | |
def ip2long(ip) | |
long = 0 | |
ip.split(/\./).each_with_index do |b, i| | |
long += b.to_i << 8*(3-i) | |
end | |
long | |
end | |
#读取偏移值 | |
def read_offset(position) | |
@file.seek position | |
chars = @file.read(3).unpack('C3') | |
(chars[2] << 16 ) + (chars[1] << 8) + chars[0] | |
end | |
#读取记录中的4字节作为一个long值 | |
def read_long(position) | |
@file.seek position | |
@file.read(4).unpack('L')[0] | |
end | |
#读取模式信息,1和2为正常,其他值异常 | |
#position:字符串偏移量 | |
def read_mode(position) | |
@file.seek position #前4位为IP值 | |
@file.read(1).unpack('C')[0] | |
end | |
#根据IP在索引中查找具体位置 | |
def find_str_offset(ip_long) | |
offset_min,offset_max = @index_first,@index_last | |
while offset_min <= offset_max | |
offset_mid = offset_min + (offset_max - offset_min) / 14*7 | |
mid = read_long(offset_mid) | |
if ip_long < mid | |
offset_max = offset_mid - 7 | |
elsif ip_long == mid | |
return read_offset(offset_mid+4) | |
else | |
offset_min = offset_mid + 7 | |
end | |
end | |
return read_offset(offset_max+4) | |
end | |
#读取字符串 | |
def read_str(position) | |
@file.seek position | |
str = [] | |
while c = @file.getc | |
break if str.size > 60 #地址不会太长,防止有异常数据 | |
break if c == "\0" #地址字符串以\0结尾 | |
str << c | |
end | |
str.join '' | |
end | |
#根据IP查找地址 | |
def find_ip_location(ip) | |
offset = find_str_offset(ip2long(ip))#读取具体数据在记录区的偏移 | |
@location = {} | |
case read_mode(offset+4) | |
when 1 | |
str_offset = read_offset(offset+4+1) #读取字符串存储位置偏移(4是IP值,1是模式) | |
if read_mode(str_offset)==2 then | |
country_offset = read_offset(str_offset+1) | |
@location[:country] = read_str country_offset | |
@location[:area] = read_area(str_offset+4) | |
else | |
@location[:country] = read_str str_offset | |
@location[:area] = read_area(@file.pos) | |
end | |
when 2 | |
str_offset = read_offset(offset+4+1) #读取字符串存储位置偏移(4是IP值,1是模式) | |
@location[:country] = read_str(str_offset) | |
@location[:area] = read_area(offset+8) | |
else | |
@location[:country] = read_str(offset) | |
@location[:area] = read_str(@file.pos) | |
end | |
@location | |
end | |
#读取记录中的地址信息 | |
def read_area(position) | |
mode = read_mode(position) | |
if mode==1 || mode==2 | |
offset = read_offset(position+1) | |
return '' if offset==0 | |
return read_str(offset) | |
else | |
return read_str(position) | |
end | |
end | |
#取得国家,UTF8编码 | |
def country | |
#Iconv.iconv('UTF-8//IGNORE','GB2312//IGNORE',@location[:country]) | |
@location[:country].force_encoding('gbk').encode("utf-8", :undef => :replace, :replace => "?", :invalid => :replace) | |
end | |
#取得地区,UTF8编码 | |
def area | |
#Iconv.iconv('UTF-8//IGNORE','GB2312//IGNORE',@location[:area]) | |
@location[:area].force_encoding('gbk').encode("utf-8", :undef => :replace, :replace => "?", :invalid => :replace) | |
end | |
#取得国家,GB2312编码 | |
def country_gb | |
@location[:country].force_encoding('utf-8').encode('gbk-2312', :undef => :replace, :replace => "?", :invalid => :replace) | |
end | |
#取得地区,GB2312编码 | |
def area_gb | |
@location[:country].force_encoding('utf-8').encode('gbk-2312', :undef => :replace, :replace => "?", :invalid => :replace) | |
end | |
end | |
if __FILE__==$0 | |
#************************以下测试代码***************** | |
time_start = Time.now | |
list = %w[66.249.71.153 202.8.15.255.2 61.157.175.233 58.19.176.201 61.178.12.170 61.191.187.113 121.14.133.169 58.222.234.230 202.198.184.136 121.12.116.58 203.191.148.55] | |
is = IpSearch.new | |
100.times do |i| | |
list.each do |ip| | |
is.find_ip_location(ip) | |
puts is.country | |
puts is.country.split("省").last.gsub("市","") | |
puts is.area | |
puts '-'*50 | |
end | |
end | |
puts "total time:#{Time.now-time_start}" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment