Erlang实战文本排版

文本排版大家都并不陌生,并且随处可见,无论是当前的字处理软件,如Word、PDF等,还是传统的出版行业,如:书籍、报刊、杂志等,都有良好的排版风格和样式。好的排版风格让人耳目一新,有一种继续读下去的冲动。本文主要是通过Erlang实现简单的文本排版,借此学习Erlang、熟悉Erlang。

  实战:文本排版。

  要求如下:

  在代码中我附了注释,代码如下:

 
 
 
 
  1. -module(text_process).
  2. -export([process/2]).
  3. -import(lists,[reverse/1]).
  4. process(FileIn,FileOut) ->
  5.     {Status,Value} = file:open(FileIn,read),
  6.     if
  7.         Status =:= ok ->
  8.             Tokens = readFile(Value), %% 将要处理文本FileIn拆分为单词列表
  9.             io:format("token :~p~n",[Tokens]),
  10.             writeToFile(FileOut,Tokens), %% 将单词重新排版输出到屏幕与文件FileOut
  11.             io:format("~nsuccess!~n");
  12.         Status =/= ok ->
  13.             io:format("Open file error: file doesn't exist!")
  14.     end.    
  15.     
  16. readFile(Value) -> readFile(Value,[]).
  17. readFile(S,Dict) ->
  18.     OneLine = io:get_line(S,''), %%读取文件的一行
  19.     if
  20.         OneLine =:= eof ->
  21.              io:format("~nCome across the file end, stop!~n"),
  22.              file:close(S),
  23.              Dict;
  24.         OneLine =/= eof ->
  25.             Tokens = string:tokens(OneLine,"\r\t\n "),  %%以空格、回车、换行、制表符为单词分隔符
  26.             Len = string:len(Tokens),  %%获得字符串的长度
  27.             if
  28.                 Len =:= 0 ->  %%遇到空行,停止处理
  29.                     io:format("~nCome across blank line, stop!~n"),
  30.                     file:close(S),
  31.                     Dict;
  32.                 Len =/= 0 ->
  33.                     NewDict = string:concat(Dict,Tokens), %% 将Tokens连接到Dict尾
  34.                     readFile(S,NewDict) %%未读到空行、EOF,继续读取文本
  35.             end
  36.     end.
  37. writeToFile(FileOut,Tokens) ->
  38.     {ok,S} = file:open(FileOut,write),
  39.     Data = parse(Tokens,[]),
  40.     String  = lineToPara(Data),
  41.     io:format("~n~s~n",[String]),  %% 输出到屏幕
  42.     io:format(S,"~s~n",[String]). %% 输出到文件
  43.     %lists:foreach(fun({X}) -> io:format(S,"~s~n",[X]) end, Data).        
  44. lineToPara(Data) -> lineToPara(Data,"").
  45. lineToPara([],Ret) -> Ret;
  46. lineToPara(Data,Ret) ->
  47.     Last = lists:nth(1,Data),
  48.     Element = element(1,Last),
  49.     Temp = Ret ++ Element ++ "\n",
  50.     %io:format("~s",[Temp]),
  51.     lineToPara(lists:delete(Last,Data), Temp).
  52. %%解析单词列表
  53. parse(Tokens,Contents) ->
  54.     M =50,
  55.     {ALine,RemainToken} = parse_line(Tokens,M),
  56.     if
  57.         length(RemainToken) =:= 0 ->
  58.             Plain = plain(ALine),
  59.             io:format("last line----:~s~n",[Plain]),
  60.             NewContent = lists:append(Contents,[{Plain}]);
  61.         length(RemainToken) =/= 0 ->
  62.             NewContent = lists:append(Contents,[{ALine}]),
  63.             parse(RemainToken,NewContent)
  64.     end.
  65. plain(Last) ->plain(Last, "").
  66.     
  67. plain([], String) -> String;
  68. plain(L, String) ->
  69.     if
  70.         length(L) =:= 1 ->
  71.             Last = lists:nth(1,L),
  72.             NewS = string:concat(String,Last);
  73.         length(L) =/= 1 ->
  74.             First = lists:nth(1,L),
  75.             Temp = string:concat(First," "),
  76.             NewS = string:concat(String,Temp),
  77.             plain(lists:delete(First,L), NewS)
  78.     end.
  79. parse_line(Tokens,M) -> parse_line(Tokens,M,[]).
  80. %% @ Tokens: 当前剩余单词个数
  81. %% @ M:每行***字符阀值
  82. %% @ Line: 当前单词行列表
  83. parse_line([],_M,Line) -> {Line,[]};
  84. parse_line(Tokens,M,Line) ->
  85.     if
  86.         length(Line) =:= 0 ->
  87.             Element = lists:nth(1,Tokens),
  88.             NewL = length(Element),
  89.             NewToken  = lists:delete(Element,Tokens),
  90.             parse_line(NewToken,M,[Element]);
  91.         length(Line) =/= 0 ->
  92.             Element = lists:nth(1,Tokens),
  93.             NewL = len(Line)+length(Element)+length(Line),
  94.             case NewL > M of
  95.                     true ->
  96.                         String = toString(Line,M),
  97.                         {String,Tokens};
  98.                     false ->
  99.                         NewLine = lists:append(Line,[Element]),
  100.                         NewToken = lists:delete(Element,Tokens),
  101.                         parse_line(NewToken,M,NewLine)
  102.             end
  103.     end.
  104. %% 排版输出当前行Line数据
  105. toString(Line,M) -> 
  106.     Len = len(Line),
  107.     Blank = M - Len,
  108.     %io:format("Blank:~p Len:~p length(Line):~p ~n",[Blank,Len,length(Line)]),
  109.     Ret = justify(Line,Blank,length(Line)),
  110.     io:format("Mess------Ret:~s length:~p~n",[Ret,length(Ret)]),
  111.     Ret.
  112.     
  113. justify(Line,Blank,Len) -> justify(Line,Blank,Len,"").
  114. %% @Line: 一行字符串数据
  115. %% @Blank:需要的空格个数
  116. %% @Len: 当前行中剩余的单词个数
  117. %% @String: 返回一行矫正结果
  118. justify(Line,Blank,Len,String) ->
  119.     if
  120.         length(String) =:= 0 ->  %%初始情况
  121.             Temp = lists:nth(1,Line),
  122.             Avg = Blank div Len, %% 计算单词之间平均需要Avg个空格字符
  123.             NewLine = lists:delete(Temp,Line),
  124.             %io:format("1---avg: ~p Blank:~p  Len: ~p~n",[Avg,Blank,Len]),
  125.             case Avg =:= 0 of
  126.                 true ->
  127.                     NewS = while(1,Temp),
  128.                     justify(NewLine,Blank-1,length(NewLine),NewS);
  129.                 false ->
  130.                     NewS = while(Avg,Temp),
  131.                     justify(NewLine,Blank-Avg,length(NewLine),NewS)
  132.             end;
  133.         length(String) =/= 0 -> 
  134.             if
  135.                 length(Line) =:= 1 -> %%一行中***一个单词,返回结果
  136.                     Last = lists:nth(1,Line),
  137.                     NewS = while(Blank,String), %%将剩余的Blank个字符补全
  138.                     string:concat(NewS,Last);
  139.                 length(Line) =/= 1 ->
  140.                     First = lists:nth(1,Line),
  141.                     Avg = Blank div Len,
  142.                     NewLine = lists:delete(First,Line),
  143.                     %io:format("2---avg: ~p Blank:~p  Len: ~p~n",[Avg,Blank,Len]),
  144.                     case Avg =:= 0 of
  145.                         true ->
  146.                             NewS = while(1,String),
  147.                             justify(NewLine,Blank-1,length(NewLine), string:concat(NewS,First));
  148.                         false ->
  149.                             NewS = while(Avg,String),
  150.                             justify(NewLine,Blank-Avg,length(NewLine), string:concat(NewS,First))
  151.                     end
  152.             end
  153.     end.
  154. %% 功能: 子字符串后面加Count个空格
  155. while(0,String) -> String;
  156. while(Count,String) ->
  157.     NewS = string:concat(String," "),
  158.     while(Count-1,NewS).
  159. %%统计一行中字符的个数(只包括单词字符,不包括空格)
  160. len(Line) -> len(Line,0).
  161. len([],Len) -> Len;
  162. len([H|T],Len) ->
  163.     len(T,Len+length(H)).

