Как читать содержимое файла в Erlang?

Я знаю, что вы можете сделать что-то вроде этого:

readlines(FileName) ->
    {ok, Device} = file:open(FileName, [read]),
    get_all_lines(Device, []).

get_all_lines(Device, Accum) ->
    case io:get_line(Device, "") of
        eof  -> file:close(Device), Accum;
        Line -> get_all_lines(Device, Accum ++ [Line])
    end.

: Есть ли один BIF-вкладыш, который тоже может это сделать?

Ответ 1

file: read_file/1 - это то, что вы ищете. Только для целей обучения Accum ++ [Line] - это плохая практика. Проблема в том, что левый аргумент ++ скопирован и правый используется как есть. В вашем коде вы будете копировать большую и большую часть на каждой итерации. Решение lists:reverse(Line,Accum) и возврат lists:reverse(Accum) в ветки eof (или [Line|Accum] и lists:append(lists:reverse(Accum)) в eof) или используйте двоичные файлы, которые имеют лучшую операцию добавления или...). Другой способ - не использовать функцию хвоста, которая не так плоха, как кажется в первый раз согласно Миф: хвостово-рекурсивные функции МНОГО быстрее, чем рекурсивные функции.

Итак, ваша функция readlines/1 должна выглядеть как

readlines(FileName) ->
    {ok, Device} = file:open(FileName, [read]),
    try get_all_lines(Device)
      after file:close(Device)
    end.

get_all_lines(Device) ->
    case io:get_line(Device, "") of
        eof  -> [];
        Line -> Line ++ get_all_lines(Device)
    end.

Ответ 2

Вы можете использовать file:read_file/1 и binary:split/3 для выполнения этой работы в два этапа:

readlines(FileName) ->
    {ok, Data} = file:read_file(FileName),
    binary:split(Data, [<<"\n">>], [global]).