Home » excel » Writing Excel FIle through Erlang

Writing Excel FIle through Erlang

Posted by: admin April 23, 2020 Leave a comment

Questions:

i am trying to write excel file through erlang. I used the following code to write the excel file

-module(excel).
-export([start/1]).

start(Val)->
        case  file:open("office-test.xls",[append]) of
        {ok,Fd} -> io:format(" file created"),
                io:fwrite(Fd,"~p\t~p\t~p~n", ["Name","Date","Number"]),
                export(Fd,Val),
                file:close(Fd);
        {error,_} ->  io:format("~nerror in creation of file")
        end.


export(_,0)->
        ok;

export(Fd,Val) ->
        io:fwrite(Fd, "~p\t~p\t~p\t~n" ,["B123","2012/10/11 12:12:12","val"++integer_to_list(Val)]),
        export(Fd,Val-1).

It was able to write successfully but when i open in LibreOffice. I got up a pop-up window asking the data seperated by. I dont want the end user to work on it.

1) Is there any way such that the office(ms office or libre office) will automatically parse it.??

2) Is there any other way to write the excel sheets through erlang..??

How to&Answers:

you have to write a CSV , Comma delimited text file. You would have to save it with .csv file extension. You write to this file line, by line. make sure that each line ends with \r\n. This file can be read very well from excel.

You make sure that the headings appear on the first line, like this:

Name,Sex,Project\r\n
Joe Armstrong,Male,Erlang\r\n
Klacke Wickstrom,Male,Yaws\r\n
Rusty R,Male,Nitrogen\r\n
Bill Gates,Male,\r\n
Muzaaya Joshua,Male,ZeePay\r\n

Also, the file encoding matters. ANSI encoding is better. You can as well process Excel files in Erlang by first converting/re-saving the file as .csv , comma delimited file using excel.
Then use this csv file parser module

%%% --- csv parser in Erlang. ------
%%% To help process large csv files without loading them into
%%% memory. Similar to the xml parsing technique of SAX
-module(csv). -compile(export_all).
parse(FilePath,ForEachLine,Opaque)-> case file:open(FilePath,[read]) of {_,S} -> start_parsing(S,ForEachLine,Opaque); Error -> Error end.

start_parsing(S,ForEachLine,Opaque)-> Line = io:get_line(S,''),
case Line of eof -> {ok,Opaque}; "\n" -> start_parsing(S,ForEachLine,Opaque); "\r\n" -> start_parsing(S,ForEachLine,Opaque); _ -> NewOpaque = ForEachLine(scanner(clean(clean(Line,10),13)),Opaque), start_parsing(S,ForEachLine,NewOpaque) end.
scan(InitString,Char,[Head|Buffer]) when Head == Char -> {lists:reverse(InitString),Buffer}; scan(InitString,Char,[Head|Buffer]) when Head =/= Char -> scan([Head|InitString],Char,Buffer); scan(X,_,Buffer) when Buffer == [] -> {done,lists:reverse(X)}. scanner(Text)-> lists:reverse(traverse_text(Text,[])).
traverse_text(Text,Buff)-> case scan("",$,,Text) of {done,SomeText}-> [SomeText|Buff]; {Value,Rem}-> traverse_text(Rem,[Value|Buff]) end.
clean(Text,Char)-> string:strip(string:strip(Text,right,Char),left,Char).

How to use this module to parse csv files from Excel. An example of our simple csv file above, in shell

C:\Windows\System32>erl
Eshell V5.9  (abort with ^G)
1> ForEachLine = fun(Line,Buffer)-> io:format("Line: ~p~n",[Line]),Buffer end.
#Fun<erl_eval.12.111823515>
2> InitialBuffer = [].
[]
3> csv:parse("E:/erlang_projects.csv",ForEachLine,InitialBuffer).
Line: ["Name","Sex","Project"]
Line: ["Joe Armstrong","Male","Erlang"]
Line: ["Klacke Wickstrom","Male","Yaws"]
Line: ["Rusty R","Male","Nitrogen"]
Line: ["Bill Gates","Male",[]]
Line: ["Muzaaya Joshua","Male","ZeePay"]
{ok,[]}
4> ForEachLine2 = fun(Line,Buffer)-> io:format("Line: ~p~n",[Line]),[Line|Buffer] end.
#Fun<erl_eval.12.111823515>
5> csv:parse("E:/erlang_projects.csv",ForEachLine2,InitialBuffer).
Line: ["Name","Sex","Project"]
Line: ["Joe Armstrong","Male","Erlang"]
Line: ["Klacke Wickstrom","Male","Yaws"]
Line: ["Rusty R","Male","Nitrogen"]
Line: ["Bill Gates","Male",[]]
Line: ["Muzaaya Joshua","Male","ZeePay"]
{ok,[["Muzaaya Joshua","Male","ZeePay"],
     ["Bill Gates","Male",[]],
     ["Rusty R","Male","Nitrogen"],
     ["Klacke Wickstrom","Male","Yaws"],
     ["Joe Armstrong","Male","Erlang"],
     ["Name","Sex","Project"]]}