看起来比较复杂,其实思路比较简单:

1.将文本切分单词列表;

2.设定一个阀值M,将每行的字符设置为M,同时必须保证单词不能分割;

我觉得此程序的难度主要是设定单词与单词之间的空格字符的问题,我的思路是:根据每行中所有单词所占的字符个数不同,求出单词之间空格的平均Avg个数,一般情况,单词与单词之间的字符个数为1,因此justify/4很好的处理了这个问题。

程序运行结果示意图如下:

test.txt文件内容:

结果如下:

  好了,文档排版Erlang实战就到此为止了。说个小问题,我在Erlang实战:杨辉三角、选择排序、集合交与并中的实战2:选择排序中不是用了两次列表逆置来讲一个元素加入到列表尾吗,其实Erlang中模块lists:append可以解决这个问题,我在本文代码中多次用到了这个函数,如:NewLine = lists:append(Line,[Element]),意思就是讲Element元素加入到列表尾,但是append函数的两个参数必须都为列表,因此Element元素需以列表形式加入:[Element],可见世上存在这样一条真理:只有你不知道的,没有不可能发生的。

原文:http://www.cnblogs.com/itfreer/archive/2012/05/08/Erlang_in_practise_text-process.html

本文题目:Erlang实战文本排版
网站链接:http://www.shufengxianlan.com/qtweb/news17/440517.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联