2017-02-20 107 views
1

Я преподаю себе Elixir для своих исследований, и часто мое исследование требует открытия нескольких десятков или сотен текстовых файлов, объединяя данные в эти файлы и манипулируя данными. Я пытаюсь выяснить, как я могу открыть все файлы, которые у меня есть в каталоге, и получить доступ к данным во всех этих файлах. Я хотел бы избежать использования цикла for, потому что итерация через 100 файлов в цикле будет очень медленной. Я думаю, что модуль Stream идеален для моих целей, но я не знаю, как его использовать.Открывать и собирать данные из нескольких файлов в Elixir

Ниже приведено несколько тестовых кодов. Все, что предполагается сделать, это открыть кучу файлов, содержащих случайные числа, преобразовать строки чисел в файлы в целые числа и отсортировать их. Все работает, кроме части открытых файлов. Вы можете видеть, что я пытался использовать модуль 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 

ответ

2

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.

+0

Действительно большой и подробный ответ! Спасибо. Да, я думаю, что реализация параллельной карты будет моим лучшим выбором. –