Phân tích cú pháp các tệp XLS và XLSX (MS Excel) với Ruby?


76

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:


56

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.


12
roo chắc chắn hoạt động, nhưng nó không giống Ruby một cách bực bội và (đối với tôi, dù sao) rất đáng ngạc nhiên: không thể lặp qua các hàng bằng cách sử dụng mỗi? không thể lặp lại trên các trang tính? một khái niệm về "trang tính mặc định" theo sau bởi quyền truy cập vào các ô thông qua đối tượng sổ làm việc?
M. Anthony Aiello

7
Tôi đã mất một lúc để tìm, nhưng ngã ba roo hiện chính thức này , mà bạn phải ghim rõ ràng, đã khắc phục những phàn nàn của tôi về roo. Nó có #each, #to_a, quyền truy cập trang tính hợp lý và không gây ô nhiễm không gian tên chung Spreadsheetbằng cách yêu cầu bảng tính ruby.
Woahdae

@woahdae Tuyệt vời! Thật tuyệt khi xem một ví dụ với những tính năng mới này. Có tài liệu nào không? Tôi đặc biệt quan tâm đến việc có thể lặp lại qua từng hàng của mỗi trang tính của sổ làm việc.
Anconia

README của fork đó có một phần bổ sung về những gì mới trong fork. Tuy nhiên, sau khi thực hiện tải lên xlsx yêu cầu đánh máy tốt, tôi thấy rằng việc định kiểu roo có nhiều điều mong muốn. Nó bị nghẹn khi cố gắng phân tích cú pháp "2" (được định dạng là một số) làm ngày. Tôi đã viết trình phân tích cú pháp của riêng mình mà tôi thích hơn nhiều, tôi sẽ tải nó lên github tối nay và liên hệ lại với bạn.
Woahdae

@woahdae Thứ tốt. Mong được nhìn thấy tác phẩm của bạn. Vui lòng gửi liên kết khi bạn có thể.
Anconia

119

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ợ:

nhập mô tả hình ảnh ở đây

Nếu bạn quan tâm đến hiệu suất, đây là cách các xlsxthư viện so sánh: nhập mô tả hình ảnh ở đây

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 xlsxtệ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 xlstệp kế thừa bằng spreadsheetthư 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

1
Đây là một bài đăng tuyệt vời và tôi đã ủng hộ, nhưng không may là tôi thấy rằng cả roo và bảng tính đều không hoạt động với dữ liệu .xls của tôi.
guero64

2
Thnks @ guero64 Chức năng xls cho roo thực sự được giữ trong một dự án khác có tên là roo-xls github.com/roo-rb/roo-xls . Bạn đã thử thư viện đó?
mattnedrich 14/07/17

4
Tôi đã tìm thấy vấn đề. Nguồn tạo tệp đang lưu chúng dưới dạng .xls, nhưng nội dung là HTML. Cảm ơn bạn đã đóng góp ý kiến.
guero64

Bạn có tìm thấy bất kỳ cái nào đã làm một công việc hợp lý với các tên được xác định là phạm vi không? Ví dụ với openpyxl: gist.github.com/empiricalthought/...
Steven Huwig

Công việc so sánh tuyệt vời! Tôi đang cố gắng sử dụng roo và tôi đã đi lang thang nếu bạn quản lý để trích xuất nhận xét từ một ô và làm thế nào để đạt được điều này với chức năng được cung cấp?
user1185081

44

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

2
Cẩn thận với quy ước đặt tên này - Bảng tính là một hằng số hiện có đề cập đến một mô-đun: 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!
Anconia

2
Roo mới nhất (trên empact fork mà bạn trỏ đến) không gây ô nhiễm không gian tên và đi kèm với #each và những thứ tương tự. Cuối cùng! yay empact.
Woahdae

1
Roo gem rất khủng với các tệp lớn. Mở tệp XLSx 5MB có thể mất 30-60 giây, điều này không có ý nghĩa gì.
Yura Omelchuk

1
Roo mất nhiều thời gian vì phải tải mọi thứ vào bộ nhớ. Nó cũng dường như phân tích bảng tính thành một cấu trúc dữ liệu có thể sử dụng được, điều này có thể chậm.
Carlosfocker

Xin vui lòng ở đâu trong một dự án đường ray có thể giữ một tệp như vậy @Bruno Buccolo.
wokoro douye samuel

