[Erlang 0107] Erlang实现文本截断

时间:2022-06-01 16:17:16
   抽时间处理一下之前积压的一些笔记.前段时间有网友 @稻草人 问字符串截断的问题"各位大侠 erlang截取字符串一般用哪个函数啊",有人支招用string:substr/3,紧接着他补充了一下"大侠们 一个字符串有汉字和字母组合我想截取 但是不管用什么方法每个汉字的长度都是3 字母是1 截取出来总是有乱码 还望高手们赐教",我们一步步看看这个问题.
 
  在Eshell先看下什么情况,貌似结果很理想啊,但是考虑到Erlang Shell和文件对编码的处理方式不一致,还是要写段代码测试下
 
> string:substr("abcd我们就是喜欢Erlang,就是喜欢OTP",,).
[,,]
> io:format("~ts",[v()]).
d我们ok
 
同样的代码贴到文件里面,编译后执行,看结果:
Eshell V5.10.2  (abort with ^G)
> u:sub().
[,,]
> io:format("~ts",[v()]).
dæok
> q().
ok
>
  傻眼了吧,之所以出现这种情况是因为EShell是按照UTF-8编码读取的代码,而Erlang编译器是按照ISO-latin-1(ISO-8859-1)编码进行代码解析的,所以就出现上面的不一致的"怪现象"了.怎么解决?如果你和我也是R16B01或更高版本,那么就简单了,只需要在源代码文件的头部添加一个文件编码的声明即可:
 
%% coding: utf- 
重新编译,之后执行结果:
Eshell V5.10.2  (abort with ^G)
> u:sub().
[,,]
> io:format("~ts",[v()]).
d我们ok
  这个解决方案见Erlang的epp模块,epp模块是Erlang源代码的预处理器(处理宏替换,include文件),所以编码问题它会首当其冲遇到.看代码片段,可知默认编码是latin1,目前支持的编码选项有latin-1和utf-8
 
-define(DEFAULT_ENCODING, latin1).

-spec default_encoding() -> source_encoding().

default_encoding() ->
?DEFAULT_ENCODING. -spec encoding_to_string(Encoding) -> string() when
Encoding :: source_encoding(). encoding_to_string(latin1) -> "coding: latin-1";
encoding_to_string(utf8) -> "coding: utf-8".
 
 
R16B的epp模块文档中多了下面这段说明:
 
The Erlang source file encoding is selected by a comment in one of the first two lines of the source file. The first string that matches the regular expression coding\s*[:=]\s*([-a-zA-Z0-9])+ selects the encoding. If the matching string is not a valid encoding it is ignored. The valid encodings are Latin-1 and UTF-8 where the case of the characters can be chosen freely. 
 在其它语言可以看到一些很奇葩的写法比如在XX管理系统中用中文定义各种类,属性,实现了所谓"中文编程".在Erlang中目前只能是string和注释部分使用unicode,其它部分还要再ISO-latin-1的编码范围内选择.看下文档:
 
As of Erlang/OTP R16 Erlang source files can be written in either UTF-8 or bytewise encoding (a.k.a. latin1 encoding). The details on how to state the encoding of an Erlang source file can be found in epp(3). Strings and comments can be written using Unicode, but functions still have to be named using characters from the ISO-latin-1 character set and atoms are restricted to the same ISO-latin-1 range. These restrictions in the language are of course independent of the encoding of the source file. Erlang/OTP R18 is expected to handle functions named in Unicode as well as Unicode atoms. http://www.erlang.org/doc/apps/stdlib/unicode_usage.html
  
 

R16B之前版本

   
   看了上面的解决方法估计是有人欢喜有人愁,不是所有人都升级到了R16B,特别是R16B的一些 Breaking Changes 更是很多人暂时搁置了升级计划.那在之前的版本如何解决这个问题呢?之前曾经用过ErlDTL,其中有一个Web中常见的功能就是文本超长后截断然后用省略号显示.按照这个线索,找到 erlydtl_filters.erl 代码.把文本截断的两个方法完整的剥离出来,两个方法其中一个是按字符截断,一个是按照词截断.代码如下:
