在Elixir中打开并从多个文件中收集数据

时间:2017-02-20 23:21:12

标签: functional-programming stream elixir

我正在自学Elixir进行研究,而且我的研究经常需要打开几十或一百个文本文件,将这些文件中的数据组合起来,并操纵数据。我试图找出如何打开目录中的所有文件并访问所有这些文件中的数据。我想避免使用for循环,因为在循环中迭代100个文件会非常慢。我认为Stream模块非常适合我的目的,但我不知道如何使用它。

下面,我有一些测试代码。它应该做的就是打开一堆包含随机数的文件,将文件中的数字字符串转换为整数,然后对它们进行排序。除了打开文件部分外,一切正常。您可以看到我尝试使用Path模块,这确实成功找到了所有文件,但我不知道如何以可用的方式将它传递给sort_num函数。谢谢大家的帮助!

defmodule OpenFiles do

  def file_open do
    Path.wildcard("numfiles/*.txt")
  end

  def sort_num do
    file_open
    |> File.stream!
    |> Stream.map(&String.strip/1)
    |> Stream.map(&String.to_integer/1)
    |> Enum.sort
  end 
end

IO.inspect OpenFiles.sort_num

1 个答案:

答案 0 :(得分:3)

File.stream!/3功能一次只能在一个文件上运行。如果您正在使用通配符并一次收集多个文件,则它无法按预期方式工作。

如果查看Path.wildcard/2的返回,您将获得所有匹配文件的列表。

的内容
["foo.txt", "bar.txt", "baz.txt"]

如果将其传递给File.stream!/3,它会尝试将所有这些值附加在一起。

File.stream! ["foo.txt", "bar.txt", "baz.txt"]
%File.Stream{line_or_bytes: :line, modes: [:raw, :read_ahead, :binary],
 path: "foo.txtbar.txtbaz.txt", raw: true}

正如您所看到的,它认为您尝试访问的路径是"foo.txtbar.txtbaz.txt",这是不正确的并且所有“路径”连接在一起。

为了访问所有这些文件,您必须自己运行每个文件。

defmodule OpenFiles do
  def file_open do
    Path.wildcard("numfiles/*.txt")
  end

  def sort_num do
    file_open()
    |> Enum.map(fn file ->
      file
      |> File.stream!()
      |> Stream.map(&String.strip/1)
      |> Stream.map(&String.to_integer/1)
      |> Enum.take(1) # This only takes the first line. This may or may not be what you want.
    end)
    |> List.flatten()
    |> Enum.sort()
  end 
end

正如您所提到的,如果您有大量文件(或大文件),这可能需要很长时间。但是,您可以使用并行映射实现而不是顺序Enum.map/2来缓解此问题。