Чи є якісь камені, здатні проаналізувати файли XLS та XLSX? Я знайшов електронну таблицю та ParseExcel, але вони обидва не розуміють формат XLSX.
Відповіді:
Щойно знайшов ру , який міг би виконати цю роботу - працює за моїми вимогами, читаючи базову таблицю.
Spreadsheet, вимагаючи таблиці ruby.
Нещодавно мені потрібно було проаналізувати деякі файли 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
РОО камінь відмінно працює для 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
Spreadsheet.class # => ModuleПерейменування класу на щось на зразок "Roobook" вирішує цю проблему. Однак чудова робота!
Я використовую струмок, який використовує нокогірі. Це швидко. Використовував 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
Ще один цікавий - окселікс. Він використовує синтаксичний аналізатор SAX від ox, який нібито швидший за аналізатор DOM і SAX від nokogiri. Він нібито виводить матрицю. Я не міг змусити його працювати. Крім того, були деякі проблеми залежності з rubyzip. Не рекомендував би.
На закінчення, струмок здається хорошим вибором. Інші публікації рекомендують simple_xlsx_parser, оскільки він має подібну продуктивність.
Видалено тупий за рекомендацією, оскільки він застарів, і люди отримують помилки / проблеми з ним.
dullardбув повний помилок для мене (з нелатинськими даними). creekдав те, що мені потрібно
Якщо ви шукаєте сучасніші бібліотеки, перегляньте таблицю: http://spreadsheet.rubyforge.org/GUIDE_txt.html . Я не можу сказати, чи підтримує він файли XLSX, але, враховуючи, що він активно розробляється, я здогадуюсь, що він підтримує (я не в Windows або в Office, тому не можу перевірити).
На даний момент, схоже, ру - це знову хороший варіант. Він підтримує XLSX, дозволяє (деякі) ітерації, просто використовуючи timesз доступом до стільникового зв'язку. Зізнаюся, це не красиво.
Крім того, RubyXL тепер може дати вам своєрідну ітерацію, використовуючи їх extract_dataметод, який дає вам 2d масив даних, який можна легко повторити.
Крім того, якщо ви намагаєтесь працювати з файлами XLSX у Windows, ви можете скористатися бібліотекою Win32OLE від Ruby, яка дозволяє взаємодіяти з OLE-об'єктами, такими як ті, що надаються Word та Excel. Однак , як згадував @PanagiotisKanavos у коментарях, це має кілька основних недоліків:
Але якщо ви вирішите використовувати його, ви можете не показувати Excel, завантажувати свій файл XLSX і отримувати до нього доступ через нього. Я не впевнений, чи підтримує вона ітерацію, однак, я не думаю, що було б надто складно обійти надані методи, оскільки це повний API OLE Microsoft для Excel. Ось документація: http://support.microsoft.com/kb/222101 Ось самоцвіт: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html
Знову ж таки, варіанти виглядають не набагато кращими, але там, на жаль, багато іншого немає. важко проаналізувати формат файлу, який є чорною рамкою. І ті небагатьом, хто зумів це зламати, зробили не так помітно. Документи Google закриті, а LibreOffice - це тисячі рядків Гаррі С ++.
extract_dataхочеться спробувати з RubyXL.
Останні кілька тижнів я активно працюю з електронною таблицею та 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"
RubyXL камінь розбирає XLSX файли красиво.
Я не зміг знайти задовільний парсер xlsx. RubyXL не робить датування тексту, Ру намагався ввести номер як дату, і обидва вони є хаосом як в api, так і в коді.
Отже, я написав simple_xlsx_reader . Однак вам потрібно було б використовувати щось інше для xls, тому, можливо, це не повна відповідь, яку ви шукаєте.
Більшість онлайн-прикладів, включаючи веб-сайт автора електронної таблиці електронних таблиць, демонструють читання всього вмісту файлу 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
Бібліотека 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"}