life-boy 发表于 2011-01-14 13:29

ruby 查询 QQwry.dat ip 库的 IpLocationSeeker 类

ruby 查询 QQwry.dat ip 库的类
使用的是 QQwry.dat 的IP库, 网上貌似可以下载这个库, 不过已经是exe格式了, 汗. 还是喜欢以前的 压缩格式#!/usr/bin/env ruby
# coding: utf-8
# jianglibo(jianglibo@gmail.com)
# website: http://www.g0574.com
# =KK= 修改
#

require 'iconv'

class IpLocationSeeker
def initialize()
    fQQwry='D:\QQWry.Dat'
    #@datafile = File.open(fQQwry,"r:utf-8")
    @datafile = File.open(fQQwry,"rb")
    @first_index_pos,@last_index_pos= @datafile.read(8).unpack('L2')
    @index_num = (@last_index_pos - @first_index_pos)/7 + 1
end

def renamed_eump(number=100)
    p 'renamed eump' if $DEBUG
    last_index_pos = number * 7
    last_index_pos = @last_index_pos if last_index_pos > @last_index_pos
    current_num = 0
    error_num = 0
    @first_index_pos.step(last_index_pos,7) do |a_pos|
      begin
          current_num +=1
          @datafile.seek(a_pos)
          ip_int = @datafile.read(4).unpack('L1')
          @ip_record_pos = three_bytes_long_int(@datafile.read(3).unpack('C3'))
          puts ip_int.to_s + "***"+get_country_string + '***' + get_area_string
      rescue
          err
          puts "error at " + current_num.to_s
          error_num += 1
      end
      end
    return "total num:%s,error_num:%s" %
end

def seek(ip_str) #查询IP
    p ' seek' if $DEBUG
    @ip_str = ip_str
    @ip_record_pos = get_ip_record_pos
    begin #错误处理
      return get_country_string + get_area_string
    rescue Interrupt
      return ip_str
    rescue Exception => detail
      err
      return ip_str
    #~ retry
    end
end

def half_find(ip_want_int,index_lowest,index_highest) #二分
   if index_highest - index_lowest == 1
          index_lowest
   else
      index_middle = (index_lowest + index_highest)/2
      file_offset = @first_index_pos + index_middle * 7
      @datafile.seek(file_offset)
      ip_middle_int = @datafile.read(4).unpack("L")
      if ip_want_int == ip_middle_int
         index_lowest
      elsif ip_want_int > ip_middle_int
         index_lowest = index_middle
         half_find(ip_want_int,index_lowest,index_highest)
      else
         index_highest = index_middle
         half_find(ip_want_int,index_lowest,index_highest)
      end
   end
end

def three_bytes_long_int(three_byte)
    (three_byte << 16) + (three_byte<<8) + three_byte
end

def get_ip_record_pos()
    p 'get ip record pos' if $DEBUG
    ip_record_pos_pos = @first_index_pos + half_find(my_inet_aton(@ip_str),0,@index_num-1)*7 + 4
    @datafile.seek(ip_record_pos_pos)
    three_byte = @datafile.read(3).unpack('C3')
    three_bytes_long_int(three_byte)
end

#读取直到字符串结尾
def read_zero_end_string(file_pos)
    p 'read 0 str' if $DEBUG
    @datafile.seek(file_pos)
    str = ""
    count = 0
    while c = @datafile.getc
      break if count>100
      break if c.ord < 0x32
      str << c
      count += 1
    end
    if count > 70
      puts 'ipwry count:' + count.to_s
      str = "2 unknown string."
      @get_country_string_error = true
    end
    @after_read_country_pos = @datafile.pos
    return Iconv.conv("UTF-8//IGNORE","GB18030//IGNORE",str) rescue ''
end

#private

def get_country_string()
    p 'get country str' if $DEBUG
    @get_country_string_error = false
    begin
      @datafile.seek(@ip_record_pos + 4)
      @mode_flag,= @datafile.read(1).unpack("C1")
      if @mode_flag == 1 #the next three bytes are another pointer
      @ip_record_level_two_pos = three_bytes_long_int(@datafile.read(3).unpack('C3'))
      @datafile.seek(@ip_record_level_two_pos)
      @level_two_mode_flag, = @datafile.read(1).unpack("C1")
      if @level_two_mode_flag == 2
          @ip_record_level_three_pos = three_bytes_long_int(@datafile.read(3).unpack('C3'))
          @level_three_mode_flag, = @datafile.read(1).unpack("C1")
          read_zero_end_string(@ip_record_level_three_pos)
      else
          @level_two_mode_flag = 0
          read_zero_end_string(@ip_record_level_two_pos)
      end
      elsif @mode_flag == 2
      @ip_record_level_two_pos = three_bytes_long_int(@datafile.read(3).unpack('C3'))
      read_zero_end_string(@ip_record_level_two_pos)
      else
      @mode_flag = 0
      read_zero_end_string(@ip_record_pos + 4)
      end
    rescue
      err
      @get_country_string_error = true
      return "unknown country!"
    end
end

def get_area_string()
    p 'get area string' if $DEBUG
    @get_area_string_error = false
    if @get_country_string_error
      @get_area_string_error = true
      return "4 unknown area!"
    end
    begin
      if @mode_flag == 0
      read_zero_end_string(@after_read_country_pos)
      elsif @mode_flag == 1
      if @level_two_mode_flag == 2
          #p @level_three_mode_flag
          if @level_three_mode_flag == 1 || @level_three_mode_flag == 2
            @datafile.seek(@ip_record_level_two_pos + 5)
            @ip_record_area_string_pos = three_bytes_long_int(@datafile.read(3).unpack('C3'))
            read_zero_end_string @ip_record_area_string_pos
          else
            read_zero_end_string(@ip_record_level_two_pos + 4)
          end
      else
          read_zero_end_string(@after_read_country_pos)
      end
      else
      read_zero_end_string(@ip_record_pos+8)
      end
    rescue
      err
      @get_area_string_error = true
      return "unknown area!"
    end
end
def err
    p $!.message + $@
end

def my_inet_aton(ip_str)
    ip_str.split(".").collect{|x|x.to_i}.inject(0){|ip_int,ip_field|
      ip_int = (ip_int << 8) + ip_field
    }
end
end
页: [1]
查看完整版本: ruby 查询 QQwry.dat ip 库的 IpLocationSeeker 类