Error in trying to write with a file

Hi, everyone!
I’m new to elixir and I’m interested in trying to do the following: to write a list (which is an output of function) within a file. I found a usefull function, which is file. write() but I think it’s not recognizing its format. Here are the modules (the first one gets the file and treats it, outputing a list and then I try to write inside a file):

  def tratar_serie() do 
   if !File.exists?("priv/serie_tratada.TXT") do
     {:ok, fp} = File.open("priv/serie_tratada.TXT", [:write]) 
      File.stream!("priv/series.TXT")
      |> CSV.decode!(separator: ?\t)
      |> Enum.to_list()
      |> Enum.map(fn(row) ->  
          row_tratada = hd(row)
          nome_papel = get_nome_papel(row_tratada)
          preco_papel = get_preco_fechamento(row_tratada)
          [nome_papel: nome_papel, preco_papel: preco_papel]        
      end)
      |> Enum.filter(fn(x) -> 
          x[:nome_papel] == "PETR3" 
      end)
      |> Enum.map(fn(x) -> 
        String.slice(x[:preco_papel], 9, 4)
        |> Decimal.new()
        |> Decimal.div(100)
      end)
      |> Enum.join()
      |> criar_arquivo_serie(fp)
  end
end

def criar_arquivo_serie(lista_valores, fp) do
    File.write(fp, lista_valores)
  end

This is the log:

`** (FunctionClauseError) no function clause matching in IO.chardata_to_string/1`    
    
    The following arguments were given to IO.chardata_to_string/1:
    
        # 1
        #PID<0.203.0>
    
    Attempted function clauses (showing 2 out of 2):
    
        def chardata_to_string(string) when is_binary(string)
        def chardata_to_string(list) when is_list(list)
    
    (elixir) lib/io.ex:461: IO.chardata_to_string/1
    (elixir) lib/file.ex:998: File.write/3

Could anyone help me? Thank you in advance!

Well, it seems that you are trying to join a list of decimals and pass it directly to File.write/2 which expect iodata as the second param.

I would suggest you convert your list of Decimals to a list of Strings then joining them.

e.g

def criar_arquivo_serie(lista_valores, fp) do    
  contents =
    lista_valores
    |> Enum.map(fn(x) -> Decimal.to_string(x) end)
    |> Enum.join("\n")

  IO.binwrite(fp, contents) 
end

That should do the trick.

1 Like

File.write takes a path as its first argument, so you need to change the logic of working with the output file.

Looks like you only need to write to it at the end of processing the input priv/series.TXT file. In that case, something like this could work for you:

  def tratar_serie() do 
   if !File.exists?("priv/serie_tratada.TXT") do
      File.stream!("priv/series.TXT")
      |> CSV.decode!(separator: ?\t)
      # |> Enum.to_list()   # <-- there's no need for this
      |> Stream.map(fn(row) -> 
          row_tratada = hd(row)
          nome_papel = get_nome_papel(row_tratada)
          preco_papel = get_preco_fechamento(row_tratada)
          [nome_papel: nome_papel, preco_papel: preco_papel]        
      end)
      |> Stream.filter(fn(x) -> 
          x[:nome_papel] == "PETR3" 
      end)
      |> Stream.map(fn(x) -> 
        String.slice(x[:preco_papel], 9, 4)
        |> Decimal.new()
        |> Decimal.div(100)
      end)
      |> Enum.join()
      |> criar_arquivo_serie("priv/serie_tratada.TXT")
  end
end

def criar_arquivo_serie(lista_valores, path) do
  File.write(path, lista_valores)
end

Notice also that I’ve changed Enum to Stream as that is more efficient in this case.