Gibt es Edelsteine, die XLS- und XLSX-Dateien analysieren können? Ich habe Spreadsheet und ParseExcel gefunden, aber beide verstehen das XLSX-Format nicht.
Antworten:
Spreadsheet
indem es eine Ruby-Tabelle benötigt.
Ich musste kürzlich einige Excel-Dateien mit Ruby analysieren. Die Fülle an Bibliotheken und Optionen erwies sich als verwirrend, deshalb schrieb ich einen Blog-Beitrag darüber.
Hier ist eine Tabelle mit verschiedenen Ruby-Bibliotheken und deren Unterstützung:
Wenn Sie Wert auf Leistung xlsx
legen, vergleichen die Bibliotheken Folgendes:
Ich habe Beispielcode zum Lesen von XLSX-Dateien mit jeder unterstützten Bibliothek hier
Hier einige Beispiele zum Lesen von xlsx
Dateien mit verschiedenen Bibliotheken:
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
Bach
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
Hier ist ein Beispiel für das Lesen einer Legacy- xls
Datei mithilfe der spreadsheet
Bibliothek:
Kalkulationstabelle
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
Das Roo- Juwel eignet sich hervorragend für Excel (.xls und .xlsx) und wird aktiv weiterentwickelt.
Ich bin damit einverstanden, dass die Syntax weder großartig noch rubinartig ist. Aber das kann leicht erreicht werden mit so etwas wie:
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
Umbenennen der Klasse in "Roobook" löst dieses Problem. Tolle Arbeit!
Ich benutze Creek, der Nokiaogiri verwendet. Es ist schnell. Verwendet 8,3 Sekunden auf einem 21x11250 xlsx-Tisch auf meinem Macbook Air. Ich habe es für Ruby 1.9.3+ zum Laufen gebracht. Das Ausgabeformat für jede Zeile ist ein Hash aus Zeilen- und Spaltennamen zum Zelleninhalt: {"A1" => "eine Zelle", "B1" => "eine andere Zelle"} Der Hash übernimmt keine Garantie dafür, dass die Schlüssel vorhanden sind die ursprüngliche Spaltenreihenfolge. https://github.com/pythonicrubyist/creek
Dullard ist ein weiterer großartiger, der Nokogiri verwendet. Es ist super schnell. Verwendet 6,7 Sekunden auf einem 21x11250 xlsx-Tisch auf meinem Macbook Air. Ich habe es für Ruby 2.0.0+ zum Laufen gebracht. Das Ausgabeformat für jede Zeile ist ein Array: ["eine Zelle", "eine andere Zelle"] https://github.com/thirtyseven/dullard
Der erwähnte simple_xlsx_reader ist großartig, ein bisschen langsam. Verwendet 91 Sekunden auf einem 21x11250 xlsx-Tisch auf meinem Macbook Air. Ich habe es für Ruby 1.9.3+ zum Laufen gebracht. Das Ausgabeformat für jede Zeile ist ein Array: ["eine Zelle", "eine andere Zelle"] https://github.com/woahdae/simple_xlsx_reader
Ein weiteres interessantes ist Oxcelix. Es verwendet den SAX-Parser von ox, der angeblich schneller ist als der DOM- und SAX-Parser von Nokiaogiri. Es wird angeblich eine Matrix ausgegeben. Ich konnte es nicht zum Laufen bringen. Außerdem gab es einige Abhängigkeitsprobleme mit Rubyzip. Würde es nicht empfehlen.
Zusammenfassend scheint Creek eine gute Wahl zu sein. Andere Beiträge empfehlen simple_xlsx_parser, da es eine ähnliche Leistung hat.
Dummkopf wie empfohlen entfernt, da er veraltet ist und Leute Fehler bekommen / Probleme damit haben.
dullard
war voller Fehler für mich (mit nicht lateinischen Daten). creek
gab, was ich brauche
Wenn Sie nach moderneren Bibliotheken suchen, werfen Sie einen Blick auf die Tabelle: http://spreadsheet.rubyforge.org/GUIDE_txt.html . Ich kann nicht sagen, ob es XLSX-Dateien unterstützt, aber wenn man bedenkt, dass es aktiv entwickelt wird, schätze ich, dass dies der Fall ist (ich bin nicht unter Windows oder mit Office, daher kann ich nicht testen).
An diesem Punkt sieht es so aus, als wäre Roo wieder eine gute Option. Es unterstützt XLSX und ermöglicht (einige) Iterationen, indem es nur times
mit Zellenzugriff verwendet wird. Ich gebe zu, es ist aber nicht schön.
Außerdem kann RubyXL Ihnen jetzt mithilfe seiner extract_data
Methode eine Art Iteration geben , die Ihnen ein 2D- Datenarray liefert, über das Sie leicht iterieren können.
Wenn Sie versuchen, mit XLSX-Dateien unter Windows zu arbeiten, können Sie alternativ die Win32OLE-Bibliothek von Ruby verwenden, mit der Sie eine Schnittstelle zu OLE-Objekten herstellen können, wie sie von Word und Excel bereitgestellt werden. Wie @PanagiotisKanavos in den Kommentaren erwähnt hat, hat dies jedoch einige Hauptnachteile:
Wenn Sie es jedoch verwenden möchten, können Sie festlegen, dass Excel nicht angezeigt wird, dass Sie Ihre XLSX-Datei laden und über diese Datei darauf zugreifen. Ich bin mir nicht sicher, ob es Iteration unterstützt, aber ich denke nicht, dass es zu schwierig wäre, um die bereitgestellten Methoden herum aufzubauen, da es sich um die vollständige Microsoft OLE-API für Excel handelt. Hier ist die Dokumentation: http://support.microsoft.com/kb/222101 Hier ist das Juwel: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html
Auch hier sehen die Optionen nicht viel besser aus, aber ich fürchte, es gibt nicht viel anderes da draußen. Es ist schwierig, ein Dateiformat zu analysieren, das eine Black Box ist. Und die wenigen, die es geschafft haben, es zu brechen, haben es nicht so sichtbar gemacht. Google Text & Tabellen ist eine geschlossene Quelle, und LibreOffice besteht aus Tausenden von Zeilen von Harry C ++.
extract_data
mit RubyXL zu versuchen .
Ich habe in den letzten Wochen intensiv mit Spreadsheet und rubyXL gearbeitet und ich muss sagen, dass beide großartige Tools sind. Ein Bereich, unter dem beide leiden, ist das Fehlen von Beispielen für die tatsächliche Implementierung von nützlichen Dingen. Derzeit baue ich einen Crawler und verwende rubyXL, um xlsx-Dateien und Tabellenkalkulationen für alle xls zu analysieren. Ich hoffe, der folgende Code kann als hilfreiches Beispiel dienen und zeigen, wie effektiv diese Tools sein können.
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"
Das rubyXL- Juwel analysiert XLSX-Dateien auf wundervolle Weise.
Ich konnte keinen zufriedenstellenden xlsx-Parser finden. RubyXL führt keine Datums-Typumwandlung durch, Roo hat versucht, eine Zahl als Datum zu typisieren, und beide sind sowohl in der API als auch im Code ein Chaos.
Also habe ich simple_xlsx_reader geschrieben . Sie müssten jedoch etwas anderes für xls verwenden, sodass es möglicherweise nicht die vollständige Antwort ist, nach der Sie suchen.
Die meisten Online-Beispiele, einschließlich der Website des Autors für das Spreadsheet-Juwel, zeigen das Lesen des gesamten Inhalts einer Excel-Datei in den Arbeitsspeicher. Das ist in Ordnung, wenn Ihre Tabelle klein ist.
xls = Spreadsheet.open(file_path)
Für alle, die mit sehr großen Dateien arbeiten, ist es besser, den Inhalt der Datei per Stream zu lesen . Das Spreadsheet-Juwel unterstützt dies - wenn auch derzeit nicht gut dokumentiert (ca. 3/2015).
Spreadsheet.open(file_path).worksheets.first.rows do |row|
# do something with the array of CSV data
end
Zitieren: https://github.com/zdavatz/spreadsheet
Die RemoteTable-Bibliothek verwendet Roo intern. Es erleichtert das Lesen von Tabellenkalkulationen in verschiedenen Formaten (XLS, XLSX, CSV usw., möglicherweise entfernt, möglicherweise in einem Zip, GZ usw. gespeichert):
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
Ausgabe:
{"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"}