时间:2010-07-23 18:00:44

标签: ruby excel

10 个答案:

答案 0 :(得分:87)

我最近需要用Ruby解析一些Excel文件。丰富的库和选项变得令人困惑,所以我写了一篇关于它的blog post

以下是不同Ruby库及其支持的表格:

enter image description here

如果您关心性能,xlsx库的比较方式如下: enter image description here

我有示例代码来读取每个支持的库here

的xlsx文件

以下是使用一些不同库读取xlsx文件的一些示例:

<强> ruby​​XL

require 'rubyXL'

workbook = RubyXL::Parser.parse './sample_excel_files/xlsx_500_rows.xlsx'
worksheets = workbook.worksheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.sheet_name}"
  num_rows = 0
  worksheet.each do |row|
    row_cells = row.cells.map{ |cell| cell.value }
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

<强>袋鼠

require 'roo'

workbook = Roo::Spreadsheet.open './sample_excel_files/xlsx_500_rows.xlsx'
worksheets = workbook.sheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet}"
  num_rows = 0
  workbook.sheet(worksheet).each_row_streaming do |row|
    row_cells = row.map { |cell| cell.value }
    num_rows += 1
  end
  puts "Read #{num_rows} rows" 
end

<强>小溪

require 'creek'

workbook = Creek::Book.new './sample_excel_files/xlsx_500_rows.xlsx'
worksheets = workbook.sheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.name}"
  num_rows = 0
  worksheet.rows.each do |row|
    row_cells = row.values
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

<强> simple_xlsx_reader

require 'simple_xlsx_reader'

workbook = SimpleXlsxReader.open './sample_excel_files/xlsx_500000_rows.xlsx'
worksheets = workbook.sheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.name}"
  num_rows = 0
  worksheet.rows.each do |row|
    row_cells = row
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

以下是使用xls库阅读旧版spreadsheet文件的示例:

<强>电子表格

require 'spreadsheet'

# Note: spreadsheet only supports .xls files (not .xlsx)
workbook = Spreadsheet.open './sample_excel_files/xls_500_rows.xls'
worksheets = workbook.worksheets
puts "Found #{worksheets.count} worksheets"

worksheets.each do |worksheet|
  puts "Reading: #{worksheet.name}"
  num_rows = 0
  worksheet.rows.each do |row|
    row_cells = row.to_a.map{ |v| v.methods.include?(:value) ? v.value : v }
    num_rows += 1
  end
  puts "Read #{num_rows} rows"
end

答案 1 :(得分:53)

刚刚找到roo,可以完成这项工作 - 符合我的要求,阅读基本电子表格。

答案 2 :(得分:42)

roo gem适用于Excel(.xls和.xlsx),并且正在积极开发。

我同意语法不是很好,也不像ruby。但这可以通过以下方式轻松实现:

class Spreadsheet
  def initialize(file_path)
    @xls = Roo::Spreadsheet.open(file_path)
  end

  def each_sheet
    @xls.sheets.each do |sheet|
      @xls.default_sheet = sheet
      yield sheet
    end
  end

  def each_row
    0.upto(@xls.last_row) do |index|
      yield @xls.row(index)
    end
  end

  def each_column
    0.upto(@xls.last_column) do |index|
      yield @xls.column(index)
    end
  end
end

答案 3 :(得分:25)

我正在使用使用nokogiri的小溪。它很快。在我的Macbook Air上的21x11250 xlsx表上使用8.3秒。得到它在ruby 1.9.3+上工作。每行的输出格式是行和列名称到单元格内容的哈希: {“A1”=&gt;“一个单元格”,“B1”=&gt;“另一个单元格”} 哈希不保证密钥将按原始列顺序排列。 https://github.com/pythonicrubyist/creek

dullard是另一个使用nokogiri的伟大人物。它超级快。在Macbook Air上的21x11250 xlsx表上使用6.7秒。得到它在ruby 2.0.0+上工作。每行的输出格式是一个数组: [“一个细胞”,“另一个细胞”] https://github.com/thirtyseven/dullard

