Розбір файлів XLS та XLSX (MS Excel) за допомогою Ruby?


76

Чи є якісь камені, здатні проаналізувати файли XLS та XLSX? Я знайшов електронну таблицю та ParseExcel, але вони обидва не розуміють формат XLSX.

Відповіді:


56

Щойно знайшов ру , який міг би виконати цю роботу - працює за моїми вимогами, читаючи базову таблицю.


12
roo, безумовно, працює, але це засмучує не схоже на Ruby і (як би там не було) дуже дивно: неможливість перебирати рядки, використовуючи кожен? неможливість перебору аркушів? поняття "аркуша за замовчуванням" з подальшим доступом до комірок через об'єкт книги?
M. Anthony Aiello

7
Мені знадобився час, щоб знайти, але ця офіційна fork-форка , яку ви повинні чітко закріпити, виправляє мої скарги щодо roo. Він має #each, #to_a, розумний доступ до аркушів, і не забруднює глобальний простір імен Spreadsheet, вимагаючи таблиці ruby.
Woahdae

@woahdae Чудово! Було б чудово побачити приклад із цими новими функціями. Чи є якась документація? Мене особливо цікавить можливість перегляду кожного рядка кожного аркуша книги.
Анконія

У README цієї форки є додатковий розділ про те, що нового у форці. Однак, здійснивши завантаження xlsx, що вимагає хорошого текстового тестування, я виявив, що руографічне текстове тестування мало бажати. Він подавився, намагаючись проаналізувати "2" (відформатований як число) як дату. Я написав власний синтаксичний аналізатор, який мені подобається набагато більше, завантажу його сьогодні у github і зв’яжусь з вами.
Woahdae

@woahdae Хороші речі. Будемо раді побачити вашу роботу. Будь ласка, надішліть посилання, коли зможете.
Анконія,

119

Нещодавно мені потрібно було проаналізувати деякі файли 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

1
Це був чудовий допис, і я підтримав його, але, на жаль, я виявив, що ні roo, ні електронна таблиця не працювали з моїми даними .xls.
guero64

2
Thnks @ guero64 Функціональність xls для roo насправді зберігається в іншому проекті, який називається roo-xls github.com/roo-rb/roo-xls . Ви пробували цю бібліотеку?
mattnedrich

4
Я знайшов проблему. Джерело, яке генерувало файли, зберігало їх як .xls, але вміст - HTML. Дякую за ваш внесок.
guero64

Ви знайшли когось, хто виконував розумну роботу з визначеними назвами як діапазони? Наприклад, з openpyxl: gist.github.com/empiricalthought/…
Стівен Гувіг,

Чудова робота порівняння! Я намагаюся використовувати roo, і я блукав, чи вдалося вам витягти коментарі з комірки, і як цього досягти за допомогою наданої функції?
user1185081

44

РОО камінь відмінно працює для 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

2
Обережно з цією умовою іменування - Електронна таблиця - це існуюча константа, яка посилається на модуль: Spreadsheet.class # => ModuleПерейменування класу на щось на зразок "Roobook" вирішує цю проблему. Однак чудова робота!
Анконія

2
Останнє ру (на розгалуженні empact, на яке ви вказуєте) не забруднює простір імен і поставляється з #each та подібними. Нарешті! yay empact.
Woahdae

1
Roo gem страшний з великими файлами. Відкриття 5 МБ файлу XLSx може зайняти 30-60 секунд, що просто не має сенсу.
Юра Омельчук

1
Roo займає багато часу, тому що він повинен завантажити все в пам'ять. Здається, це також аналізує таблицю на корисну структуру даних, яка може бути повільною.
Carlosfocker

Будь ласка, де в проекті rails можна зберігати такий файл @Bruno Buccolo.
wokoro douye Samuel

26

Я використовую струмок, який використовує нокогірі. Це швидко. Використовував 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, оскільки він має подібну продуктивність.

Видалено тупий за рекомендацією, оскільки він застарів, і люди отримують помилки / проблеми з ним.


2
Цей пост повинен бути номер один
Carlosfocker