-module(u).
-compile(export_all).
test() ->
t("abcd我们就是喜欢Erlang,就是喜欢OTP",). test2() ->
tw("Youth is not a time of life; it is a state of mind; it is not a matter of
rosy cheeks, red lips and supple knees; it is a matter of the will, a
quality of the imagination, a vigor of the emotions; it is the freshness of
the deep springs of life.",10). dump(FileName,Data)->
file:write_file(FileName, io_lib:fwrite("~s.\n", [Data])). sub()->
string:substr("abcd我们就是喜欢Erlang,就是喜欢OTP",,). t(Input,Max) ->
truncatechars(Input,Max). tw(Input,Max) ->
truncatewords(Input,Max). %% @doc Truncates a string after a certain number of characters.
truncatechars(_Input, Max) when Max =< ->
"";
truncatechars(Input, Max) when is_binary(Input) ->
list_to_binary(truncatechars(binary_to_list(Input), Max));
truncatechars(Input, Max) ->
truncatechars(Input, Max, []). %% @doc Truncates a string after a certain number of words.
truncatewords(_Input, Max) when Max =< ->
"";
truncatewords(Input, Max) when is_binary(Input) ->
list_to_binary(truncatewords(binary_to_list(Input), Max));
truncatewords(Input, Max) ->
truncatewords(Input, Max, []). truncatechars([], _CharsLeft, Acc) ->
lists:reverse(Acc);
truncatechars(_Input, , Acc) ->
lists:reverse("..." ++ Acc);
truncatechars([C|Rest], CharsLeft, Acc) when C >= # ->
truncatechars(Rest, CharsLeft + , [C|Acc]);
truncatechars([C|Rest], CharsLeft, Acc) when C >= # ->
truncatechars(Rest, CharsLeft + , [C|Acc]);
truncatechars([C|Rest], CharsLeft, Acc) when C >= # ->
truncatechars(Rest, CharsLeft + , [C|Acc]);
truncatechars([C|Rest], CharsLeft, Acc) when C >= # ->
truncatechars(Rest, CharsLeft + , [C|Acc]);
truncatechars([C|Rest], CharsLeft, Acc) when C >= # ->
truncatechars(Rest, CharsLeft, [C|Acc]);
truncatechars([C|Rest], CharsLeft, Acc) ->
truncatechars(Rest, CharsLeft - , [C|Acc]). truncatewords(Value, _WordsLeft, _Acc) when is_atom(Value) ->
Value;
truncatewords([], _WordsLeft, Acc) ->
lists:reverse(Acc);
truncatewords(_Input, , Acc) ->
lists:reverse("..." ++ Acc);
truncatewords([C1, C2|Rest], WordsLeft, Acc) when C1 =/= $\ andalso C2 =:= $\ ->
truncatewords([C2|Rest], WordsLeft - , [C1|Acc]);
truncatewords([C1|Rest], WordsLeft, Acc) ->
truncatewords(Rest, WordsLeft, [C1|Acc]).
 
测试代码如下:
test() ->
t("abcd我们就是喜欢Erlang,就是喜欢OTP",). dump(FileName,Data)->
file:write_file(FileName, io_lib:fwrite("~s.\n", [Data])). Eshell V5.10.2 (abort with ^G)
> u:test().
[,,,,,,,,,,,,,,,
,,,,,,,,,]
>
> u:dump("u_result",v()).
ok
>
  执行一下结果,那么这次的结果是不是正确呢?[97,98,99,100,230,136,145,228,187,172,229,176,177,230,152,
175,229,150,156,230,172,162,46,46,46]这样的结果着实很难判断,我们使用上面的dump方法把它写到文本里面看看.看看结果:
[root@nimbus demo]# cat u_result
abcd我们就是喜欢....
结果正确,OK,下面我们看ErlyDTL是怎么实现的.我们知道Unicode是变长编码,不同范围的字符使用不同的长度编码.比如下面我们看"开心"这两个字的编码过程,下面的是字符范围和编码模板对照表,更多信息可以参考: http://www.cl.cam.ac.uk/~mgk25/unicode.html
 
Unicode编码(16进制) 
UTF-8 字节流模板
000000 - 00007F
0xxxxxxx
000080 - 0007FF
110xxxxx 10xxxxxx
000800 - 00FFFF
1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 
我们先在EShell中把需要的几个数据做出来,然后按照根据字符范围选择三字节编码模板:
 
Eshell V5.10.2  (abort with ^G)
> unicode:characters_to_binary("开心").
<<,,,,,>>
> unicode:characters_to_list("开心").
[,]
> integer_to_list(,).
""
> integer_to_list(,).
""
> integer_to_list(,).
""
 [Erlang 0107] Erlang实现文本截断
 
truncatechars实现的要点就是根据上面的字符范围和字节变长规则做了判断,这个从Guard部分的代码就能看出来,在其它语言里面实现这个逻辑也大多如此思路.truncatewords方法实现的是按照单词进行截断,需要判断一下单词边界,比如 "Youth is not a time of life; it is a state of mind; it is not a matter of rosy cheeks, red lips and supple knees; it is a matter of the will, a quality of the imagination, a vigor of the emotions; it is the freshness of the deep springs of life."这段文字做单词截断的结果是"Youth is not a time of life; it is a..."这个就没有什么好说的了.
 
 
 
 
最后,强烈推荐一个Erlang资源站:
 

[Erlang 0107] Erlang实现文本截断

[Erlang 0107] Erlang实现文本截断的更多相关文章

  1. &lbrack;Erlang 0124&rsqb; Erlang Unicode 两三事 - 补遗

    最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本 ...

  2. &lbrack;Erlang 0129&rsqb; Erlang 杂记 VI

    把之前阅读资料的时候记下的东西,整理了一下. Adding special-purpose processor support to the Erlang VM   P23 简单介绍了Erlang C ...

  3. &lbrack;Erlang 0122&rsqb; Erlang Resources 2014年1月~6月资讯合集

    虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/   ...

  4. &lbrack;Erlang 0105&rsqb; Erlang Resources 小站 2013年1月~6月资讯合集

    很多事情要做,一件一件来; Erlang Resources 小站 2013年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/     ...

  5. Erlang 103 Erlang分布式编程

    Outline 笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明 2014-11-23 A Outl ...

  6. 解决NSTextContainer分页时文本截断问题

    解决NSTextContainer分页时文本截断问题 NSTextContainer与NSLayoutManager配合使用可以将大文本文件分页,但是,分页过程中会遇到问题,显示字符被截断的问题:) ...

  7. &lbrack;Erlang 0057&rsqb; Erlang 排错利器&colon; Erlang Crash Dump Viewer

    http://www.cnblogs.com/me-sa/archive/2012/04/28/2475556.html Erlang Crash Dump Viewer真的是排错的天兵神器,还记得我 ...

  8. &lbrack;Erlang 0119&rsqb; Erlang OTP 源码阅读指引

      上周Erlang讨论群里面提到lists的++实现,争论大多基于猜测,其实打开代码看一下就都明了.贴出代码截图后有同学问这代码是哪里找的?   "代码去哪里找?",关于Erla ...

  9. &lbrack;Erlang 0123&rsqb; Erlang EPMD

     epmd进程和Erlang节点进程如影随形,在Rabbitmq集群,Ejabberd集群,Couchbase集群产品文档中都会有相当多的内容讲epmd,epmd是什么呢?   epmd 是Erlan ...

