[erlang-questions] Erlang Basics: Reading File Content

Erik Søe Sørensen eriksoe@REDACTED
Mon Dec 16 18:33:01 CET 2013

Two questions:
1) Do the files always contain text content?
If no, then "get_line()" would probably be a bad idea; using file:read/2 to
read N bytes is a better bet. Also, open the file in 'binary' mode.
2) Do you expect the files to always be small?
If yes, then file:read_file/1 is nice and easy to use: it reads the entire
file up and gives you the content in form of a binary.
Only in rare usecases would I find it necessary to read the entire file on
a line-by-line basis, as your code so far does.
Simple file serving would be both simpler and faster by keeping the file
data in binary form.
(If there needs to be done some line-ending transformation, or if the file
is a pipe or similar in which the data appears slowly, line-by-line, e.g. a
log, then it's of course another matter.)

With file:read_file/1, it'd be simple:
    {ok,Data} = file:read_file(Filename),
    % write Data to socket
With file:read/2 in a read-it-all loop (close to what you've written, but
in essence replaceable by read_file), you'd have something like:

    try get_data(Device)
    of Data -> % write Data to socket
    after file:close(Device)
   -define(BLOCK_SIZE, 4096).
   get_data(Device) ->
      case file:read(Device, ?BLOCK_SIZE) of
        {ok, Data} -> [Data | get_data(Device)];
        eof -> []

(Or the tail recursive equivalent.)
Note the "try-of" construct, which is peculiar to Erlang, but very handy -
it's like the
     Data = get_data(Device),
     % write Data to socket
   after ...
you'd probably write (with other syntax, of course) in other languages,
except that only exceptions in the get_data() call will be caught; the
"of..." clause won't be covered by the exception handler.
(This is the kind of thing you probably won't think about before you learn
that such a construct exists - and from then on you'll find it lacking from
the languages which don't have it...)

And finally, with file:read/2 in a transfer-a-chunk-at-a-time loop, it'd be:
    try transfer_data(Device, Socket)
    after file:close(Device)
   -define(BLOCK_SIZE, 4096).
   transfer_data(Device, Socket)) ->
      case file:read(Device, ?BLOCK_SIZE) of
        {ok, Data} ->
           % Write Data to Socket
           transfer_data(Device, Socket)
        eof -> ok

OK, this response is long enough by now. I hope the answer helps :-)

2013/12/16 Ari King <ari.brandeis.king@REDACTED>

> I've just started with Erlang and to learn/practice I'm attempting to put
> together a simple TCP server that reads data from a file and writes it to a
> socket. So far I've put together some code to read the data (combination of
> ascii, binary, control characters). But since (as I understand) Erlang
> doesn't have a return mechanism, how do I read a line of data and return it
> to be written to the socket? Right now, the code just recursively collects
> the data.
>     -module(mock_tcp).
>     -export([start/1]).
>     start([Ip, Port, Filename]) ->
>       io:format("Server available at ~w on port ~w. Reading from ~w.",
> [Ip, Port, Filename]),
>       {ok, Device) = file:open(Filename, read),
>       try get_data(Device)
>         after file:close(Device)
>       end.
>     get_data(Device) ->
>       case io:get_line(Device) of
>         {ok, Data} -> [Data | get_data(Device)];
>         eof -> []
>       end.
> Thanks.
> Ari
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20131216/7c28807a/attachment.htm>

More information about the erlang-questions mailing list