因為臉書上有人分享這篇:
http://blog.darkthread.net/post-2017-02-13-in-memory-ip-to-country-mapping.aspx
用 100 行 C# 打造 IP 所屬國家快速查詢功能
然後有人說,是不是用 python 可以更少行,所以我就來試試會變成怎樣。
總之重寫文章裡的最後成果而已,儘量把文章中提到要防呆的也做進去,不然會不公平。
效能測試我因為不知道怎麼快速的弄出 1024 個隨機 ipv4 的列表,https://www.randomlists.com/ip-addresses 這裡給的看來是 ipv6,所以就偷懶不做。若是有人教我或是給我檔案,我可以按照文章中標準來測效能。我程式裡就隨便測 10 個 ip 的總時間,不然會因為時間太少得到 0.0。
import csv
import bisect
class IPCountryFinder:
def __init__(self, filename):
self.ranges = []
self.IPRanges = []
unknownCode = "--"
self.CountryNames = {unknownCode:"Unknown"}
self.IP2CN = {}
self.count = 0
with open(filename,'rb') as f:
s = csv.reader(f, delimiter=',', quotechar='"')
lastRangeEnd = 0
for i in s:
st = int(i[0])
ed = int(i[1])
cn = i[4]
if lastRangeEnd > 0 and st > lastRangeEnd:
self.IP2CN[lastRangeEnd] = unknownCode
self.IP2CN[st-1] = unknownCode
self.count += 2
self.IP2CN[st] = cn
self.IP2CN[ed] = cn
lastRangeEnd = ed + 1
self.CountryNames[cn] = i[6]
self.IPRanges = sorted(self.IP2CN.keys())
def GetIPAddrDec(self, ipAddr):
b = map(int,ipAddr.split('.'))
return reduce(lambda s, x: s*256 + x, b)
def GetCountryCode(self, ipAddr):
a = self.GetIPAddrDec(ipAddr)
idx = bisect.bisect_left(self.IPRanges, a) - 1
if idx < 0:
idx = -1
return self.IP2CN[self.IPRanges[idx]]
def ConvertCountryCodeToName(self, cnCode):
if cnCode in self.CountryNames:
return self.CountryNames[cnCode]
return cnCode
def GetCountryName(self, ipAddr):
return self.ConvertCountryCodeToName(self.GetCountryCode(ipAddr))
def test():
import time
a = IPCountryFinder('IpToC.csv')
m = time.time()
p=a.GetCountryName('1.2.3.4')
p=a.GetCountryName('1.2.3.8')
p=a.GetCountryName('1.2.4.8')
p=a.GetCountryName('1.4.3.8')
p=a.GetCountryName('6.2.3.8')
p=a.GetCountryName('1.2.3.34')
p=a.GetCountryName('1.2.34.8')
p=a.GetCountryName('1.34.3.8')
p=a.GetCountryName('34.2.3.8')
p=a.GetCountryName('18.2.3.8')
print time.time() - m
test()
如果很想比有幾行的話,全部程式是 66 行,不含 test 的部份是 47 行。這樣比一定不是很公平,c# 光右邊大括號就很多行了。但是少很多型態宣告是比較不殺腦筋的。