Erlang Central

Difference between revisions of "Applying a Function to Each Line of a String"

From ErlangCentral Wiki

(new recipe)
 
m (*facepalm* same typo; also remove ugly captions)
Line 9: Line 9:
 
provided that you don't mind eliding blank lines:
 
provided that you don't mind eliding blank lines:
  
<code caption="string:tokens/2">
+
<code>
 
1> string:tokens("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").
 
1> string:tokens("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").
 
["hello","there","mon","frere"]
 
["hello","there","mon","frere"]
Line 17: Line 17:
 
'lines', you can use regexp:split/2:
 
'lines', you can use regexp:split/2:
  
<code caption="regexp:split/">
+
<code>
 
2> regexp:split("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").
 
2> regexp:split("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").
 
{ok,["hello","there","mon\r\rfrere\n"]}
 
{ok,["hello","there","mon\r\rfrere\n"]}
Line 32: Line 32:
 
respectively.
 
respectively.
  
<code caption="foreach_line/2 and mapeach_line/2">
+
<code>
 
foreach_line(F,L) ->
 
foreach_line(F,L) ->
 
     lists:reverse(lists:foldl(fun (C,[]) when C == $\r orelse C == $\n -> [];
 
     lists:reverse(lists:foldl(fun (C,[]) when C == $\r orelse C == $\n -> [];
Line 60: Line 60:
 
</code>
 
</code>
  
[[Category:Cookbook]][[Category:ListRecipes]]
+
[[Category:CookBook]][[Category:ListRecipes]]

Revision as of 00:53, 15 October 2006

Problem

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.

Solution

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

1> string:tokens("hello\r\nthere\r\nmon\r\rfrere\n","\r\n").
["hello","there","mon","frere"]

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").
{ok,["hello","there","mon\r\rfrere\n"]}
3> regexp:split("hello\r\nthere\r\nmon\r\rfrere\n","[\r\n]+").
{ok,["hello","there","mon","frere",[]]}
4> regexp:split("hello\r\nthere\r\nmon\r\rfrere\nfoo","[\r\n]+").
{ok,["hello","there","mon","frere","foo"]}

You can also use foreach_line/2 and mapeach_line/2; these elid 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) ->
                                      [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 ->
                                {[],[F(lists:reverse(S))|M]};
                            (C,{S,M}) ->
                                {[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
"foo"
6> mapeach_line(fun lists:reverse/1, "hello\r\nthere\rmon\n\rfrere\n\nfoo").                   
{"foo",["olleh","ereht","nom","ererf"]}