I have a MS Excel spreadsheet which does some complex computations. I'd like to create a script which will create a CSV file with the results obtained from the spreadsheet.
I could rewrite the logic from the spreadsheet in my programming language (for example Ruby, but I'm open to use a different language), but then I would have to update my code whenever someone changes the logic in the spreadsheet. Is it possible to use a MS Excel spreadsheet as a black box, a computation engine, which can be invoked from my code? Then I would only have write the CSV part and input data download in my code, the whole computation logic could stay in the spreadsheet and could be easily updated.
Ideally, I don't want to add any CSV generation or data download code to the spreadsheet, because it's used by domain-experts (not programmers). Additionally, I have to download some data from the Internet and pass it to the spreadsheet as the input values. I'd like to keep that part of the code externally, in a version control system like Git. One additional note is that the spreadsheet uses the Solver Excel plugin.
Any help how to do that would be very appreciated.
Thanks,
Michal
To manipulate an Excel spreadsheet using Ruby, you may want to use win32ole
Here's a sample script:
data = [["Hello", "World"]]
# Require the WIN32OLE library
require 'win32ole'
# Create an instance of the Excel application object
xl = WIN32OLE.new('Excel.Application')
# Make Excel visible
xl.Visible = 1
# Add a new Workbook object
wb = xl.Workbooks.Add
# Get the first Worksheet
ws = wb.Worksheets(1)
# Set the name of the worksheet tab
ws.Name = 'Sample Worksheet'
# For each row in the data set
data.each_with_index do |row, r|
# For each field in the row
row.each_with_index do |field, c|
# Write the data to the Worksheet
ws.Cells(r+1, c+1).Value = field.to_s
end
end
# Save the workbook
wb.SaveAs('workbook.xls')
# Close the workbook
wb.Close
# Quit Excel
xl.Quit
To work out more complicated code, just record a macro of what you want to do, and then look at the code of your macro, and convert it from VB into Ruby.
Related
I am trying to access the VBA method 'RGB' via Ruby:
The problem is this method belongs to the 'VBA' library not the 'Excel' library. To connect to the Excel library I would do something like this:
require 'win32ole'
xl = WIN32OLE.new('Excel.Application') # => opens Excel
xl.visible = true # => show it
#wb = xl.Workbooks.open(excel_file) # => adds a workbook
#ws = #wb.Worksheets(sheet) # => open specific sheet
But I do not know how to connect to the VBA library. I've tried:
require 'win32ole'
vba = WIN32OLE.new('VBA.Application')
vba = WIN32OLE.new('VBA')
Even better would be if I could somehow programmatically generate a list of all OLE libraries on my computer.
I don't know much about the WIN320LE thing, but if I were you, I'd create a vba script, and run that script from the shell via ruby, then pipe the output from that script into ruby as a string.
Not sure if this helps, just a generic pattern
I had been using spreadsheet to read in a template excel file, modify it and output a new file for the end-user.
As far as I can identify from the documentation spreadsheet provides no way to input or edit formulas in the produced document.
However, the purpose of my script is to read an undefined number of items from a site and enter them into the spreadsheet, then calculate totals and subtotals.
The end user (using excel or libreoffice etc) is then able to make slight modifications to the quantity of items whilst the totals update (due to formulas) as they are accustomed.
I have looked into the writeexcel gem which claims to be able to input formulas, but I can't see how to take an existing template file and modify it to produce my output. I can only create fresh workbooks.
Any tips please? I do not want to use Win32OLE.
This is surprisingly difficult; apparently all Gems for handling Excel files are missing some crucial functionality.
I can think of two approaches for this problem:
use a combination of spreadsheet (to read the Excel file) and use writeexcel (to write the output file)
use an input file that already contains the required formulas on a separate "formula" sheet and copies the formulas to the "real" sheet
Here's a simplistic version of the second approach:
require 'rubygems'
require 'spreadsheet'
Dir.chdir(File.dirname(__FILE__))
# input file, contains this data
# Sheet0: headers + data (for this simple demo, we will generate the data on-the-fly)
# Sheet1: Formula '=SUM(Worksheet1.A2:A255) in cell A1
book = Spreadsheet.open 'in.xls'
sheet = book.worksheet 0
formulasheet = book.worksheet 1
# insert some input data (in a real application,
# this data would already be present in the input sheet)
rows = rand(20) + 1
(1..rows).each do |i|
sheet[i,0] = i
end
# add total at bottom of column C
sheet[rows+1,2] = formulasheet[0,0]
# write output file
book.write 'out.xls'
However, this will fail if
you're using the same column for your input data and your totals (since then, the total will try to include itself in the calculation)
I have to read data from a spread sheet modify some rows and then write the updated rows / cells into the same file.
I have used Spreadsheet gem with Ruby 2.0.0.
When I write the results back to the same file, I am unable to open the xls any more. I get an error
"File Format is not Valid"
in MS Excel.
When the updates are written onto a different file, I am able to open the file but it is in protected view. Is there a solution to this issue?
Below is the sample code:
require 'rubygems'
require 'spreadsheet'
book = Spreadsheet::open('filePath')
sheet = book.worksheet 0
## have application logic in here
book.write('filePath')
I've worked with this problem a few times and they've had the issue on log for around a year now.
The first problem is that it locks the file when spreadsheet loads it and there is no clear way to close it the only way I've been able to get it to not lock is with this code block. It opens it and stores the first worksheet off into its own variable then closes the file.
worksheet = nil
Spreadsheet.open workbook_name do |inner_book|
worksheet = inner_book.worksheet 0
end
worksheet
If you want all the worksheets you could do something similar. In addition to the file opening closing/problem you have the issue around capturing the content of the worksheet depending on the format. I know for my purposes I end up doing the following to capture the content. This sadly loses any formatting you might have had in the source spreadsheet.
rows = []
worksheet.each do |row|
rows << row
end
You can then make your own workbook/sheet and iterate through the rows and add them to the new sheet/book. Then save the new book with the same file name.
Its not fun or efficient, but it is a way to go about solving the problem. Hope this helped.
check your file extension.
spreadsheet, writeexcel..etc gems seem couldn't work with xlsx files.
try .xls not .xlsx
I need to read an Excel sheet(.xls) values with Ruby using query. Is there any gems available in ruby to do this? If so please help me on this.
Any tips or advice on this would be great.
Thanks
Anto
You can use Sequel and OLEDB to read Excel Files:
require 'sequel'
Encoding.default_external = 'utf-8' #needed for umlauts in excel
def read_excel(source)
source = File.expand_path(source) #Full path needed
db = Sequel.ado(:conn_string=>"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=#{source};Extended Properties=Excel 8.0;")
# Excel 2000 (for table names, use a dollar after the sheet name, e.g. Sheet1$)
p db.test_connection
dataset = db[:'Tabelle1$']
p dataset
dataset.each{|row|
puts row
}
end #test_read
read_excel('my_spreadsheet.xls')
You should know the name of the tab (in my example it's Tabelle1)
The 'real' solution here is not Sequel, but the ADO-Interface. I'm not familiar with other ORM, so I may not really help you. But you may check for example active record.
There are hints, how to connect MS-Access or sqlserver via ADO, some use ActiveRecord.
If you replace the connection string with the Excel-String in my Sequel example, then you may use other ORMs.
You may also try to read Excel-Data via an ODBC-connection.
Read data from excel file using spreadsheet gem
require 'spreadsheet'
doc = Spreadsheet.open('simple.xls')
sheet = doc.worksheet(0) # list number, first list is 0 and so on...
val = sheet2[r,c] # read particular cell from list 0, r for row, c for column
Some information is there.
More information on the net, just use Google.
Is there a way to read Excel 97-2003 files from Ruby?
Background
I'm currently using the Ruby Gem parseexcel -- http://raa.ruby-lang.org/project/parseexcel/
But it is an old port of the perl module. It works fine, but the latest format it parses is Excel 95. And guess what? Excel 2007 will not produce the Excel 95 format.
John McNamara has taken over duties as the maintainer for the Perl Excel parser, see http://metacpan.org/pod/Spreadsheet::ParseExcel The current version will parse Excel 95-2003 files. But is there a port to Ruby?
My other thought is to build some Ruby to Perl glue code to enable use of the Perl library itself from Ruby. Eg, see What's the best way to export UTF8 data into Excel?
(I think it would be much faster to write the glue code than to port the parser.)
Thanks,
Larry
I'm using spreadsheet, give it a shot.
There is also roo:
http://roo.rubyforge.org/
In my experience spreadsheet works much faster than roo, however roo can support the .xlsx format which spreadsheet cannot.
As khell mentioned, spreadsheet is a great tool. See my code below that I used to build a crawler.
require 'find'
require 'spreadsheet'
Spreadsheet.client_encoding = 'UTF-8'
count = 0
Find.find('/Users/toor/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} pieces of information were found"
I've not tried to parse Excel files before, but I know FasterCSV is a great library for parsing CSV files (which Excel can produce).
In the case that you are Windows,
you can always use WIN32OLE.
Have a look at http://rubyonwindows.blogspot.com/search/label/excel