Есть ли гемы, способные анализировать файлы XLS и XLSX? Я нашел Spreadsheet и ParseExcel, но они оба не понимают формат XLSX.
Разбор файлов XLS и XLSX (MS Excel) с помощью Ruby?
Ответ 1
Просто найден roo, который может выполнять работу - работает для моих требований, читая базовую электронную таблицу.
Ответ 2
Мне недавно нужно было разбирать некоторые файлы Excel с помощью Ruby. Обилие библиотек и опций оказалось запутанным, поэтому я написал сообщение в блоге об этом.
Вот таблица из разных библиотек Ruby и то, что они поддерживают:
Если вы заботитесь о производительности, вот как сравниваются библиотеки xlsx
:
У меня есть пример кода для чтения файлов 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
РОО
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
Ответ 3
roo gem отлично подходит для 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
Ответ 4
Я использую ручей, который использует нокогири. Это быстро. Использовал 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, который, предположительно, быстрее, чем nokogiri DOM и SAX парсер. Якобы выводит матрицу. Я не мог заставить его работать. Также были некоторые проблемы с зависимостью от rubyzip. Не рекомендовал бы это.
В заключение, ручей кажется хорошим выбором. Другие посты рекомендуют simple_xlsx_parser, поскольку он имеет аналогичную производительность.
Удален тупица как рекомендовано, так как он устарел, и люди получают ошибки/проблемы с ним.
Ответ 5
Если вы ищете более современные библиотеки, взгляните на таблицу: http://spreadsheet.rubyforge.org/GUIDE_txt.html. Я не могу сказать, поддерживает ли он файлы XLSX, но, учитывая, что он активно развивается, я предполагаю, что это так (я не в Windows или с Office, поэтому я не могу проверить).
На данный момент это выглядит как roo. Он поддерживает XLSX, разрешает (некоторую) итерацию, просто используя times
с доступом к ячейке. Я признаю, что это не очень хорошо.
Кроме того, RubyXL теперь может дать вам своего рода итерацию с помощью метода extract_data
, который дает вам 2d-массив данных, который можно легко повторить.
В качестве альтернативы, если вы пытаетесь работать с файлами XLSX в Windows, вы можете использовать библиотеку Ruby Win32OLE, которая позволяет вам взаимодействовать с объектами OLE, такими как те, которые предоставляются Word и Excel. Однако, как упоминает в комментариях @PanagiotisKanavos, это имеет несколько серьезных недостатков:
- Необходимо установить Excel
- Для каждого документа запускается новый экземпляр Excel.
- Потребление памяти и других ресурсов намного больше, чем то, что необходимо для простой обработки документов 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 - тысячи строк harry С++.
Ответ 6
rubyXL gem прекрасно разбирает файлы XLSX.
Ответ 7
Я занимался сильной работой как с таблицами, так и с 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"
Ответ 8
Я не смог найти удовлетворительный синтаксический анализатор xlsx. RubyXL не делает тип даты, Roo попытался придумать число как дату, и оба являются беспорядком как в api, так и в коде.
Итак, я написал simple_xlsx_reader. Вы должны использовать что-то еще для xls, хотя, возможно, это не полный ответ, который вы ищете.
Ответ 9
Большинство онлайн-примеров, включая веб-сайт автора для драгоценного камня Spreadsheet, демонстрируют чтение всего содержимого файла Excel в ОЗУ. Это прекрасно, если ваша таблица небольшая.
xls = Spreadsheet.open(file_path)
Для тех, кто работает с очень большими файлами, лучший способ - потоковое чтение содержимого файла. Драгоценный камень таблицы поддерживает это - хотя и не хорошо документированный в это время (около 3/2015).
Spreadsheet.open(file_path).worksheets.first.rows do |row|
# do something with the array of CSV data
end
Ответ 10
библиотека 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"}