Rubyでタブ区切りファイルを解析するための最良の(最も効率的な)方法は何ですか?
4 に答える
Ruby CSVライブラリでは、フィールド区切り文字を指定できます。Ruby1.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を参照してください。
実際には2種類のTSVファイルがあります。
区切り文字がTabに設定された実際のCSVファイルであるTSVファイル。これは、たとえばExcelスプレッドシートを「UTF-16Unicodeテキスト」として保存した場合に得られるものです。このようなファイルはCSV引用符ルールを使用します。つまり、引用符で囲まれている限り、フィールドにタブと改行を含めることができ、リテラルの二重引用符は2回書き込まれます。すべてを正しく解析する最も簡単な方法は、
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エントリはありません)。