Có bất kỳ đá quý nào có thể phân tích cú pháp các tệp XLS và XLSX không? Tôi đã tìm thấy Bảng tính và ParseExcel, nhưng cả hai đều không hiểu định dạng XLSX.
Câu trả lời:
Chỉ cần tìm thấy roo , điều đó có thể thực hiện công việc - phù hợp với yêu cầu của tôi, đọc một bảng tính cơ bản.
Spreadsheet
bằng cách yêu cầu bảng tính ruby.
Gần đây tôi cần phân tích cú pháp một số tệp Excel bằng Ruby. Sự phong phú của các thư viện và các tùy chọn trở nên khó hiểu, vì vậy tôi đã viết một bài đăng trên blog về nó.
Đây là bảng các thư viện Ruby khác nhau và những gì chúng hỗ trợ:
Nếu bạn quan tâm đến hiệu suất, đây là cách các xlsx
thư viện so sánh:
Tôi có mã mẫu để đọc các tệp xlsx với từng thư viện được hỗ trợ tại đây
Dưới đây là một số ví dụ để đọc xlsx
tệp với một số thư viện khác nhau:
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
Lạch nhỏ
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
Dưới đây là một ví dụ về cách đọc xls
tệp kế thừa bằng spreadsheet
thư viện:
bảng tính
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
Các roo đá quý hoạt động tuyệt vời cho Excel (.xls và .xlsx) và nó đang được tích cực phát triển.
Tôi đồng ý rằng cú pháp không tuyệt vời và cũng không giống như ruby. Nhưng điều đó có thể dễ dàng đạt được với những thứ như:
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
Đổi tên lớp thành một cái gì đó như "Roobook" giải quyết vấn đề này. Tuy nhiên, công việc tuyệt vời!
Tôi đang sử dụng creek sử dụng nokogiri. Nó nhanh. Đã sử dụng 8,3 giây trên bảng xlsx 21x11250 trên Macbook Air của tôi. Làm cho nó hoạt động trên ruby 1.9.3+. Định dạng đầu ra cho mỗi hàng là một hàm băm của hàng và tên cột cho nội dung ô: {"A1" => "một ô", "B1" => "ô khác"} Hàm băm không đảm bảo rằng các khóa sẽ nằm trong thứ tự cột ban đầu. https://github.com/pythonicrubyist/creek
xỉnard là một trong những tuyệt vời khác sử dụng nokogiri. Nó là siêu nhanh. Đã sử dụng 6,7 giây trên bảng xlsx 21x11250 trên Macbook Air của tôi. Làm cho nó hoạt động trên ruby 2.0.0+. Định dạng đầu ra cho mỗi hàng là một mảng: ["một ô", "ô khác"] https://github.com/thirtyseven/dullard
simple_xlsx_reader đã được đề cập là rất tốt, hơi chậm. Đã sử dụng 91 giây trên bảng xlsx 21x11250 trên Macbook Air của tôi. Làm cho nó hoạt động trên ruby 1.9.3+. Định dạng đầu ra cho mỗi hàng là một mảng: ["một ô", "một ô khác"] https://github.com/woahdae/simple_xlsx_reader
Một điều thú vị khác là oxcelix. Nó sử dụng trình phân tích cú pháp SAX của ox được cho là nhanh hơn cả trình phân tích cú pháp DOM và SAX của nokogiri. Nó được cho là xuất ra một Ma trận. Tôi không thể làm cho nó hoạt động. Ngoài ra, có một số vấn đề phụ thuộc với rubyzip. Không muốn giới thiệu nó.
Kết luận, creek có vẻ là một lựa chọn tốt. Các bài đăng khác đề xuất simple_xlsx_parser vì nó có hiệu suất tương tự.
Đã xóa dấu mờ theo khuyến nghị vì nó đã lỗi thời và mọi người đang gặp lỗi / gặp sự cố với nó.
dullard
có đầy lỗi đối với tôi (với dữ liệu không phải la tinh). creek
đã đưa những gì tôi cần
Nếu bạn đang tìm kiếm các thư viện hiện đại hơn, hãy xem Bảng tính: http://spreadsheet.rubyforge.org/GUIDE_txt.html . Tôi không thể biết liệu nó có hỗ trợ tệp XLSX hay không, nhưng xem xét rằng nó được phát triển tích cực, tôi đoán nó có (tôi không sử dụng Windows hoặc với Office, vì vậy tôi không thể kiểm tra).
Tại thời điểm này, có vẻ như roo lại là một lựa chọn tốt. Nó hỗ trợ XLSX, cho phép (một số) lặp lại chỉ bằng cách sử dụng times
với quyền truy cập ô. Tôi thừa nhận, nó không đẹp.
Ngoài ra, RubyXL hiện có thể cung cấp cho bạn một loại lặp lại bằng extract_data
phương pháp của họ , cung cấp cho bạn một mảng dữ liệu 2d, có thể dễ dàng lặp lại.
Ngoài ra, nếu bạn đang cố gắng làm việc với các tệp XLSX trên Windows, bạn có thể sử dụng thư viện Win32OLE của Ruby cho phép bạn giao diện với các đối tượng OLE, như các đối tượng được cung cấp bởi Word và Excel. Tuy nhiên , như @PanagiotisKanavos đã đề cập trong các nhận xét, điều này có một số nhược điểm lớn:
Nhưng nếu bạn chọn sử dụng nó, bạn có thể chọn không hiển thị Excel, tải tệp XLSX của bạn và truy cập thông qua nó. Tôi không chắc liệu nó có hỗ trợ lặp lại hay không, tuy nhiên, tôi không nghĩ rằng sẽ quá khó để xây dựng xung quanh các phương thức được cung cấp, vì nó là API Microsoft OLE đầy đủ cho Excel. Đây là tài liệu: http://support.microsoft.com/kb/222101 Đây là viên ngọc: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html
Một lần nữa, các tùy chọn trông không tốt hơn nhiều, nhưng tôi e rằng không có nhiều thứ khác ngoài đó. thật khó để phân tích cú pháp một định dạng tệp là một hộp đen. Và số ít những người đã phá vỡ nó đã không làm điều đó một cách rõ ràng. Google Documents là mã nguồn đóng và LibreOffice là hàng nghìn dòng harry C ++.
extract_data
với RubyXL.
Tôi đã làm việc tích cực với cả Spreadsheet và rubyXL trong vài tuần qua và tôi phải nói rằng cả hai đều là những công cụ tuyệt vời. Tuy nhiên, một lĩnh vực mà cả hai đều mắc phải là thiếu các ví dụ về việc thực sự triển khai bất cứ điều gì hữu ích. Hiện tại, tôi đang xây dựng trình thu thập thông tin và sử dụng rubyXL để phân tích cú pháp tệp xlsx và Bảng tính cho mọi thứ xls. Tôi hy vọng đoạn mã dưới đây có thể là một ví dụ hữu ích và cho thấy những công cụ này có thể hiệu quả như thế nào.
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"
Các rubyXL đá quý phân tích XLSX đẹp.
Tôi không thể tìm thấy trình phân tích cú pháp xlsx ưng ý. RubyXL không thực hiện đánh máy ngày tháng, Roo đã cố gắng nhập một số làm ngày tháng, và cả hai đều là một mớ hỗn độn cả về api và mã.
Vì vậy, tôi đã viết simple_xlsx_reader . Tuy nhiên, bạn sẽ phải sử dụng thứ gì đó khác cho xls, vì vậy có thể đó không phải là câu trả lời đầy đủ mà bạn đang tìm kiếm.
Hầu hết các ví dụ trực tuyến bao gồm trang web của tác giả cho viên ngọc Bảng tính chứng minh việc đọc toàn bộ nội dung của tệp Excel vào RAM. Điều đó tốt nếu bảng tính của bạn nhỏ.
xls = Spreadsheet.open(file_path)
Đối với bất kỳ ai làm việc với các tệp rất lớn, cách tốt hơn là đọc trực tuyến nội dung của tệp. Đá quý Bảng tính hỗ trợ điều này - mặc dù không được ghi chép đầy đủ vào thời điểm này (khoảng 3/2015).
Spreadsheet.open(file_path).worksheets.first.rows do |row|
# do something with the array of CSV data
end
Các thư viện RemoteTable sử dụng Roo trong nội bộ. Nó giúp bạn dễ dàng đọc bảng tính ở các định dạng khác nhau (XLS, XLSX, CSV, v.v. có thể từ xa, có thể được lưu trữ bên trong zip, gz, v.v.):
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
Đầu ra:
{"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"}