26

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ó.


2
Bài này nên số một
Carlosfocker

2
Cảm ơn bạn đã chia sẻ. Tôi thấy rằng truyền trực tuyến 100K + hàng từ tệp XLSX nhanh chóng và bộ nhớ hiệu quả bằng cách sử dụng đá quý Dullard.
Scarver

1
dullardcó đầ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
okliv

okliv, sẽ thật tuyệt nếu bạn có thể chỉ định bộ sắp chữ nào không hoạt động với xỉn ở đây. Ngoài ra, hãy chụp một bài đăng lên trình theo dõi vấn đề ngu ngốc trên github! :)
the_minted

1
Câu trả lời này chỉ đề cập đến việc đọc các tệp 'xlsx', còn các tệp 'xls' thì sao?
anshul410

7

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 timesvớ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_dataphươ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:

  • Excel phải được cài đặt
  • Một phiên bản Excel mới được bắt đầu cho mỗi tài liệu
  • Tiêu thụ bộ nhớ và tài nguyên khác nhiều hơn mức cần thiết để thao tác tài liệu XLSX đơn giả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 ++.


Thông tin rất hữu ích! Tôi hiện đang xây dựng một trình thu thập thông tin excel và thấy phù hợp với nó ( stackoverflow.com/questions/14044357/… ). Tôi đã từ bỏ roo vì việc lặp đi lặp lại khá đau đớn. Tuy nhiên, tôi rất nóng lòng muốn thử extract_datavới RubyXL.
Anconia

2
Sử dụng OLE cho XLSX là một ý tưởng tồi - XLSX chỉ là XML được nén với một định dạng nổi tiếng .. Nó chắc chắn không phải là một hộp đen - định dạng Open XML được xác định rất rõ ràng, Open XML SDK cung cấp tất cả thông tin cần thiết để tạo XML bằng tay có rất nhiều thư viện giúp đơn giản hóa việc làm việc với XLSX.
Panagiotis Kanavos

@PanagiotisKanavos: Điểm thú vị. Mặc dù tôi chắc chắn thấy tại sao điều đó sẽ tốt hơn, nhưng có lý do nào (một lần nữa, vì tò mò) tại sao việc sử dụng OLE lại tệ như vậy? Tôi đã không sử dụng hoặc phát triển Windows trong một vài năm, vì vậy tôi có thể thiếu một cái gì đó rõ ràng.
Linuxios

Những gì bạn gọi là OLE là giao diện Tự động hóa cho Excel - nó yêu cầu Excel phải được cài đặt trên máy chủ và thực sự khởi động nó cho mỗi yêu cầu tệp. Nó chậm - mọi cuộc gọi đều là cuộc gọi ngoài quy trình tới Excel. Nó cũng nguy hiểm, vì quên đóng một phiên bản có nghĩa là một phiên bản Excel sẽ nằm trong bộ nhớ. Điều này có thể nhanh chóng ngốn tài nguyên của máy chủ. Trên thực tế, XLSX được tạo ra để bất kỳ ứng dụng nào cũng có thể tạo tệp Excel hợp lệ mà không yêu cầu Excel trên máy chủ. Các chi phí là tối thiểu - nó chỉ là xử lý XML
Panagiotis Kanavos

@PanagiotisKanavos: Đúng. Tôi quên rằng OLE giống IPC hơn là một thư viện. Cảm ơn! Tôi sẽ thêm ghi chú vào câu trả lời.
Linuxios

6

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"

Làm cách nào để lặp lại các hàng ?? Khả thi?
some_other_guy

4

Các rubyXL đá quý phân tích XLSX đẹp.


6
rubyXL (như roo, ở trên) cũng trở nên kỳ lạ khi bạn thực sự truy cập dữ liệu trong trang tính. Có điều gì cơ bản về mô hình dữ liệu cho một bảng tính mà việc lặp qua các hàng và cột không thể được cung cấp một cách đơn giản không?
M. Anthony Aiello

4
RubyXL là một mớ hỗn độn. Tôi không khuyên bạn nên nó.
benzado

3

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.


mong được thực hiện điều này. Hy vọng sẽ thấy nhiều tính năng hơn trên đường. Khởi đầu tuyệt vời!
Anconia

3

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

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


Bạn sẽ cần phải thêm .each nếu bạn muốn một cái gì đó xảy ra
peter

2

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"}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.