【问题标题】:What is the best approach to append to a file in Elixir在 Elixir 中附加到文件的最佳方法是什么
【发布时间】:2020-01-22 16:20:27
【问题描述】:

我一直被一些简单的任务所困。假设我们有一些伪代码:

Enum.each 1..1_000_000, fn(id) ->
    some_complex_method(id) |> save_results
end

save_results 在哪里

def save_results(data) do
    {:ok, file} = File.open "data.log", [:append]
    Enum.each(data, &(IO.binwrite(file, &1)))
    File.close file
end

问题是,如果我们的范围真的很大,我们就会花时间打开和关闭文件。如何让它处理打开状态并在工作完成时调用close方法?

【问题讨论】:

  • 您的 save_results 代码看起来不像每次都会打开和关闭文件。是什么让你相信这就是正在发生的事情?
  • 对不起,我错过了伪代码中的重要部分。现在更新了。
  • 如何在循环之前打开文件并在之后关闭它?感觉并不理想,但我相信它会适用于您的情况。如果您愿意做更多的工作,您可以轻松使用 GenServer,您可以在其中不断推送文件写入的内容。然后你让 GenServer 以一定的延迟写入文件,比如说 10 条消息。当 10 条消息或某个时间间隔结束时,您会将所有消息一起归档。
  • 您可能还想通过StringIO 创建一个“buf”并写入它,就像这里建议的那样:stackoverflow.com/a/72048883/3995261

标签: elixir


【解决方案1】:

使用{:delayed_write, non_neg_integer, non_neg_integer} 代替:append 作为写入模式,以便缓冲您的写入,直到达到一定数量的要写入的数据或达到一定的时间。如果您只打算在文件末尾追加,请不要忘记另外使用:append

第一个non_neg_integer 是写入发生前要缓冲的字节大小。

第二个non_neg_integer 是写入发生前缓冲的延迟毫秒数。如需更详细的解释,请访问the erlang documentation of :file.open/2。 对于其他可能的模式,请查看elixir documentation for File.open/2

您的示例可能如下所示:

Enum.each 1..1_000_000, fn(id) ->
    some_complex_method(id) |> save_results
end

save_results 在哪里

def save_results(data) do
    {:ok, file} = File.open "data.log", [:append, {:delayed_write, 100, 20}]
    Enum.each(data, &(IO.binwrite(file, &1)))
    File.close file
end

我无法判断这种确切的配置是否适合您的情况,但它肯定会减少文件打开的数量。

翻译上面的配置意思是:

亲爱的File模块,
请缓冲我对文件 data.log 的写入,直到我要求您写入至少 100 字节的数据,或者直到您等待 20 毫秒让我给您更多要写入的数据。
非常感谢!
真的是你的your elixir coder

【讨论】:

  • 哇,你真有幽默感!
【解决方案2】:

你能提前完成工作,并且只有在完成后才进行文件写入?

results = Enum.map 1..1_000_000, fn(id) ->
    some_complex_method(id)
end

然后:

log_results(results)

地点:

defp log_results(results) do
    {:ok, file} = File.open("data.log", [:append])
    save_results(file, results)
    File.close(file)
end

defp save_results(file, []), do: :ok
defp save_results(file, [data|rest]) do
    IO.binwrite(file, data)
    save_results(file, rest)
end

【讨论】:

    【解决方案3】:

    使用其他答案编写了一个小附加函数:

    filewriter = fn (filename, data) -> 
      File.open(filename, [:append]) 
      |> elem(1) 
      |> IO.binwrite(data) 
    end
    
    filewriter.("Rakefile", "here's a line to append!\n")
    

    【讨论】:

      【解决方案4】:

      看起来您上面的代码似乎不会在每次写入时打开和关闭文件,但如果您没有显示某些内容,那么我建议您也许想用 @987654321 替换您的 File.open/2 @

      【讨论】:

      • 对不起,我错过了伪代码中的重要部分。现在它更新了。基本上我们正在经历 1...1_000_000,将每个数字传递给某个方法,该方法会生成一些数据,并将其结果保存在日志文件中。
      • 还有其他方法可以实现吗?也许使用 GenServer?
      【解决方案5】:

      你可以使用Path模块

      Path.expand('./text.txt') |> Path.absname |> File.write("new content!", [:write])
      # => :ok
      

      在此处阅读更多信息
      Working With the File System in Elixir

      【讨论】:

      • 这不会将数据附加到文件中,它会用“新内容!”覆盖其内容
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      • 2017-11-13
      • 2020-12-21
      • 1970-01-01
      • 2013-05-09
      • 2011-11-21
      相关资源
      最近更新 更多