Rubyでタブ区切りファイルを解析する最良の(最も効率的な)方法は何ですか?
Ruby CSVライブラリを使用すると、フィールド区切り文字を指定できます。Ruby 1.9は FasterCSV を使用します。
require "csv"
parsed_file = CSV.read("path-to-file.csv", { :col_sep => "\t" })
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 をご覧ください。
実際には、TSVファイルには2種類あります。
実際にCSVファイルであり、区切り文字がタブに設定されているTSVファイル。これは、たとえばExcelスプレッドシートを「UTF-16 Unicode Text」として保存します。このようなファイルはCSVの引用規則を使用します。つまり、引用されている限り、フィールドにタブと改行を含めることができ、リテラルの二重引用符は2回書き込まれます。すべてを正しく解析する最も簡単な方法は、csv
gemを使用することです。
_use 'csv'
parsed = CSV.read("file.tsv", col_sep: "\t")
_
IANA標準 に準拠するTSVファイル。タブと改行はフィールド値として許可されておらず、引用符は一切ありません。これは、たとえばExcelスプレッドシート全体を選択し、テキストファイルに貼り付けます(一部のセルにタブまたは改行が含まれていると混乱するため注意してください)。このようなTSVファイルは、単純なline.rstrip.split("\t", -1)
で行ごとに簡単に解析できます(split
が空の末尾フィールドを削除するのを防ぐ_-1
_に注意してください)。 csv
gemを使用する場合は、単に_quote_char
_をnil
に設定します。
_use 'csv'
parsed = CSV.read("file.tsv", col_sep: "\t", quote_char: nil)
_
私はmmmriesの回答が好きです。しかし、私はRubyがスプリットの終わりから空の値を取り除く方法が嫌いです。それは、行の終わりで改行を取り除くことでもありません。
また、フィールド内に潜在的な改行を含むファイルがありました。そこで、私は彼の「解析」を次のように書き直しました。
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エントリはありません)。