The goal was a script which reads the file, line by line, containing file paths (Windows and Linux). It strips the path leaving only a file name with an extension. Then replaces any special characters in the file name with an "_" - underscore and at the end reduces the consecutive underscores with just one. Like st__a___ck becomes st_a_ck. I got it working but I believe there may be a better/nicer looking way of doing this. I'm a very beginner and still learning to think the Elixir/functional way. What I want is to see different ways of doing this, ways of improving and elixifying a bit.
The test sample:
c:\program files\mydir\mydir2\my&@Doc.doc
c:\program files\mydir\mydir2\myD$oc2.doc\
c:\\program files\\mydir\\mydir2\\myD;'oc2.doc
c:\\program files\\mydir\mydir2\\my[Doc2.doc\\
/home/python/projects/files.py
/home/python/projects/files.py/
//home//python//projects//files.py
//home//python//projects//files.py//
c:\program files\mydir\mydir2\my!D#oc.doc
c:\program files\mydir\mydir2\myDoc2.doc\
c:\\program files\\mydir\\mydir2\\my';Doc2.doc
c:\\program files\\mydir\mydir2\\myD&$%oc2.doc\\
/home/python/projects/f_)*iles.py
/home/python/projects/files.py/
//home//python//projects//fi=-les.py
//home//python//projects//fil !%es.py//
/home/python/projects/f_)* iles.py
/home/python/projects/fi les.py/
//home//python//projects//fii___kiii=- les.py
//home//python//projects//ff###f!%#illfffl! %es.py//
The code:
defmodule Paths do
def read_file(filename) do
File.stream!(filename)
|> Enum.map( &(String.replace(&1,"\\","/")) )
|> Enum.map( &(String.trim(&1,"\n")) )
|> Enum.map( &(String.trim(&1,"/")) )
|> Enum.map( &(String.split(&1,"/")) )
|> Enum.map( &(List.last(&1)) )
|> Enum.map( &(String.split(&1,".")) )
|> Enum.map( &(remove_special)/1 )
|> Enum.map( &(print_name_and_suffix)/1 )
end
defp print_name_and_suffix(str) do
[h|t] = str
IO.puts "Name: #{h}\t suffix: #{t}\t: #{h}.#{t}"
end
defp remove_special(str) do
[h|t] = str
h = String.replace(h, ~r/[\W]/, "_")
h = String.replace(h, ~r/_+/, "_")
[h]++t
end
end
Paths.read_file("test.txt")
Any insights much appreciated.
EDIT: I refactored the code a little. Which version is more Elixir style like?
defmodule Paths do
def read_file(filename) do
File.stream!(filename)
|> Enum.map( &(format_path)/1 )
|> Enum.map( &(remove_special)/1 )
|> Enum.map( &(print_name_and_suffix)/1 )
end
defp format_path(path) do
path
|> String.replace("\\","/")
|> String.trim("\n")
|> String.trim("/")
|> String.trim("\\")
end
defp print_name_and_suffix(str) do
[h|t] = str
IO.puts "Name: #{h}\t suffix: #{t}\t: #{h}#{t}"
end
defp remove_special(str) do
ext = Path.extname(str)
filename = Path.basename(str)
|> String.trim(ext)
|> String.replace(~r/[\W]/, "_")
|> String.replace( ~r/_+/, "_")
[filename]++ext
end
end
Paths.read_file("test.txt")
1条答案
按热度按时间balp4ylt1#
I would point to the generic problems with the code in the first place.
File.stream!/3
produces aStream
explicitly designed to be processed lazily (so we don't keep the whole content of the file in memory). Passing it toEnum.map/2
makes zero sense. UseStream.map/2
to keep processing the file lazily or useFlow.map/2
to parallelize the mapping operations and use all available cores (you keep the laziness too!).formatter
) to format your code.defp print_name_and_suffix(str), do: [h|t] = str ...
do directlydefp print_name_and_suffix([h|t])
.That said, the most [opinionated] Elixirish approach would be:
Please pay attention mostly to
strip_path/2
function, it does recursively parse the input string, returning the part after the last slash, forward or backward. I could useString.split/2
or any internal function fromString
module but I explicitly had it implemented with a most functional approach.