Erlang实战文本排版

开发 开发工具
本文主要是通过Erlang实现简单的文本排版,借此学习Erlang、熟悉Erlang。希望对大家有所帮助。

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

  实战:文本排版。

  要求如下:

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

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

【编辑推荐】

  1. Erlang之父Joe Armstrong访谈:程序调试与啤酒
  2. Scala和Erlang,以及多核主导的未来
  3. Erlang面向分布与并发的编程语言
  4. 看Erlang中Actor模型的执行方式和优劣
  5. Erlang视点:并行计算和云计算
责任编辑:彭凡 来源: 博客园
相关推荐

2012-05-07 15:32:46

Erlang

2012-05-14 13:58:19

Erlang

2021-02-26 20:01:30

LaTex排版LaTeX排版

2010-09-10 14:54:12

DIV排版

2010-09-17 14:13:20

SIP业务Erlang

2021-02-24 16:15:20

LaTeX排版LaTeX列表

2021-02-24 16:50:07

LaTex排版表格

2012-05-07 08:47:25

Erlang

2021-09-27 14:44:48

鸿蒙HarmonyOS应用

2023-11-20 22:07:51

PythonPDF

2021-02-20 20:58:04

LaTeXTeXstudio排版文档

2014-12-08 14:15:48

Material De字体排版

2010-03-22 14:45:40

云计算

2009-04-22 09:20:26

Erlang并发函数式

2009-07-30 10:59:44

Scala和Erlan多核

2012-08-16 09:07:57

Erlang

2012-12-28 14:23:12

Android开发TextView

2010-09-08 17:20:42

CSS

2010-09-14 10:41:24

DIV+CSS排版

2010-09-13 13:21:29

CSS排版
点赞
收藏

51CTO技术栈公众号