天天看点

根据ip获得省市信息

根据输入ip,使用二分法获得ip对应的省市信息。

处理掉无效数据后,数据量大小不到20MB,使用一个MAP,一个SET,占用内存不多,可以作为一个接口服务使用。

输入ip地址,输出ip地址对应的省市。

内容格式如下:

IP开始 IP结束 国家 区县 区域Code
根据ip获得省市信息

代码如下:

/**
  *
  *
  *  根据输入ip,使用二分法算法获得省市信息  
  *  
  *  第一位ip255  *256*256*256 = 4278190080,
  *  目前数据中最大值为224  *256*256*256=3758096384,也超过int正数最大值2147483647,所以要使用long类型存储ip地址转后的值
  *  result=first+second+third+forth是将ip地址转为long类型。每一位ip通过移动位置的操作,保证每一位ip的值不会覆盖。
  *
  */
object IP2CityInfo {
  //声明排序后的long类型ip集合
  var sorted:Seq[Long]=_
  //构建低位ip地址(就是第一位ip的值)与省市的映射
  val ip2city=mutable.Map[Long,String]()

  def main(args: Array[String]) {
    val input=Source.fromFile("/Users/path/final_ip.txt")
    val lines= input.getLines()

    //用于测试,存储省市信息为未知的记录,用于后续将数据源进行再次处理
    val ip2cityError=mutable.Set[String]()
    //用于测试,记录中第一个ip地址信息是否有重复
    var total=0
    val set=mutable.Set[Long]()

    lines.foreach(line=>{
      val fields = line.split("\t")
      val province=fields(3)
      val city=fields(4)
      var cityInfo=province+","+city
      if(province.equals(city)){
        //当为直辖市的情况
        cityInfo=province
      }

      val ip = fields.apply(0)
      val result=ip2Long(ip)

      if(!(province.equals("未知")||city.equals("未知"))){
        ip2city.put(result,cityInfo)
      }else{
        ip2cityError.add(line)
      }

      set.add(result)
      total=total+1
    })

    //total与set.size相等,表示ip地址转化为long类型,没有冲突
    if(total==set.size){
      println("ip地址转化为long类型,没有冲突")
    }else{
      println("ip地址转化为long类型,有冲突。即存在第一个ip值完全相同的记录")
    }

    println("输入记录总数:",total)
    println("有效记录条数:",ip2city.size)
    println("无效记录条数:",total-ip2city.size)
    println("有效记录取样:")
    for(e<-ip2city.take(10)){
      println(e)
    }
    println("无效记录取样:")
    for(e<-ip2cityError.take(10)){
      println(e)
    }

    println("排序后记录取样:")
    sorted=ip2city.keySet.toSeq.sorted
    for(e<-sorted.take(10)){
      println(e)
    }

    //test
    println(ip2cityInfo("1.194.194.198"))
    println(ip2cityInfo("1.194.4.198"))
  }


  /**
    *
    * 利用二分查找算法,根据输入的ip字符串省市信息,查询出包含此ip区间的
    *
    *  @param ip  输入ip
    *  @return    省市信息
    * */
  def ip2cityInfo(ip :String):String= {
    val value=ip2Long(ip)
    var index=0
    var min=0
    var mid=0
    var max=sorted.size-1
    var break=true
    while(min<=max && break){
      //采用无符号右移一位,即可以表示除以2
      mid = (min+max)>>>1
      val midValue = sorted(mid)
      if(midValue>value){
        max = mid-1
      }else if(sorted(mid)<value){
        min = mid+1
      }else{
        index= mid
        break=false
      }
    }

    //当没有查询到结果,保证index取得最小的索引值
    if(index==0){
      index=Math.min(min,Math.min(mid,max))
    }

    ip2city(sorted(index))
  }

  /**
    * 将ip字符串转化为long类型数字
    *
    * 使用第一个ip值的四位ip进行省市的锁定。如果使用前三段导致获得省市信息不准确
    * 222.221.30.199   222.221.30.201 中国 云南省    昆明市    未知 530100
    * 222.221.30.202   222.221.30.202 中国 云南省    保山市    昌宁县    530524
    *
    * */
  def ip2Long(ip :String):Long={
    val subIpArray = ip.split("\\.")
    val first=subIpArray(0).toLong * 256 * 256 * 256
    val second=subIpArray(1).toLong * 256 * 256
    val third=subIpArray(2).toLong * 256
    val forth=subIpArray(3).toLong
    val result=first+second+third+forth
    result
  }

}
           

继续阅读