在Ruby中解析制表符分隔文件的最佳(最有效)方法是什么?
Answers:
Ruby CSV库使您可以指定字段定界符。Ruby 1.9使用FasterCSV。这样的事情会起作用:
require "csv"
parsed_file = CSV.read("path-to-file.csv", { :col_sep => "\t" })
quote_char
为nil
:parsed_file = CSV.read("path-to-file.csv", col_sep: "\t", quote_char: nil)
。与使用您认为不会出现在文件中的字符相比,这更健壮和优雅。
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
\d
CSV解析器失败得多么可怕。
line = 'boogie\ttime\tis "now"'
会导致字符串中带有两个转义的制表符,因此我认为失败可能是由于我实际上编写错误的测试导致的。要获得预期使用的测试字符串,line = "boogie\ttime\tis \"now\""
或者"boogie\ttime\tis " + '"now"'
可以使用进行测试puts
。前一个结果出现boogie\ttime\tis "now"
,而后两个结果出现boogie time is "now"
(选项卡在此处显示不佳,但会在您的控制台中显示)。感谢您的全面回答👍–
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\"")
实际上,有两种不同的TSV文件。
TSV文件实际上是CSV文件,分隔符设置为Tab。例如,将Excel电子表格另存为“ UTF-16 Unicode文本”时,将获得此内容。这样的文件使用CSV引用规则,这意味着只要使用引号将字段包含制表符和换行符,并且文字双引号将被写入两次。正确解析所有内容的最简单方法是使用csv
gem:
use 'csv'
parsed = CSV.read("file.tsv", col_sep: "\t")
符合IANA标准的TSV文件。不允许将制表符和换行符用作字段值,并且不引用任何形式。例如,当您选择整个Excel电子表格并将其粘贴到文本文件中时,您会得到这种结果(请注意:如果某些单元格确实包含制表符或换行符,它将被弄乱)。可以轻松地逐行分析此类TSV文件line.rstrip.split("\t", -1)
(请注意-1
,这可防止split
删除空的尾随字段)。如果要使用csv
gem,只需将其设置quote_char
为nil
:
use 'csv'
parsed = CSV.read("file.tsv", col_sep: "\t", quote_char: nil)
我喜欢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条目)。