随机推荐

  1. 【poj1160】 Post Office

    http://poj.org/problem?id=1160 (题目链接) 题意 按照递增顺序给出一条直线上坐标互不相同的n个村庄,要求从中选择p个村庄建立邮局,每个村庄使用离它最近的那个邮局,使得所 ...

  2. chrome https添加信任

    在浏览器地址栏输入:chrome://net-internals/#hsts 然后到Add domain下,Domain添上诸如google.com和google.com.hk ,并勾选Include ...

  3. QObject&colon;&colon;deleteLater&lpar;&rpar;并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象 good

    程序编译运行过程很顺利,测试的时候也没发现什么问题.但后来我随手上传了一个1G大小的文件,发现每次文件上传到70%左右的时候程序就崩溃了,小文件就没这个问题.急忙打开任务管理器,这才发现上传文件的时候 ...

  4. BackgroundWorker的使用

    一个程序中需要进行大量的运算,并且需要在运算过程中支持用户一定的交互,为了获得更好的用户体验,使用BackgroundWorker来完成这一功能.   基本操作: bgw.RunWorkerAsync ...

  5. TMS320C54x系列DSP的CPU与外设——第1章 绪论

    第1章 绪论 TMS320C54x DSP是TMS320系列DSP产品中的定点数字信号处理器.C54x DSP满足了实时嵌入式应用的一些要求,例如通信方面的应用. C54x的*处理单元(CPU)具有 ...

  6. django查询常用操作符及models和admin的写法

    以Publisher.Author.Book的model为例子 #coding=utf-8 from django.db import models # Create your models here ...

  7. 设计模式&amp&semi;UML学习

    1. 1.1 1.2 2. 2.1 2.2 3.参考文档 [1] 陈金荣:http://blog.csdn.net/cjr15233661143/article/details/8532997 [2] ...

  8. HDU1506&lpar;单调栈或者DP) 分类: 数据结构 2015-07-07 23&colon;23 2人阅读 评论&lpar;0&rpar; 收藏

    Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...

  9. Json填充到Form中

    很多框架都支持将json解释到grid的或者form中,个人手痒,自己写了一个.所用到的内容主要是javascript对json的遍历.如: for (var key in json) { alert ...

  10. 面向面试编程——javascript对象的几种创建方式

    javascript对象的几种创建方式 总共有以下几个模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.混合构造函数和原型模式 5.动态原型模式 6.寄生构造函数模式 7.稳妥构造函数模式 1 ...