simple_xlsx_reader已经提到过很棒,有点慢。在Macbook Air上的21x11250 xlsx表上使用了91秒。得到它在ruby 1.9.3+上工作。每行的输出格式是一个数组: [“一个细胞”,“另一个细胞”] https://github.com/woahdae/simple_xlsx_reader

另一个有趣的是oxcelix。它使用ox的SAX解析器,据说比nokogiri的DOM和SAX解析器都要快。它应该输出一个矩阵。我无法让它发挥作用。此外,rubyzip存在一些依赖性问题。不推荐它。

总之,小溪似乎是一个不错的选择。其他帖子推荐simple_xlsx_parser,因为它具有相似的性能。

删除了dullard,因为它已经过时,人们会遇到错误/遇到问题。

答案 4 :(得分:7)

如果您正在寻找更多现代化的图书馆,请查看电子表格:http://spreadsheet.rubyforge.org/GUIDE_txt.html。 我不知道它是否支持XLSX文件,但考虑到它是积极开发的,我猜它确实(我不是在Windows上,或者在Office上,所以我无法测试)。

此时,看起来roo再次成为一个不错的选择。它支持XLSX,只需使用times进行单元访问即可进行(某些)迭代。我承认,它并不漂亮。

此外,RubyXL现在可以使用他们的extract_data方法为您提供一种迭代,它为您提供了一个可以轻松迭代的二维数据数组。

或者,如果您尝试在Windows上使用XLSX文件,则可以使用Ruby的Win32OLE库,该库允许您与OLE对象(如Word和Excel提供的对象)进行交互。 然而,正如@PanagiotisKanavos在评论中提到的,这有一些主要的缺点:

  • 必须安装Excel
  • 为每个文档启动一个新的Excel实例
  • 内存和其他资源消耗远远超过简单XLSX文档操作所需的内容。

但是如果您选择使用它,您可以选择不显示Excel,加载XLSX文件并通过它访问它。我不确定它是否支持迭代,但是,我认为构建所提供的方法并不太难,因为它是用于Excel的完整Microsoft OLE API。 这是文档:http://support.microsoft.com/kb/222101 这是宝石:http://www.ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html

同样,选项看起来并没有那么好,但是我没有其他的东西,我很害怕。很难解析一个黑盒子的文件格式。那些设法打破它的人并没有明显地做到这一点。 Google Docs是封闭源代码,LibreOffice是数千行harry C ++。

答案 5 :(得分:5)

过去几周我一直在使用Spreadsheet和rubyXL,我必须说两者都是很棒的工具。然而,一个受到影响的领域是缺乏实际实施任何有用的实例。目前我正在构建一个爬虫并使用rubyXL解析xlsx文件和Spreadsheet以获取任何xls。我希望下面的代码可以作为一个有用的示例,并展示这些工具的有效性。

require 'find'
require 'rubyXL'

count = 0

Find.find('/Users/Anconia/crawler/') do |file|             # begin iteration of each file of a specified directory
  if file =~ /\b.xlsx$\b/                                  # check if file is xlsx format
    workbook = RubyXL::Parser.parse(file).worksheets       # creates an object containing all worksheets of an excel workbook
    workbook.each do |worksheet|                           # begin iteration over each worksheet
      data = worksheet.extract_data.to_s                   # extract data of a given worksheet - must be converted to a string in order to match a regex
      if data =~ /regex/
        puts file
        count += 1
      end      
    end
  end
end

puts "#{count} files were found"

require 'find'
require 'spreadsheet'
Spreadsheet.client_encoding = 'UTF-8'

count = 0

Find.find('/Users/Anconia/crawler/') do |file|             # begin iteration of each file of a specified directory
  if file =~ /\b.xls$\b/                                   # check if a given file is xls format
    workbook =  Spreadsheet.open(file).worksheets          # creates an object containing all worksheets of an excel workbook
    workbook.each do |worksheet|                           # begin iteration over each worksheet
      worksheet.each do |row|                              # begin iteration over each row of a worksheet
        if row.to_s =~ /regex/                             # rows must be converted to strings in order to match the regex
          puts file
          count += 1
        end
      end
    end
  end
