My solution suffers from over-generality (is supposed to work for any sorted
string file by any sorting criteria), and from over-limiting memory usage -
I like Simon's approach, reading in chunks makes everything easier and
faster.
BTW, all solutions already submitted will lie for subnets 1,2 and 5 :)
Most (but not all) will break on out of bounds submissions (256.256.256.256
or 0.0.0.-1, latter if comments are stripped out)
(yes, I know, just trying to find an excuse for not very elegant solution -
edge cases kill elegancy :)
>-----------------------------------------------------------------------------
def bin_find(file,search)
if block_given?
compare = lambda { |a, b| yield(a, b) }
else
compare = lambda { |a, b| a <=> b }
end
open(file) do |f|
return bin_search(f,search,f.lstat.size,&compare)
end
end
def bin_search(f,search,size)
start,finish=0,size
while start<finish
dist=(finish-start)/2
f.seek(start+dist)
f.readline unless start+dist==0
case (l1=f.readline.chomp rescue false) ? yield(search,l1) : -1
when -1
next if (finish=start+dist)>0
break
when 0
return l1
else
case (l2=f.readline.chomp rescue false) ? yield(search,l2) : -1
when -1
return l1
when 0
return l2
else
start+=dist; next
end
end
end
nil
end
nums=[]
out=true
if ARGV[0]=='test'
n=ARGV[1].to_i
n.times{nums << rand(4294967296)}
out=false
else
ARGV.each do |argv|
nums << ((($1.to_i*256)+$2.to_i)*256+$3.to_i)*256+$4.to_i if
argv=~/(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})/
end
end
if nums.empty?
puts "Please enter valid ip(s) (or use '#{$0} test NNN' for testing)"
exit
end
nums.each do |num|
ctry='Unknown'
res=bin_find('IpToCountry.csv',num) { |search, str|
str.empty? || str[0,1]!='"' ? 1 : search <=>
str.gsub('"','').split(',')[0].to_i
}.gsub('"','').split(',')
ctry=res[4] if (res[0].to_i..res[1].to_i)===num
puts ctry if out
end