【问题标题】:Using Roo with Ruby(Rails) to parse Excel使用 Roo 和 Ruby(Rails) 解析 Excel
【发布时间】:2017-04-25 23:56:55
【问题描述】:

我正在尝试允许用户上传 CSV/Excel 文档,并使用 Roo(我见过的最推荐的一个)对其进行解析,但我在弄清楚它时遇到了一些问题。

当前脚本

require 'roo'

xlsx = Roo::Excelx.new("./TestAppXL.xlsx")
xlsx.each_row_streaming do |row|
  puts row.inspect # Array of Excelx::Cell objects
end

这是我唯一能够得到工作的 - 它返回看起来是 JSONB 的内容。

我正在尝试做的是几个部分过程:

A) 用户将“卡片”列表上传到我的网站。(尝试允许尽可能多的选项。CSV、Excel 等)

B) 它立即返回标题列表并询问“哪个标题是名称、数量等?”

C) 我解析数据以获取特定标头并执行“X”。

B 是我主要需要帮助的地方。我正在努力弄清楚 Roo 到底是什么。我无法控制标题,所以我不能使用数字列号。

(添加 Rails 标记,因为这最终将在控制器中,也许是更简单的方法。)

更新脚本

我实际上已经取得了很大的进步。仍在努力接近我最初的要求。

require 'roo'
require 'roo-xls'

xlsx = Roo::Spreadsheet.open('Demo.xls')
headers =  xlsx.first_row
puts xlsx.row(headers)
puts "Which number header is the Card Name?"
CardName = gets
puts xlsx.column(CardName.to_i)
# => Returns basic info about the spreadsheet file

在获取时需要更多逻辑,但目前如果我输入“3”,它将返回“CardName”列的所有内容。现在正在对行进行迭代。

伪工作脚本

require 'roo'
require 'roo-xls'

xlsx = Roo::Spreadsheet.open('Demo.xls')
headers =  xlsx.first_row
puts xlsx.row(headers)
puts "Which number header is the Card Name?"
CardName = gets.to_i
specHeader = xlsx.cell(headers,CardName)
xlsx.column(CardName).drop(0).each_with_index do |item, index|
    if index == 0

    else
        puts item
    end
end

这实际上按预期执行,我现在可以开始将文件输入 Rake 作业。仍在进行一些迭代,但这非常接近。

【问题讨论】:

  • 无法控制标题?你不能要求用户命名标题名称和数量等吗?对他们来说会更容易,而且你肯定会遵守这个约定
  • @peter 可能代表我措辞不佳。我可以要求他们专门输入“CardName”,但我宁愿接受xlsx.row(1) 并返回选择,他们会明确告诉我“哪个是哪个”,或者在我这边输入逻辑,所以如果它像“名字” ' 可能是CardName
  • 如果我在 Excel 中导入文本文件,我总是不喜欢这样,也不要指望成为第 1 行的标题行,我在第 2 到第 5 行得到很多标题,通常有一个通用标题带有标题。我可以给你举个例子,用正确的标题名称阅读,但不是让他们选择,你确实意识到你的例子中的 put 和 get 不适用于 Rails 站点?
  • @peter 任何例子都很好。在我进行研究时,我开始更倾向于强迫他们匹配 -my- Header 名称要求。 .first_row 是 Roo 的助手,它实际上进入了正确的第一行。在这个演示文件中,我的标题行在第 5 行(专门用来测试它)

标签: ruby-on-rails ruby excel csv


【解决方案1】:

我为您提供了一种通用方法,可以根据一些标题名称从 Roo 电子表格中提取数据,这将是您的上传者使用的约定。

require 'roo'
require 'roo-xls'

xlsx = Roo::Spreadsheet.open('Demo.xls')
first_row = xlsx.first_row
headers = ['CardName', 'Item']
headers.each{|h|Kernel.const_set(h, xlsx.row(first_row).index{|e| e =~ /#{h}/i})}
begin
  xlsx.drop(first_row).each do |row|
    p [row[CardName], row[Item]]
  end
rescue
  # the required headers are not all present
end

我想唯一需要解释的就是headers.each{|h|Kernel.const_set(h, xlsx.row(first_row).index{|e| e =~ /#{h}/i})}

对于每个 headername 使用 const_set 为其分配它在 xlsx.row(first_row) (我们的 headerrow)中的索引,其中正则表达式 /h/i 返回一个索引,h 周围的 #{} 是将 h 扩展为它的值,'CardName' 在第一种情况下,/h/i 末尾的 i 表示要忽略这种情况,因此常量 CardName 被分配标题行中字符串 CardName 的索引。 您可以使用 const_get 检查是否所有必需的常量都存在,而不是相当笨拙的开始救援结构,并对其采取行动,而不是捕获错误。

编辑

而不是p [row[CardName], row[Item]],您可以检查并执行任何操作,但请记住,如果这将成为 Rails 或其他网站的一部分,则与用户的交互将比您的 put 和 get 示例更棘手.比如像

headers = ['CardName', 'Item', 'Condition', 'Collection']
...
xlsx.drop(first_row).each do |row|
  if row[CardName].nil? || row[Item].nil?
    # let the user know or skip
  else
    condition, collection = row[Condition], row[Collection]
    # and do something with it
  end
end

【讨论】:

  • 哇,这真的很有趣。 .index 行绝对让我失望,但我有点明白它在做什么。类似于 .each |item, index|我以前做过 - 嗯,类似的结果。如果有“Y”标题,我怎么能修改这个,所以我做“X”?例如:'CardName' 和 'Count' 是 100% 必需的,但是像 'Condition' 'Collection' 等这样的行是可选的,但我可能想要?如果你能给我一个独特的例子,我可以扩展它。
  • 确实在我的答案中添加了一些内容,不确定是否是您的意思,否则请详细说明,另请参阅我所说的检查每个标题是否存在
  • 啊,真的。非常感谢您的额外解释!我必须努力将它移到控制器中,但 Ruby 版本运行良好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
  • 1970-01-01
相关资源
最近更新 更多