【问题标题】:Erlang: Read from an input stream in a efficient wayErlang:以有效的方式从输入流中读取
【发布时间】:2016-09-02 14:04:10
【问题描述】:

我正在编写一个从 input 流中读取的程序,即

erl -run p main -noshell -s erlang halt < input

问题是使用这个读取函数读取它需要很多时间(输入流很大):

read_input(L) ->
    case io:get_line("") of
        eof ->
            lists:reverse(L);
        E0 ->
            read_input([E0|L])
    end.

我一直在寻找更有效的替代方案,但一无所获。我尝试使用

读取文件
{ok, Binary} = file:read_file("input")

这要高效得多。问题是我必须在名称未知的平台上运行这个程序,所以我需要一些替代方法。另外,我无法选择运行时使用的标志,例如flag -noinput 不能添加到命令行。

我们欢迎您提供任何帮助。

【问题讨论】:

  • 如果像您所说的那样一次读取整个文件是可以接受的,为什么不只是{ok, Binary} = file:read_file(InputFile)?我不明白使用cat 复制输入并改为读取副本的意义。
  • 这是一个命令行应用程序吗?程序是否总是像这样从命令行调用?输入流从哪里来?读取数据后发生了什么变化?
  • @SteveVinoski 我已经编辑了问题以删除猫的东西,因为它令人困惑。是的,我可以直接从文件中读取它,但我不知道它的名称,所以这个解决方案只在测试时有效。
  • @nu-ex 是的,程序应该总是这样使用,输入流应该这样读取。输入来自将运行它的系统(如上所示)。之后处理数据。它主要包含整数,所以我必须在之后解析它们。
  • 你在什么平台上运行?

标签: performance io erlang inputstream processing-efficiency


【解决方案1】:

虽然Steve's solution 是我最快知道的解决方案,但可以使用性能相当好的file 模块解决方案:

-module(p).

-export([start/0]).

-define(BLK_SIZE, 16384).

start() ->
    do(),
    halt().

do() ->
    Bin = read(),
    io:format("~p~n", [byte_size(Bin)]).

read() ->
    ok = io:setopts(standard_io, [binary]),
    read(<<>>).

read(Acc) ->
    case file:read(standard_io, ?BLK_SIZE) of
        {ok, Data} ->
            read(<<Acc/bytes, Data/bytes>>);
        eof ->
            Acc
    end.

它适用于以下调用:

erl -noshell -s p < input

注意,这两种方法都可以用于面向行的输入,使用{line, Max_Line_Size} 端口选项或file:read_line/1 用于file 模块解决方案。从第 17 版开始(如果我没记错的话),我发现 file:read_line/1 中存在固定的性能错误,所以现在很好。无论如何,您不应该期望 Perl 的性能和舒适性。

【讨论】:

  • @Hyney-pichi-Vychdil 您的解决方案正是我所需要的。非常感谢!确实Steve's solution 效率更高,但差别很小。我不知道可以通过这种方式在 Erlang 程序中引用标准输入。非常有用,也很有趣。再次感谢。我希望你的回答能帮助更多有类似问题的人:)
【解决方案2】:

您可以使用open_port/2 打开标准输入并从中读取二进制文件。例如:

-module(p).
-export([start/0]).

start() ->
    process_flag(trap_exit, true),
    P = open_port({fd,0,1}, [in, binary]),
    Bin = read(P,<<>>),
    io:format("received ~p\n", [Bin]),
    halt(0).

read(P, Bin) ->
    receive
        {P, {data, Data}} ->
            read(P, <<Bin/binary, Data/binary>>);
        {'EXIT',P,_} ->
            Bin
    end.

代码必须捕获出口,以便它知道在端口关闭时退出其读取循环。此示例将所有内容读入从 read/2 函数返回的单个二进制文件中,然后将其打印出来并退出,但显然您可以在实际应用程序中对二进制文件执行进一步的操作。

你可以这样运行:

erl -noinput -s p < input

【讨论】:

  • 您的解决方案非常有效,但它并没有解决我的问题,因为从外部平台调用时我无法添加noinput 标志。
  • 您的问题表明您使用的是-noshell。为什么你可以使用它却不能使用-noinput?您确实需要在问题中明确说明您的要求。
  • 因为我无法选择运行时使用的标志。我需要它来解决必须读取输入时的性能的 Hackerrank 问题。我只需单击一个按钮并等待结果。他们中的大多数是由于超时而完成的,在分析之后我发现主要的时间消费者是输入读数。您提出的解决方案非常有效,但不幸的是在这种情况下不起作用。
猜你喜欢
  • 2011-01-30
  • 2011-11-24
  • 2013-06-03
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 2011-02-12
相关资源
最近更新 更多