Ruby:读取两个文件

时间:2015-05-18 15:47:12

标签: ruby csv

我有一个9,000多行CSV文件traffic.csv,其中包含有关通过防火墙的流量的信息 我有第二个400行文本文件myhosts.txt,其中包含我的主机。

我想在字段'src-hostclass'和'dst-hostclass'中搜索我的主机(第二个文件)。

我的第一次尝试:

#!/usr/bin/ruby
require 'csv'
hostclass = ARGV[0]

# Load file
csv_fname = ARGV[1]

csv_text = File.open(csv_fname)
csv = CSV.parse(csv_text, :headers => true )

  temp = csv.find {|row| row['src-hostclass'] == hostclass }
  puts temp if temp
  temp = csv.find {|row| row['dst-hostclass'] == hostclass }
  puts temp if temp

(“如果临时就放温度”,那么就不会打印出空行。有没有更好的方法呢?)

然后我这样调用ruby程序:

for i in `cat myhosts.txt` ; do ./findhosts.rb $i traffic.csv ; done

这很有效,但速度很慢。

更改我的脚本以便同时读取这两个文件的最佳方法是什么?

我尝试读取这两个文件然后在内存中搜索,但这不起作用(找不到任何内容):

require 'csv'

# Load all hosts
hostclasses = File.open(ARGV[0], "r")

# Load CSV file
csv_fname = ARGV[1]

csv_text = File.open(csv_fname)
csv = CSV.parse(csv_text, :headers => true )

hostclasses.each do |hostclass|
  temp = csv.find {|row| row['src-hostclass'] == hostclass }
  puts temp if temp
  temp = csv.find {|row| row['dst-hostclass'] == hostclass }
  puts temp if temp
end

我做错了什么?

2015年5月19日更新:根据建议,我更改并简化了我的计划:

require 'csv'
require 'set'

hostclasses = File.readlines(ARGV[0]).to_set

csv_text = File.open(ARGV[1])
csv = CSV.parse(csv_text, :headers => true )

results = csv.filter {|row| hostclasses.member?(row['src-hostclass']) || hostclasses.member?(row['dst-hostclass']) }

但是运行此结果

program.rb:9:in `<main>': undefined method `filter' for #<CSV::Table mode:col_or_row row_count:10> (NoMethodError)

2 个答案:

答案 0 :(得分:2)

您正在执行您的Ruby程序400次,每个主机执行一次。相反,尝试使程序更灵活,这样您就可以执行一次。这样,它只需要解析该9000行CSV文件一次。您可以使用Ruby而不是使用Bash脚本阅读myhosts.txt

另一个问题是您正在遍历9000行CSV文件以使用Array#find查找行。这将花费O(N)时间,在这种情况下可能会很慢。相反,您应该使用index,这样您就可以在O(log(N))时间内有效地查找行。简单的Ruby哈希是一种很好的索引类型。

这是我提出并测试的脚本:

#!/usr/bin/ruby
require 'csv'

raise if ARGV.size != 2
hosts_fname, csv_fname = ARGV

row_by_src = {}
row_by_dst = {}
CSV.foreach(csv_fname, headers: true) do |row|
  row_by_src[row['src-hostclass']] = row
  row_by_dst[row['dst-hostclass']] = row
end

File.foreach(hosts_fname) do |host|
  host = host.chomp
  s = row_by_src[host] and puts s
  d = row_by_dst[host] and puts d
end

答案 1 :(得分:2)

问题不在于您需要一次读取两个文件,而是在shell循环中多次读取同一文件。相反,您应该阅读一次CSV文件并构建一个包含所有主机名的Set。然后一次打印出所有行。

require 'csv'
require 'set'

hostclasses = File.readlines('myhosts.txt').to_set
results = CSV.foreach(ARGV[1], headers: true) do |row|
  puts row['src-hostclass'] if hostclasses.member?(row['src-hostclass'])
  puts row['dst-hostclass'] if hostclasses.member?(row['dst-hostclass'])
end