User9123
User9123

Reputation: 525

Parsing a CSV file for a specific IP address

In Ruby how can I parse a CSV file and output the information accordingly? For example:

require 'csv'

class CountryFilter

  class << self

    def find_specs_by_ip_address(ip)
      CSV.foreach('GeoIPCountry.csv') do |row|
        if row =~ Regexp.union(ip)
          data = row.split(',')
          return data[5]
        else
          return 'Unable to find country specifications'
        end
      end
    end

  end

end

puts CountryFilter.find_specs_by_ip_address('72.247.167.255')

CSV file:

...
"72.247.88.0","72.247.89.255","1224169472","1224169983","US","United States"
"72.247.90.0","72.247.103.255","1224169984","1224173567","NL","Netherlands"
"72.247.104.0","72.247.144.255","1224173568","1224184063","US","United States"
"72.247.145.0","72.247.145.255","1224184064","1224184319","NL","Netherlands"
"72.247.146.0","72.247.167.255","1224184320","1224189951","US","United States"
"72.247.168.0","72.247.179.255","1224189952","1224193023","NL","Netherlands"
"72.247.180.0","72.247.181.255","1224193024","1224193535","US","United States"
"72.247.182.0","72.247.182.255","1224193536","1224193791","NL","Netherlands"
"72.247.183.0","72.247.183.255","1224193792","1224194047","US","United States"
...

How can I parse this CSV file for an IP address and output the country it originates from?

Upvotes: 1

Views: 357

Answers (2)

Eric Duminil
Eric Duminil

Reputation: 54293

Notes

  • row is already a CSV row with multiple cells, you don't have to split anything and define data.
  • your method was returning after the first line in both cases : IP found or not.
  • Returning nil when no IP is found is probably a better idea than returning a String.

Refactored code

require 'csv'
class CountryFilter
  class << self
    def find_specs_by_ip_address(ip)
      CSV.foreach('GeoIPCountry.csv') do |row|
        return row.last if row.first(2).include?(ip)
      end
    end
  end
end

CountryFilter.find_specs_by_ip_address('72.247.167.255')
#=> "United States
CountryFilter.find_specs_by_ip_address('1.2.3.4')
#=> nil

v2

Thanks to @Felix for the insightful comments.

This method now considers ip1 and ip2 to be the bounds of an IP range, and checks if ip is in this range :

require 'csv'
require 'ipaddr'

module IPFilter
  def self.find_country_by_ip_address(ip)
    ip = IPAddr.new(ip).to_i
    CSV.foreach('GeoIPCountry.csv') do |_, _, ip1, ip2, _, country_name|
      return country_name if (ip1.to_i..ip2.to_i).cover?(ip)
    end
  end
end

p IPFilter.find_country_by_ip_address('72.247.167.123')
#=> "United States"
p IPFilter.find_country_by_ip_address('1.2.3.4')
#=> nil

Range#cover? :

Returns true if obj is between the begin and end of the range.

Upvotes: 1

akuhn
akuhn

Reputation: 27803

Try this,

...
CSV.foreach('GeoIPCountry.csv') do |a, b, c, d, e, country|
  return country if a == ip || b == ip
end
...

How does this work?

  • row is already split by comma
  • You can unpack the row in the block parameters to avoid magic numbers

NB, best give a, b, c, d, e more meaningful names.

Upvotes: 0

Related Questions