6>

So you can use this module later on to parse your csv files from Excel as well. Now, just learn how to write a csv file line by line, read the pragmatic Erlang Programming Book in the files chapter, or the erlang documentation.

Answer:

The following information from the following link, it create excel data directly and is satisfied with your requirement:

http://www.erlang.org/documentation/doc-5.0.1/lib/comet-1.0/doc/html/ch_examples.html

3 Examples

Detailed examples on how to use Comet
3.1 Comet examples

This chapter describes in detail som examples on Comet usage; the simpler ones first and the most advanced last.

Four examples are given:

Browsing to a specified address

Opening Excel, dumping some data, showing a graph

Calling a function in a C++ library

Source code for these are included in the distribution, in the directory comet/examples .

The abbreviations VB and VBA are used for Visual Basic and Visual Basic for Applications.
3.2 Requirements

The first example requires that Internet Explorer 4.0 or later is installed.

Example two requires Excel from Office 97 or Office 2000.

The last example can be run as it is, but to modify the COM-library, Visual C++ 5.0 or later is required.
3.3 Example one, opening a browser to a specific URL

This example shows how to open a browser (Internet Explorer), and navigate through it to a specific address.

To get the COM interface for the browser, we use a tool such as OLE/COM Object Viewer, which is included in Microsoft’s Windows Platform SDK, Visual C and Visual Basic.

Checking the interface for Internet Explorer, we find a couple of things that we need. First, we need the class ID. Then we need the name and parameter list of the funcions and properties required to create and use a browser.

Since starting a browser is not a performance-critical task, we can use the slowest and safest way to do it from Erlang. This means starting the erl_com as a port process, and using the IDispatch interface to access Internet Explorer.

Although Internet Explorer provides a dual interface, (that is an interface with both a method table and an IDispatch-interface), the IDispatch interface is safer and slower. Giving it a bad parameter list, returns an error code, rather than a core dump.

To use a COM object, we have to start the server (which starts the port) and start a thread. Then we can create the object, and do what we want with it.

To be able to use constants, we put the source in a module, rather than call it interactively in the Erlang shell.

-module(win_browse).

-include("erl_com.hrl").

-export([open/1, example/0]).
open(Url) ->
    {ok, Pid}= erl_com:start_process(),
    T= erl_com:new_thread(Pid),
    Obj= erl_com:create_dispatch(T, "InternetExplorer.Application", 
                                 ?CLSCTX_LOCAL_SERVER),
    erl_com:invoke(Obj, "Navigate", [Url]),
    erl_com:property_put(Obj, "Visible", true),
    Obj.

example() ->
    open("www.erlang.org").

The internet explorer application has a dispatch interface, that implements the IWebBrowser interface. There are a lot of methods. We use the Navigate method to open a specific URL, and the Visible property to show the browser. (By default, the browser is created invisible, like other Microsoft programs used from COM.)
3.4 Example two, making a graph in Excel

In this example, we also start an instance of the Excel application. We use the program name “Excel.Application”, which can be used instead of a class ID. This selects the Excel that is installed; Excel from Office 97 or Office 2000.

The easiest way to do anything with Excel is to first record a VBA macro. The resulting VBA macro is shown in figure 1. This macro is manually rewritten a bit to make it simpler. We try it out, and the result is shown in figure 2.

Now, to perform this into Erlang, we have two choices: either we can call the VB code as a subroutine using COM from Erlang, or we can reimplement the VB macro in Erlang. Since this is a user’s guide, we of course choose the latter.

To get to the interfaces, we use OLE/COM Object Viewer, and get the IDL for Excel. There is an Excel type library available. We do not want all of it because it is huge. We just pick the needed interfaces, which are _Application, _Graph and _Range. We also extract some enums, which are constants used for parameters in the COM calls.

There are some tricky issues when calling COM from Erlang

First, VB handles releasing of COM interfaces implicitly. Erlang and COM does not do this, so we have to make calls to erl_com:release/1 for every interface we get. For instance, every _Range we get from the property _Application.Range, has to be released. We do this in the helper function data_to_column/3.

