Erlang Central

Applying a Function to Each Line of a String

From ErlangCentral Wiki


You'd like to apply a function to a sequence of logical lines within a string, without regard for the sort of newlines (Unix, Mac, DOS) involved.


You can neatly divide the string into lines with string:tokens/2, provided that you don't mind excluding blank lines:

1> string:tokens("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").

If you must also have the blank lines, or if you've complex ideas about 'lines', you can use regexp:split/2:

2> regexp:split("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").
3> regexp:split("hello\r\nthere\r\nmon\r\rfrere\n","[\r\n]+").
4> regexp:split("hello\r\nthere\r\nmon\r\rfrere\nfoo","[\r\n]+").

You can also use foreach_line/2 and mapeach_line/2; these exclude blank lines, and have the same line semantics as the first string:tokens/2 solution, but you can easily adapt these. These also return the remainder of the string and the {remainder(),mapped_results()}, respectively.

foreach_line(F,L) ->
    lists:reverse(lists:foldl(fun (C,[]) when C == $\r orelse C == $\n -> [];
                                  (C,S) when C == $\r orelse C == $\n ->
                                      F(lists:reverse(S)), [];
                                  (C,S) ->
                              end, [], L)).

mapeach_line(F,L) ->
    {R,M} = lists:foldl(fun (C,R={[],_}) when C == $\r orelse C == $\n -> R;
                            (C,{S,M}) when C == $\r orelse C == $\n ->
                            (C,{S,M}) ->
                        end, {[],[]}, L),
    {lists:reverse(R), lists:reverse(M)}.

5> foreach_line(fun(S) -> io:fwrite("l; ~s\n",[S])  end,"hello\r\nthere\rmon\n\rfrere\n\nfoo").
l; hello
l; there
l; mon
l; frere
6> mapeach_line(fun lists:reverse/1, "hello\r\nthere\rmon\n\rfrere\n\nfoo").