2
Дякую, що поділився. Я виявив, що потокове передавання 100K + рядків із файлу XLSX відбувається швидко та ефективно, використовуючи дорогоцінний камінь Dullard.
scarver2

1
dullardбув повний помилок для мене (з нелатинськими даними). creekдав те, що мені потрібно
okliv

okliv, було б чудово, якби ви могли вказати, які набори тут не працюють з dullard. Крім того, зніміть допис до відстежувача тупих випусків на github! :)
the_minted

1
У цій відповіді згадується лише читання файлів "xlsx", а як щодо файлів "xlsx"?
anshul410

7

Якщо ви шукаєте сучасніші бібліотеки, перегляньте таблицю: 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
  • Для кожного документа запускається новий екземпляр Excel
  • Споживання пам'яті та інших ресурсів є набагато більшим, ніж те, що необхідно для простого маніпулювання документами XLSX.

Але якщо ви вирішите використовувати його, ви можете не показувати 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 - це тисячі рядків Гаррі С ++.


Дуже корисна інформація! В даний час я будую сканер Excel і маю відповідність з ним ( stackoverflow.com/questions/14044357/… ). Я відмовився від ру, оскільки ітерація досить болюча. Однак мені дуже extract_dataхочеться спробувати з RubyXL.
Анконія,

2
Використання OLE для XLSX - погана ідея - XLSX - це просто стислий XML із добре відомим форматом .. Це точно не чорний ящик - формат Open XML дуже чітко визначений, Open XML SDK надає всю інформацію, необхідну для створення XML вручну, і існує безліч бібліотек, які значно спрощують роботу з XLSX.
Панайотис Канавос

@PanagiotisKanavos: Цікавий момент. Хоча я, звичайно, розумію, чому це було б краще, чи є якась причина (знову ж таки, з цікавості), чому використання OLE так погано? Я не використовував і не розробляв Windows протягом декількох років, тому, можливо, мені не вистачає чогось очевидного.
Linuxios

Те, що ви називаєте OLE, - це інтерфейс автоматизації для Excel - він вимагає встановлення Excel на сервері і фактично запускає його для кожного запиту файлу. Це повільно - кожен дзвінок є позапроцесним викликом у Excel. Це також небезпечно, оскільки забуття закрити екземпляр означає, що екземпляр Excel залишиться в пам'яті. Це може швидко з’їсти ресурси сервера. Насправді XLSX було створено для того, щоб будь-яка програма могла створити дійсний файл Excel, не вимагаючи Excel на сервері. Накладні витрати мінімальні - це просто обробка XML
Панайотис Канавос

@PanagiotisKanavos: Так. Я забув, що OLE - це більше IPC, ніж бібліотека. Дякую! Додаю примітку до відповіді.
Linuxios

6

Останні кілька тижнів я активно працюю з електронною таблицею та 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"

Як перебирати рядки ?? Можливо?
some_other_guy

4

RubyXL камінь розбирає XLSX файли красиво.


6
rubyXL (як roo, вище) також стає дивним, коли ви насправді отримуєте доступ до даних на аркуші. Чи є щось основне в моделі даних для електронної таблиці, що ітерація рядків і стовпців не може бути просто надана?
M. Anthony Aiello

4
RubyXL - безлад. Не рекомендую.
benzado

3

Я не зміг знайти задовільний парсер xlsx. RubyXL не робить датування тексту, Ру намагався ввести номер як дату, і обидва вони є хаосом як в api, так і в коді.

Отже, я написав simple_xlsx_reader . Однак вам потрібно було б використовувати щось інше для xls, тому, можливо, це не повна відповідь, яку ви шукаєте.


з нетерпінням чекаю цього Сподіваюся побачити більше можливостей у майбутньому. Чудовий старт!
Анконія,

3

Більшість онлайн-прикладів, включаючи веб-сайт автора електронної таблиці електронних таблиць, демонструють читання всього вмісту файлу 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

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


Вам потрібно буде додати .each, якщо ви хочете, щоб щось сталося
пітер

2

Бібліотека 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"}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.