Чи є якісь камені, здатні проаналізувати файли 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"}