【问题标题】:Elixir: Trying to Write a Map to CSV, Being Written as Stream ResultsElixir:尝试将地图写入 CSV,被写为流结果
【发布时间】:2017-03-30 22:12:51
【问题描述】:

我在这个问题上摸不着头脑已经有一段时间了。我正在尝试编写一个程序,将给定文本文件中每个单词的频率输出到 .csv 文件。我已经成功地创建了查找每个单词的频率并将其结果输出为地图的函数,但是我的 tocsv 函数出于某种原因将结果写为 Stream 结果,我无法弄清楚为什么或如何避免这种情况。这是我的代码:

defmodule WordFrequency do

  def wordCount(readFile) do
     readFile
     |> words
     |> count
     |> tocsv
  end

  defp words(file) do
    file
    |> File.stream!
    |> Stream.map(&String.trim_trailing(&1))
    |> Stream.map(&String.split(&1,~r{[^A-Za-z0-9_]}))
    |> Enum.to_list
    |> List.flatten

  end

  defp count(words) when is_list(words) do
    Enum.reduce(words, %{}, &update_count/2)
  end

  defp update_count(word, acc) do
    Map.update acc, String.to_atom(word), 1, &(&1 + 1)
  end

  defp tocsv(map) do
    file = File.open!("test.csv", [:write, :utf8])
    map
    |> IO.inspect
    |> Enum.map(&CSV.encode(&1))
    |> Enum.each(&IO.inspect(file, &1, []))
  end

end

count(它是一个测试文件)的结果是:

bitterness: 1, fan: 1, respiration: 1, radiator: 1, ceiling: 1, run: 1,
  duck: 1, roundess: 1, terrorism: 1, she: 1, over: 1, equipment: 2, test: 1,
  freshness: 1, feminism: 1, bucket: 1, goodness: 1, manliness: 1,
  reflection: 1, uncomfortable: 1, tourism: 1, house: 1, ableism: 1, stairs: 1,
  heroism: 1, sadness: 1, socialism: 1, fruit: 1, dogs: 1, mechanism: 1,
  symbolism: 1, predilection: 1, up: 1, sedition: 1, faithfulness: 1,
  fruition: 1, criticism: 1, conformation: 1, extradition: 1, braveness: 1,
  ionization: 1, indigestion: 1, bubble: 1, introspection: 1, liquid: 1,
  apartment: 1, deep: 1, department: 1, centralization: 1, bitter: 1, ...}

所以我知道我没有将流传递给我的 tocsv 函数,但是在 tocsv 中发生了一些事情,将其转换为流并且在输出到 csv 文件之前不会将其转换为可写格式。任何人都知道我该如何解决这个问题?我正在使用这个 CSV 模块:https://github.com/beatrichartz/csv

谢谢!

【问题讨论】:

    标签: csv stream elixir


    【解决方案1】:

    在您使用的CSV 模块的README 中有一个生成CSV 的示例:

    file = File.open!("test.csv", [:write, :utf8])
    table_data |> CSV.encode |> Enum.each(&IO.write(file, &1))
    

    请注意,IO.write/2 将字节写入设备,而IO.inspect/3 使用IO 设备根据给定选项检查第二个参数。此外,CSV.encode/1 需要一个二维列表

    也就是说,您可能应该坚持使用示例中提到的IO.write/2,并在count 中生成二维列表,而不是Map

    defp count(words) when is_list(words) do
      words
      |> Enum.reduce(%{}, &update_count/2)
      |> Enum.reduce([], fn {k, v}, acc -> [[k, v] | acc] end)
    end
    
    defp tocsv(map) do
      file = File.open!("test.csv", [:write, :utf8])
    
      map
      |> IO.inspect
      |> CSV.encode
      |> Enum.each(&IO.write(file, &1))
    end
    

    在这种简单的情况下,我将只使用 Elixir 来生成一个文件,不过(假设 count 返回一个地图,就像在您的原始代码中一样):

    defp tocsv(map) do
      File.open("test.csv", [:write, :utf8], fn(file) ->
        Enum.each(map, &IO.write(file, Enum.join(Tuple.to_list(&1), ?,) <> "\n"))
      end)
    end
    

    或者,更简单:

    defp tocsv(map) do
      File.write!("test.csv", 
         map
         |> Enum.map(Enum.join(Tuple.to_list(&1), ?,))
         |> Enum.join("\n"))
    end
    

    【讨论】:

    • 嘿,这很好用!我实际上并没有意识到 Elixir 可以单独写入 csv - 这是一个理想且简单得多的解决方案。我遇到的一个问题是,生成的 csv 文件只是将所有数据连接在一起而没有任何中断,例如:organism1run1ceiling1plagiarism1taking1test1sounds1sadness1freshness1deep1stairs1conformation1investment2 知道如何解决这个问题吗?
    • 理想情况下,我希望对 csv 进行格式化,以便每个单词及其频率出现在一行上,并用制表符或逗号从频率中划定单词
    • 请查看更新。 Enum.join/2 可以选择将joiner 指定为第二个参数,结果现在与"\n" 连接。
    • 好吧,没关系,我通过像这样修改 tocsv 函数来解决这个问题:' defp tocsv(map) do File.open("test.csv", [:write, :utf8], fn (file) -> Enum.each(map, &IO.write(file, Enum.join(Tuple.to_list(&1), ", ")"\n")) end) end'
    • 非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    • 2013-09-24
    • 2011-09-30
    相关资源
    最近更新 更多