55

RubyCSVから現在の行/行番号を取得する方法を見つけようとしています。これは私のコードです:

options = {:encoding => 'UTF-8', :skip_blanks => true}
CSV.foreach("data.csv", options, ) do |row, i|
   puts i
end

しかし、これは期待どおりに機能していないようです。これを行う方法はありますか?

4

4 に答える 4

133

現在のRubiesではCSVが変更されているため、いくつか変更を加える必要があります。2.6より前のRubyを使用した元のソリューションについては、回答のさらに下を参照してください。そして、その使用はwith_indexバージョンに関係なく機能し続けます。

2.6以降では、これは機能します。

require 'csv'

puts RUBY_VERSION

csv_file = CSV.open('test.csv')
csv_file.each do |csv_row|
  puts '%i %s' % [csv_file.lineno, csv_row]
end
csv_file.close

私が読んだ場合:

Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00
1996,Jeep,Grand Cherokee,"MUST SELL!\nair, moon roof, loaded",4799.00

コードの結果は次のようになります。

2.6.3
1 ["Year", "Make", "Model", "Description", "Price"]
2 ["1997", "Ford", "E350", "ac, abs, moon", "3000.00"]
3 ["1999", "Chevy", "Venture \"Extended Edition\"", "", "4900.00"]
4 ["1999", "Chevy", "Venture \"Extended Edition, Very Large\"", "", "5000.00"]
5 ["1996", "Jeep", "Grand Cherokee", "MUST SELL!\\nair, moon roof, loaded", "4799.00"]

変更は、現在のファイルハンドルにアクセスする必要があるためです。以前は、グローバルを使用できましたが、$.グローバルは呼び出されたコードの他のセクションに踏みつけられる可能性があるため、常に失敗する可能性がありました。開いているファイルのハンドルがあればlineno、気にせずに使用できます。


$.

2.6より前のRubyでは、これを実行できます。

Rubyには、現在読み取られているファイルの行番号であるマジック変数があります。$.

require 'csv'

CSV.foreach('test.csv') do |csv|
  puts $.
end

上記のコードを使用すると、次のようになります。

1
2
3
4
5

$INPUT_LINE_NUMBER

$.Perlでは常に使用されます。Rubyでは、「魔法の」側面を避けるために、次の方法で使用することをお勧めします。

require 'english'

puts $INPUT_LINE_NUMBER

フィールドに埋め込まれた行末を処理する必要がある場合は、小さな変更で簡単に処理できます。新しい行が埋め込まれた行を含むCSVファイル「test.csv」を想定します。

Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1996,Jeep,Grand Cherokee,"MUST SELL!
air, moon roof, loaded",4799.00
1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00

with_index

列挙子を使用with_index(1)すると、CSVがブロックに生成される回数を簡単に追跡でき$.、行末を処理するために必要な余分な行を読み取るときに、CSVの作業を効果的にシミュレートしますが、それを尊重します。

require 'csv'

CSV.foreach('test.csv', headers: true).with_index(1) do |row, ln|
  puts '%-3d %-5s %-26s %s' % [ln, *row.values_at('Make', 'Model', 'Description')]
end

これを実行すると、次のように出力されます。

$ ruby test.rb
1   Ford  E350                       ac, abs, moon
2   Chevy Venture "Extended Edition"
3   Jeep  Grand Cherokee             MUST SELL!
air, moon roof, loaded
4   Chevy Venture "Extended Edition, Very Large"
于 2012-09-13T14:46:28.593 に答える
33

別の解決策は次のとおりです。

options = {:encoding => 'UTF-8', :skip_blanks => true}

CSV.foreach("data.csv", options).with_index do |row, i|
   puts i
end
于 2012-09-13T13:52:38.230 に答える
6

クリーンではなくシンプルなソリューション

options = {:encoding => 'UTF-8', :skip_blanks => true}
i = 0
CSV.foreach("data.csv", options) do | row |
  puts i
  i += 1
end
于 2012-09-13T13:13:59.530 に答える
4

Ruby 2.6+

ヘッダーなし

CSV.foreach( "data.csv", encoding: "UTF-8" ).with_index do |row, row_number|
  puts row_number
end

ヘッダー付き

CSV.foreach( "data.csv", encoding: "UTF-8", headers: true ).with_index( 2 ) do |row, row_number|
  puts row_number # Starts at row 2, which is the first row after the header row.
end

Ruby 2.6では$INPUT_LINE_NUMBER、現在の行番号は表示されなくなりました。さらに悪いことに、との値が返さ2れます1。それが何を表すのかはわかりませんが、行番号ではありません。例外が発生しないため、その値をチェックしていない場合は、実際に問題が発生する可能性があります。この落とし穴を避けるために、コード内のすべての出現箇所を置き換えることを強くお勧めします。$INPUT_LINE_NUMBER

于 2020-02-09T19:55:36.167 に答える