在Ruby中解析制表符分隔文件的最佳方法是什么?


68

在Ruby中解析制表符分隔文件的最佳(最有效)方法是什么?

Answers:


111

Ruby CSV库使您可以指定字段定界符。Ruby 1.9使用FasterCSV。这样的事情会起作用:

require "csv"
parsed_file = CSV.read("path-to-file.csv", { :col_sep => "\t" })

9
请注意,如果任何制表符分隔的值包含双引号,则此方法将失败。其他答案中的StrictTsv建议更可靠。
约书亚·弗拉纳根

如果文件中的双引号使用不正确,则可以替换"文件中永远不会出现的其他字符。parsed_file = CSV.read("path-to-file.csv", { col_sep: "\t", quote_char: '}')文档
亚伦·格雷

2
另一种方法是使用该liberal_parsing选项。parsed_file = CSV.read("path-to-file.csv", { col_sep: "\t", liberal_parsing: true)文档
亚伦·格雷

如果您想完全按照文件中的双引号来阅读,只需将其设置quote_charnilparsed_file = CSV.read("path-to-file.csv", col_sep: "\t", quote_char: nil)。与使用您认为不会出现在文件中的字符相比,这更健壮和优雅。
michau

最新版本的CSV库使用不希望您使用周围的花括号,并且如果您使用“警告:不建议使用最后一个参数作为关键字参数;建议将**添加到调用中”,则会抱怨它
cdmo

31

TSV的规则实际上与CSV有所不同。主要区别在于CSV的规定是在字段内粘贴逗号,然后在字段内使用引号和转义引号。我写了一个简单的例子来说明简单的响应是如何失败的:

require 'csv'
line = 'boogie\ttime\tis "now"'
begin
  line = CSV.parse_line(line, col_sep: "\t")
  puts "parsed correctly"
rescue CSV::MalformedCSVError
  puts "failed to parse line"
end

begin
  line = CSV.parse_line(line, col_sep: "\t", quote_char: "Ƃ")
  puts "parsed correctly with random quote char"
rescue CSV::MalformedCSVError
  puts "failed to parse line with random quote char"
end

#Output:
# failed to parse line
# parsed correctly with random quote char

如果要使用CSV库,则可以使用不希望看到文件是否为随机引号(示例显示此信息),但是也可以使用更简单的方法,例如下面显示的StrictTsv类,以获取同样的效果而不必担心字段引用。

# The main parse method is mostly borrowed from a tweet by @JEG2
class StrictTsv
  attr_reader :filepath
  def initialize(filepath)
    @filepath = filepath
  end

  def parse
    open(filepath) do |f|
      headers = f.gets.strip.split("\t")
      f.each do |line|
        fields = Hash[headers.zip(line.split("\t"))]
        yield fields
      end
    end
  end
end

# Example Usage
tsv = Vendor::StrictTsv.new("your_file.tsv")
tsv.parse do |row|
  puts row['named field']
end

选择使用CSV库还是使用更严格的格式仅取决于谁向您发送文件,以及他们是否期望遵守严格的TSV标准。

有关TSV标准的详细信息,请参见http://en.wikipedia.org/wiki/Tab-separated_values


请在答案中包含代码段,而不是在外部要旨中。现在,要点似乎已经下降,真是可惜。
Jezen Thomas

4
@JezenThomas感谢您的注意。我内联提取了所有代码示例,以解决必须查看要点的问题
mmmries,2015年

好答案。👍。我很惊讶\dCSV解析器失败得多么可怕。
dps

1
这是次要的,但会让我有些误入歧途。line = 'boogie\ttime\tis "now"'会导致字符串中带有两个转义的制表符,因此我认为失败可能是由于我实际上编写错误的测试导致的。要获得预期使用的测试字符串,line = "boogie\ttime\tis \"now\""或者"boogie\ttime\tis " + '"now"'可以使用进行测试puts。前一个结果出现boogie\ttime\tis "now",而后两个结果出现boogie time is "now"(选项卡在此处显示不佳,但会在您的控制台中显示)。感谢您的全面回答👍–
亚伦

我只是发现,至少在Ruby 2.5.0中,这与TSV特别相关,但由于引号的位置,整个CSV库和规范都与之有关。以下两项都将失败,CSV.parse("foo,bar,and \"baz\" quotes")并且CSV.parse("foo\tbar\tand \"baz\" quotes", col_sep: "\t")。看来引号仅在它们包围整个列的内容时才有效,以便您可以包括列分隔符。在以下两个解析罚款CSV.parse("foo\tbar\t\"and baz\tquotes\"", col_sep: "\t")CSV.parse("foo,bar,\"and baz,quotes\"")
阿伦

3

实际上,有两种不同的TSV文件。

  1. TSV文件实际上是CSV文件,分隔符设置为Tab。例如,将Excel电子表格另存为“ UTF-16 Unicode文本”时,将获得此内容。这样的文件使用CSV引用规则,这意味着只要使用引号将字段包含制表符和换行符,并且文字双引号将被写入两次。正确解析所有内容的最简单方法是使用csvgem:

    use 'csv'
    parsed = CSV.read("file.tsv", col_sep: "\t")
    
  2. 符合IANA标准的TSV文件。不允许将制表符和换行符用作字段值,并且不引用任何形式。例如,当您选择整个Excel电子表格并将其粘贴到文本文件中时,您会得到这种结果(请注意:如果某些单元格确实包含制表符或换行符,它将被弄乱)。可以轻松地逐行分析此类TSV文件line.rstrip.split("\t", -1)(请注意-1,这可防止split删除空的尾随字段)。如果要使用csvgem,只需将其设置quote_charnil

    use 'csv'
    parsed = CSV.read("file.tsv", col_sep: "\t", quote_char: nil)
    

0

我喜欢mmmries的答案。但是,我讨厌红宝石在分割结束时剥离所有空值的方式。它也不是删除行尾的换行符。

另外,我在一个字段中有一个带有潜在换行符的文件。因此,我重写了他的“解析”,如下所示:

def parse
  open(filepath) do |f|
    headers = f.gets.strip.split("\t")
    f.each do |line|
      myline=line
      while myline.scan(/\t/).count != headers.count-1
        myline+=f.gets
      end
      fields = Hash[headers.zip(myline.chomp.split("\t",headers.count))]
      yield fields
    end
  end
end

这会根据需要连接任何行以获得完整的数据行,并始终返回完整的数据集(末尾没有可能的nil条目)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.