Secondly, when an interface is returned, it is returned as an integer. This integer is actually an index into an interface array contained in the erl_com_drv port program. When calling functions in erl_com, we have to provide both the pid and the thread number, so there is a helper function erl_com::package_interface/2, that repackages the interface integer with given thread or other interface. When giving the interface as a parameter to a COM function (through erl_com:call or erl_com:invoke), however, the interface should be converted to a pointer, which is done with the tuple notation for COM types: {vt_unknown, Interface}.

When Excel is started, we execute a series of Excel commands to enter data and to draw a graph. The commands are translated from a VBA macro that we got using Excel’s standard macro recorder.

We use some constants that are needed for the Excel commands. These are taken from Visual Basic’s code generation from the Excel interfaces. Although these can be fetched from Excel using COM, erl_com does not yet support this. (Future releases will include code-generation that will greatly simplify using big COM-interfaces.

-module(xc).
-author('[email protected]').

-include("erl_com.hrl").

%% enum XlChartFormat
-define(XlPieExploded, 69).
-define(XlPie, 5).

%% enum XlChartLocation
-define(xlLocationAsNewSheet, 1).
-define(xlLocationAsObject, 2).
-define(xlLocationAutomatic, 3.


%% enum XlRowCol
-define(xlColumns, 2).
-define(xlRows, 1).


-export([populate_area/4, f/3, make_graph/6, sample1/0]).

to_cell_col(C) when C > 26 ->
        [C / 26 + 64, C rem 26 + 64];
to_cell_col(C) ->
        [C+64].

populate_area(E, _, _, []) ->
        ok;
populate_area(E, Row, Col, [Data | Resten]) ->
        Cell= to_cell_col(Col)++integer_to_list(Row),
        io:format(" ~s ~n ", [Cell]),
        N= erl_com:property_get(E, "range", [Cell]),
        Range= erl_com:package_interface(E, N),
        erl_com:property_put(Range, "Value", Data),
        erl_com:release(Range),
        populate_area(E, Row+1, Col, Resten).

f(E, _, []) ->
        ok;
f(E, Startcell, [Data | Resten]) ->
        {R, C}= Startcell,
        Cell= "R"++integer_to_list(R)++"C"++integer_to_list(C),
        io:format(" ~p ~n ", [Cell]),
        f(E, {R+1, C}, Resten).

make_graph(E, Row1, Col1, Row2, Col2, Title) ->
        Charts = erl_com:package_interface(E, erl_com:property_get(E, "Charts")),
        erl_com:invoke(Charts, "Add"),
        ActiveChart= erl_com:package_interface(E, erl_com:property_get
                                               (E, "ActiveChart")),
        erl_com:property_put(ActiveChart, "ChartType", {vt_i4, ?XlPieExploded}),
        erl_com:invoke(ActiveChart, "Location", [{vt_i4, ?xlLocationAsObject}, 
                                                 "Sheet1"]),
        Chart= erl_com:package_interface(E, erl_com:property_get(E, "ActiveChart")),
        R= to_cell_col(Col1)++integer_to_list(Row1)++":"
         ++to_cell_col(Col2)++integer_to_list(Row2),
        io:format(" ~s ~n ", [R]),
        Range= erl_com:property_get(E, "Range", [R]),
        erl_com:invoke(Chart, "SetSourceData", [{vt_unknown, Range}, 
                                                {vt_i4, ?xlColumns}]),
        erl_com:property_put(Chart, "HasTitle", true),
        ChartTitle= erl_com:package_interface(E, erl_com:property_get
                                              (Chart, "ChartTitle")),
        erl_com:property_put(ChartTitle, "Caption", Title).
        %erl_com:release(erl_com:package_interface(E, Range)),
        %erl_com:release(ActiveChart),
        %erl_com:release(Charts).

sample1() ->
        {ok, Pid}= erl_com:start_process(),
        T= erl_com:new_thread(Pid),
        E= erl_com:create_dispatch(T, "Excel.Application", ?CLSCTX_LOCAL_SERVER),
        erl_com:property_put(E, "Visible", true),
        Wb= erl_com:package_interface(T, erl_com:property_get(E, "Workbooks")),
        erl_com:invoke(Wb, "Add"),
        populate_area(E, 1, 1, ["Erlang", "Java", "C++"]),
        populate_area(E, 1, 2, ["25", "100", "250"]),
        make_graph(E, 1, 1, 3, 2, "Programming errors, by programming language"),
        {T, E, Wb}.

3.5 Example three, calling a COM object in C++

To be done.