Ruby CSV - 获取当前行/行号

Posted

技术标签:

【中文标题】Ruby CSV - 获取当前行/行号【英文标题】:Ruby CSV - get current line/row number 【发布时间】:2012-09-06 14:04:46 【问题描述】:

我正在尝试研究如何从 Ruby CSV 获取当前行/行号。这是我的代码:

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

但这似乎没有按预期工作。有没有办法做到这一点?

【问题讨论】:

【参考方案1】:

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 不再提供当前行号。更糟糕的是它返回的值是21。我不确定那应该代表什么,但它肯定不是行号。由于它不会引发异常,因此如果您不检查该值,它真的会咬您一口。 我强烈建议您替换代码中出现的所有 $INPUT_LINE_NUMBER 以避免此问题。

【讨论】:

【参考方案2】:

由于当前 Rubies 中 CSV 的变化,我们需要进行一些更改。在 Ruby 2.6 之前的原始解决方案的答案中进一步了解。以及使用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 有一个magic variable $.,这是正在读取的当前文件的行号:

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

使用 Enumerator 的 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"

【讨论】:

这在 rspec 中产生了意想不到的结果 这不是为了在 rspec 中工作而编写的。它适用于常规 Ruby。 仅供参考,如果您使用 headers: true 选项,第一行 将返回 2 与其说“不要使用这个”,请在单独的答案中解释原因。帮助教育。 使用$.的问题在于它是行号,它并不总是反映行号,因为一个单元格可以包含多行.【参考方案3】:

这里有一个替代解决方案:

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

CSV.foreach("data.csv", options).with_index do |row, i|
   puts i
end

【讨论】:

是的,这很有效并且很干净。但它需要一次将整个 CVS 内容读入 RAM。这就是read 所做的。 根据 CSV 文档,read 实际上 slurps 中的数据。http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV.html 你想说,“啜饮”在某种程度上意味着懒惰阅读?我不信。 read 的结果是 Array,据我所知,在 Ruby 中无法延迟数组构建。 你应该使用with_index,而不是each_with_indexeach_with_index 需要 Ruby 加载整个文件。 更新了示例以改用with_index【参考方案4】:

不是一个干净但简单的解决方案

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

【讨论】:

据我所知,这是获取行号而不是行号的唯一方法但是,如果:headers => true,您应该将i 偏移1。 使用 with_index 会是一个更干净、更像 Ruby 的解决方案。

以上是关于Ruby CSV - 获取当前行/行号的主要内容,如果未能解决你的问题,请参考以下文章

easyui datagrid处于编辑状态时获取当前行或者列

微信小程序如何获取当前行号

PHP CSV,如何从当前行获取前三行和后三行

获取当前行号?

awk

TStringGrid - 有没有一种简单的方法来获取“当前/选定”行?