Regarding charlist
you can write code like:
# split by any horizontal whitespace character (regex: \h)
# do it globally i.e. for all horizontal whitespaces occurrences
# return binary i.e. Elixir string (Erlang string is charlist)
'your charlist' |> :string.trim() |> :re.replace("\\h+", ',', [:global, return: :binary])
and use it as a normal csv
.
Regarding ps aux
command output you can create a custom parser
using NimbleCSV
:
defmodule Example do
# [head | tail] pattern for fetching headers and rows
def sample([headers | rows) do
Enum.map(rows, &sample(headers, &1, %{}))
end
# again [head | tail] pattern for fetching key-value pairs from 2 lists
# we are updating accumulator
defp sample([key | rest_keys], [value | rest_values], acc) do
sample(rest_keys, rest_values, Map.put(acc, key, value))
end
# which is returned when we are at end of headers or row cells
defp sample([], _, acc), do: acc
defp sample(_, [], acc), do: acc
# or just
# defp sample([], [], acc), do: acc
# in case we are sure that every row length is equal to headers length
end
# for a different number of spaces between columns
# for me it's 11 columns, so I used 20 as safe value
separator = Enum.map 1..20, &String.duplicate(" ", &1)
# simple custom parser with multiple separators
NimbleCSV.define(MyParser, separator: separator)
# a csv string
csv = 'ps aux' |> :os.cmd() |> List.to_string() |> String.trim()
# parse a csv and transform it to map
result = csv |> MyParser.parse_string(skip_headers: false) |> Example.sample()
Here is how I found my highest separator length:
:os.cmd('ps aux')
|> List.to_string()
# split by everything which is not space character
|> String.split(~r/[^ ]+/)
# get highest length
|> Enum.reduce(1, fn elem, acc ->
length = String.length(elem)
if length > acc, do: length, else: acc
end)
# this is faster than
# list |> Enum.max_by(&String.length/1) |> String.length()
# as we do not call String.length/1 twice for separator with highest length
Helpful resources:
-
:re
Erlang
module
-
NimbleCSV.define/2 documentation