抽时间处理一下之前积压的一些笔记.前段时间有网友 @稻草人 问字符串截断的问题"各位大侠 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(,).
""
 
 
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实现文本截断的更多相关文章

  1. [Erlang 0124] Erlang Unicode 两三事 - 补遗

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

  2. [Erlang 0129] Erlang 杂记 VI

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

  3. [Erlang 0122] Erlang Resources 2014年1月~6月资讯合集

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

  4. [Erlang 0105] 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. [Erlang 0057] Erlang 排错利器: Erlang Crash Dump Viewer

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

  8. [Erlang 0119] Erlang OTP 源码阅读指引

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

  9. [Erlang 0123] Erlang EPMD

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

随机推荐

  1. andriod 手机按键检测事件 onKeyDown() 简述

    函数原型: public boolean onKeyDown(int keyCode, KeyEvent event); 第一个参数是用户按下键时,当前所接收到的按键代号: 第二个参数是按键事件的对象 ...

  2. CSS3中border-radius、box-shadow与gradient那点事儿

    一.border-radius border-radius用于添加圆角边框,用处非常广泛. 1)一个值,代表了四个角 .radius-one { /* Safari 3-4, iOS 1-3.2, A ...

  3. c/c++ 函数、常量、指针和数组的关系梳理

    压力才有动力,15年中旬就要准备实习,学习复习学习复习学习复习学习复习……无限循环中,好记性不如烂笔头……从数组开始,为主干. c 的array由一系列的类型相同的元素构成,数组声明包括数组元素个数和 ...

  4. SQL SERVER 通用分页存储过程,两种用法任你选

    写在前面 从SQLSERVER 2005开始,提供了Row_Number()函数,利用函数生成的Index来处理分页,按照正常的逻辑思维都是传pageIndex和pageSize来完成分页,昨天前端和 ...

  5. Struts2 源码分析——DefaultActionInvocation类的执行action

    本章简言 上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识.我们也清楚的知道在执行用户action类实例之前,struts2会先去执行当前action类对应的拦截器.而关于在哪里执行a ...

  6. SpringData —— HelloWorld

    SpringData 简介 优点 简化数据库访问,减少数据访问层的开发量. 支持的数据库类型 支持 NoSQL 存储,如 MongoDB,Redis. 支持关系型数据存储技术,如 jdbc,jpa. ...

  7. 百度编辑器UEditor常用设置函数大全

    在线文档对UEditor说明不够全面,收集了一些常用的方法和基本设置,以供参考.1.创建编辑器UE.getEditor('editor', { initialFrameWidth:"100% ...

  8. Solr3.6.1 在Tomcat6下的环境搭建

    Solr3.6.1 在Tomcat6下的环境搭建 Tomcat路径:D:\prg\apache-tomcat-6-solr 简写为tomcat_home Solr文件下载保存路径为:F:\softwa ...

  9. Newtonsoft.Json(Json.Net)学习笔记

    Newtonsoft.Json 在Vs2013中就有自带的: 下面是Json序列化和反序列化的简单封装: /// <summary> /// Json帮助类 /// </summar ...

  10. c语言 sizeof理解

    1.基本数据类型 char :1     short:2   int 4    long 4   long long :8    float:4    double :8字节. 2.数组:对应的基本数 ...