web-dev-qa-db-ja.com

Rubyでタブ区切りファイルを解析する最良の方法は何ですか?

Rubyでタブ区切りファイルを解析する最良の(最も効率的な)方法は何ですか?

63
mbm

Ruby CSVライブラリを使用すると、フィールド区切り文字を指定できます。Ruby 1.9は FasterCSV を使用します。

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

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 をご覧ください。

30
mmmries

実際には、TSVファイルには2種類あります。

  1. 実際にCSVファイルであり、区切り文字がタブに設定されているTSVファイル。これは、たとえばExcelスプレッドシートを「UTF-16 Unicode Text」として保存します。このようなファイルはCSVの引用規則を使用します。つまり、引用されている限り、フィールドにタブと改行を含めることができ、リテラルの二重引用符は2回書き込まれます。すべてを正しく解析する最も簡単な方法は、csv gemを使用することです。

    _use 'csv'
    parsed = CSV.read("file.tsv", col_sep: "\t")
    _
  2. 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)
    _
0
michau

私は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エントリはありません)。

0
Jim MacKenzie