end

puts "#{count} files were found"

答案 6 :(得分:4)

rubyXL gem可以很好地解析XLSX文件。

答案 7 :(得分:3)

我找不到令人满意的xlsx解析器。 RubyXL没有做日期类型转换,Roo试图将一个数字作为日期进行类型转换,并且在api和代码中都是混乱。

所以,我写了simple_xlsx_reader。但是,你必须为xls使用别的东西,所以也许这不是你正在寻找的完整答案。

答案 8 :(得分:3)

大多数在线示例(包括作者对Spreadsheet gem的网站)都展示了将Excel文件的全部内容读入RAM。如果你的电子表格很小,这很好。

xls = Spreadsheet.open(file_path)

对于使用非常大的文件的任何人,更好的方法是流式读取文件的内容。 Spreadsheet gem支持这一点 - 虽然此时没有详细记录(大约在2015年3月)。

Spreadsheet.open(file_path).worksheets.first.rows do |row|
  # do something with the array of CSV data
end

CITE:https://github.com/zdavatz/spreadsheet

答案 9 :(得分:2)

RemoteTable library在内部使用roo。它可以轻松阅读不同格式的电子表格(XLS,XLSX,CSV等可能是远程的,可能存储在zip,gz等中):

require 'remote_table'
r = RemoteTable.new 'http://www.fueleconomy.gov/FEG/epadata/02data.zip', :filename => 'guide_jan28.xls'
r.each do |row|
  puts row.inspect
end

输出:

{"Class"=>"TWO SEATERS", "Manufacturer"=>"ACURA", "carline name"=>"NSX", "displ"=>"3.0", "cyl"=>"6.0", "trans"=>"Auto(S4)", "drv"=>"R", "bidx"=>"60.0", "cty"=>"17.0", "hwy"=>"24.0", "cmb"=>"20.0", "ucty"=>"19.1342", "uhwy"=>"30.2", "ucmb"=>"22.9121", "fl"=>"P", "G"=>"", "T"=>"", "S"=>"", "2pv"=>"", "2lv"=>"", "4pv"=>"", "4lv"=>"", "hpv"=>"", "hlv"=>"", "fcost"=>"1238.0", "eng dscr"=>"DOHC-VTEC", "trans dscr"=>"2MODE", "vpc"=>"4.0", "cls"=>"1.0"}
{"Class"=>"TWO SEATERS", "Manufacturer"=>"ACURA", "carline name"=>"NSX", "displ"=>"3.2", "cyl"=>"6.0", "trans"=>"Manual(M6)", "drv"=>"R", "bidx"=>"65.0", "cty"=>"17.0", "hwy"=>"24.0", "cmb"=>"19.0", "ucty"=>"18.7", "uhwy"=>"30.4", "ucmb"=>"22.6171", "fl"=>"P", "G"=>"", "T"=>"", "S"=>"", "2pv"=>"", "2lv"=>"", "4pv"=>"", "4lv"=>"", "hpv"=>"", "hlv"=>"", "fcost"=>"1302.0", "eng dscr"=>"DOHC-VTEC", "trans dscr"=>"", "vpc"=>"4.0", "cls"=>"1.0"}
{"Class"=>"TWO SEATERS", "Manufacturer"=>"ASTON MARTIN", "carline name"=>"ASTON MARTIN VANQUISH", "displ"=>"5.9", "cyl"=>"12.0", "trans"=>"Auto(S6)", "drv"=>"R", "bidx"=>"1.0", "cty"=>"12.0", "hwy"=>"19.0", "cmb"=>"14.0", "ucty"=>"13.55", "uhwy"=>"24.7", "ucmb"=>"17.015", "fl"=>"P", "G"=>"G", "T"=>"", "S"=>"", "2pv"=>"", "2lv"=>"", "4pv"=>"", "4lv"=>"", "hpv"=>"", "hlv"=>"", "fcost"=>"1651.0", "eng dscr"=>"GUZZLER", "trans dscr"=>"CLKUP", "vpc"=>"4.0", "cls"=>"1.0"}