Разбор файлов XLS и XLSX (MS Excel) с помощью Ruby? - PullRequest
70 голосов
/ 23 июля 2010

Есть ли гемы, способные анализировать файлы XLS и XLSX?Я нашел Spreadsheet и ParseExcel, но они оба не понимают формат XLSX.

Ответы [ 10 ]

89 голосов
/ 22 марта 2017

Мне недавно нужно было проанализировать некоторые файлы Excel с Ruby.Обилие библиотек и опций оказалось непонятным, поэтому я написал об этом сообщение в блоге .

Вот таблица различных библиотек Ruby и их поддержка:

enter image description here

Если вы заботитесь о производительности, вот как сравниваются библиотеки xlsx: enter image description here

У меня естьПример кода для чтения файлов xlsx с каждой поддерживаемой библиотекой здесь

Вот несколько примеров для чтения xlsx файлов с некоторыми различными библиотеками:

rubyXL

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

roo

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
53 голосов
/ 19 октября 2010

Только что нашел roo , который мог бы выполнить эту работу - работает для моих требований, читая основную электронную таблицу.

42 голосов
/ 27 декабря 2012

Gem roo прекрасно работает для Excel (.xls и .xlsx) и активно развивается.

Я согласен, что синтаксис не велик и не похож на рубин. Но это может быть легко достигнуто с помощью чего-то вроде:

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
25 голосов
/ 05 января 2014

Я использую ручей, который использует нокогири. Это быстро. Использовал 8,3 секунды на столе 21x11250 xlsx на моем Macbook Air. Получил работать на ruby ​​1.9.3+. Формат вывода для каждой строки - это хэш имени строки и столбца для содержимого ячейки: {"A1" => "ячейка", "B1" => "другая ячейка"} Хеш не гарантирует, что ключи будут в исходном порядке столбцов. https://github.com/pythonicrubyist/creek

Дуллард это еще один великий, который использует нокогири. Это супер быстро. Использовал 6,7 секунды на столе 21x11250 xlsx на моем Macbook Air. Получил работать на ruby ​​2.0.0+. Выходной формат для каждой строки представляет собой массив: ["клетка", "другая клетка"] https://github.com/thirtyseven/dullard

simple_xlsx_reader, о котором упоминалось, великолепно, немного медленно. Использовал 91 секунду на столе 21x11250 xlsx на моем Macbook Air. Получил работать на ruby ​​1.9.3+. Выходной формат для каждой строки представляет собой массив: ["клетка", "другая клетка"] https://github.com/woahdae/simple_xlsx_reader

Еще один интересный - это Oxcelix. Он использует парсер SAX от Ox, который предположительно быстрее, чем DOM и SAX парсер nokogiri. Он предположительно выводит матрицу. Я не мог заставить его работать. Кроме того, были некоторые проблемы с зависимостью с rubyzip. Не рекомендую.

В заключение, ручей кажется хорошим выбором. Другие посты рекомендуют simple_xlsx_parser, поскольку он имеет аналогичную производительность.

Удален тупой в соответствии с рекомендациями, поскольку он устарел, и у людей появляются ошибки / возникают проблемы с ним.

7 голосов
/ 25 декабря 2012

Если вы ищете более современные библиотеки, взгляните на электронную таблицу: http://spreadsheet.rubyforge.org/GUIDE_txt.html. Я не могу сказать, поддерживает ли она файлы XLSX, но, учитывая, что она активно разрабатывается, я предполагаю, что это так(Я не на Windows или Office, поэтому я не могу проверить).

На данный момент похоже, что roo снова хороший вариант.Он поддерживает XLSX, допускает (некоторые) итерации, просто используя times с доступом к ячейке.Я признаю, что это не очень красиво.

Кроме того, RubyXL теперь может дать вам своего рода итерацию, используя их метод extract_data, который дает вам двумерный массив данных, который можно легко повторить.

В качестве альтернативы, если вы пытаетесь работать с файлами XLSX в Windows, вы можете использовать библиотеку Ruby Win32OLE, которая позволяет вам взаимодействовать с объектами OLE, такими как Word и Excel. Однако , как @PanagiotisKanavos упомянул в комментариях, это имеет несколько основных недостатков:

  • Необходимо установить Excel *
  • Новый экземпляр Excel запускается для каждогоdocument
  • Потребление памяти и других ресурсов намного больше, чем необходимо для простой манипуляции с документом XLSX.

Но если вы решите использовать его, вы можете не отображать Excel,загрузите файл XLSX и получите к нему доступ через него.Я не уверен, поддерживает ли он итерацию, однако, я не думаю, что было бы слишком сложно построить вокруг предоставленных методов, так как это полный Microsoft OLE API для Excel.Вот документация: http://support.microsoft.com/kb/222101 Вот гем: http://www.ruby -doc.org / stdlib-1.9.3 / libdoc / win32ole / rdoc / WIN32OLE.html

Сноваварианты выглядят не намного лучше, но, боюсь, не так уж много иного.трудно разобрать формат файла, который является черным ящиком.И те немногие, кому удалось это сломать, не сделали этого так заметно.Документы Google являются закрытым исходным кодом, а LibreOffice - тысячи строк Гарри C ++.

5 голосов
/ 28 декабря 2012

Последние пару недель я много работал с Spreadsheet и rubyXL, и должен сказать, что оба они являются отличными инструментами. Тем не менее, одной из областей, которой страдают оба, является отсутствие примеров того, как на самом деле реализовать что-либо полезное. В настоящее время я создаю сканер и использую rubyXL для разбора файлов xlsx и электронных таблиц для чего-либо 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"
4 голосов
/ 14 октября 2011

Gem rubyXL прекрасно разбирает файлы XLSX.

3 голосов
/ 24 марта 2015

Большинство онлайн-примеров, включая сайт автора для гема Spreadsheet, демонстрируют чтение всего содержимого файла Excel в RAM.Это нормально, если ваша электронная таблица небольшая.

xls = Spreadsheet.open(file_path)

Для тех, кто работает с очень большими файлами, лучшим способом будет потоковое чтение содержимого файла.Gem Spreadsheet поддерживает это - хотя пока не очень хорошо документировано (около 3/2015).

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

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

3 голосов
/ 16 января 2013

Я не смог найти удовлетворительного парсера xlsx.RubyXL не выполняет типизацию даты, Roo пытался типизировать число как дату, и оба являются беспорядком и в API, и в коде.

Итак, я написал simple_xlsx_reader .Вы должны будете использовать что-то еще для xls, поэтому, возможно, это не полный ответ, который вы ищете.

2 голосов
/ 01 июля 2013

Библиотека RemoteTable использует 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"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...