免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 24437 | 回复: 9

【分享】Perl读取和生成、显示UTF-8文本、UTF-16文本 [复制链接]

论坛徽章:
0
发表于 2013-12-30 21:44 |显示全部楼层
本帖最后由 paktc 于 2013-12-31 18:04 编辑

    去年的时候就看了网上有关的一些文章。但也就是临时需要,临时看看。
没有去探索究竟是咋回事。最近又翻了不少文章,做了一个小小的总结,结合了自己的观点。
以下讨论全部是基于windows xp平台。

参考文章:
关于Perl对中文的处理问题
有了这篇文章我们知道以utf-8形式保存的perl脚本,如何在屏幕上输出我们想要的中文。

Perl Unicode全攻略
这篇就更详细了,如何转码,有几种方法,终端显示、GUI的时候是以什么编码输出,应有尽有

Perl如何创建一个UTF-8的文件
CSDN的一个问答贴(代码见7楼、11楼)

在windows下读取utf-8文本一定会遇到BOM的问题,于是下面几个文章也少不了了:
UTF-8、BOM、<feff>的问题
「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别?
Windows 记事本的 ANSI、Unicode、UTF-8 这三种编码模式有什么区别?

     好了,资料那么多,其实都轮不到我说什么。做了几种情况下的测试,在ANSI格式下保存的perl脚本
处理utf-8文本的时候遇到了不少头痛的问题,最后自己做了一些总结,一句话描述就是:
用ANSI格式保存的perl 如何正确显示、读取、生成 UTF-8格式的文本。

用"中文"两个字作为示例,先做一个简单的解析,稍后作为参考:
  1. use Encode;
  2. $text="中文";
  3. printf "“$text”一词在三种状态下的编码数据:\n";
  4. printf("%-25s","原GB2312编码:");
  5. xcode($text,'x');
  6. printf("%-25s","根据其编码信息,解码为统一码:");
  7. xcode(decode('gb2312',$text),'x');
  8. printf("%-25s","按UTF-8编码:");
  9. xcode(encode('utf8',decode('gb2312',$text)),'x');
  10. print "\n";
  11. <STDIN>;

  12. #一个用来显示编码的函数
  13. sub xcode {
  14.         # xcode("string",'Mode'); Mode = x(hex), b(bin), d(int)
  15.         for my $v ( split(//,$_[0]) ) {
  16.                 print sprintf ("%l$_[1] ",ord($v));        
  17.         }
  18.         print "\n" if (!defined $_[2]);
  19. }

复制代码

“中文”一词在三种状态下的编码数据:
原GB2312编码:            d6 d0 ce c4
按GB2312解码为统一码:    4e2d 6587
按UTF-8编码:             e4 b8 ad e6 96 87


$data=decode('gb2312',$text) 对 $text 解码成 “统一码” 到 $data (参考 http://zh.wikipedia.org/wiki/Unicode
这个过程就像有一个八进制的数字要转为十六进制,我们暂时先转到十进制,然后再转十六进制。
为什么不直接转呢?只因为我们想要先知道这个数在我们熟悉的形式是怎么样的,让我们有一个清楚的对照。

"写"的操作
(脚本用ANSI文本形式保存)
  1. use Encode;
  2. #生成utf-8文本

  3. #正确的方法1
  4. open WRT,">:utf8","UTF-8 1.txt";
  5. print WRT decode('gb2312',"中文");  #通过decode解码为“统一码”
  6. close WRT;                             #然后perl以:utf8形式写入文本时帮我们将“统一码”转为UTF-8编码

  7. #错误示例
  8. open WRT,">:utf8","UTF8 ERR.txt";
  9. print WRT "中文";                   #此时"中文"的编码还是GB2312的形式
  10. close WRT;                          #perl将其当做“中间数据”直接转为utf8将得到错误的结果

  11. #正确的方法2
  12. open WRT,">:raw","UTF8 2.txt";
  13. print WRT encode("utf-8",decode('gb2312',"中文"));  #用:raw模式,直接以UTF-8编码的形式写入。
  14. close WRT;
复制代码
用notepad打开三种方法输出的文本显示如下:
三种结果.jpg

    也就是说,要让perl输出时做出正确的转换,先要把数据转化为 “通用” 的数据。
或者就干脆不要让perl帮你做转换,自己用函数编码出正确的UTF-8格式的“中文”编码,
并以:raw 形式写入文本。

下面是读取、在终端输出的操作:(脚本以ANSI格式保存)
错误的示例:
  1. use Encode;

  2. open READ,"<:utf8","UTF-8 1.txt";
  3. $text=<READ>;
  4. print $text;
  5. close READ;
  6. <STDIN>;
复制代码
原本的中文被显示为:涓 枃 并提示wide character。
      原因是perl用<:utf8 模式读取的时候,分析了utf-8编码并转码为“统一码”:
0x4e2d 0x6587,在cmd终端上显示需要再编码为GB2312格式。把print $text 改
为 print encode('gb2312',$text);就可以显示正确的结果

      或者用<:raw模式读入,得到的是UTF-8编码的数据,
先 decode('utf-8',$text) 转到中间数据  然后再将返回的结果encode('gb2312', ... 并打印到终端

  1. use Encode;

  2. open READ,"<:raw","UTF-8 1.txt";
  3. $text=<READ>;
  4. print encode('gb2312',decode('utf-8',$text));
  5. close WRT;
  6. <STDIN>;
复制代码
windows下读取带BOM(\xef\xbb\xbf 位于文件头)的UTF-8文本
方法有几种:
一、"<:raw"模式读入文本数据,去掉\xef\xbb\xbf 对剩下的数据转码。
二、用 <:utf8模式读取(<:raw读取后进行decode也行) perl不会帮你去掉\xef\xbb\xbf
而是像对待所有文本一样,对其转码,变成了\x{feff} ,这个时候去掉\xfe\xff 就是了。
  1. use Encode;

  2. #第一种方法
  3. open FILE, "<:raw","UTF-8 with BOM.txt";
  4. $raw=<FILE>;
  5. $raw=~s/\xef\xbb\xbf//;
  6. print encode('gb2312',decode('utf-8',$raw));
  7. close FILE;

  8. #第二种方法A
  9. open FILE, "<:utf8","UTF-8 with BOM.txt";
  10. $data=<FILE>;
  11. $data=~s/\x{feff}//;
  12. print encode('gb2312',$data);
  13. close FILE;

  14. #第二种方法B
  15. open FILE, "<:raw","UTF-8 with BOM.txt";
  16. $raw=<FILE>;
  17. $data=decode('utf-8',$raw);
  18. $data=~s/\x{feff}//;
  19. print encode('gb2312',$data);
  20. close FILE;
  21. <STDIN>;
复制代码
2013-12-30
补充,以上所说的“统一码”,请参考 http://zh.wikipedia.org/wiki/Unicode

2013-12-31
读windows下的unicode文本(UTF-16)
这里一定会遇到BOM,参考这篇文章:
Unicode签名bom
         所谓的unicode保存的文件实际上是utf-16,只不过恰好跟unicode的码相同而已,
    在概念上unicode与utf是两回事,unicode是内存编码表示方案,而utf是如何保存和
    传输unicode的方案。

示例文本unicode.txt(另存的时候选unicode)
  • 中国制造
  • 第二行


第一行的编码是:ff fe 2d 4e fd 56 36 52 20 90 d 0 a   
第二行的编码是:0 2c 7b 8c 4e 4c 88
(ff fe 就是文件头,BOM)

decode('utf-16',$uni_str) 的时候和处理UTF-8的时候是不一样的。它会帮你去掉BOM。
所以不需要手动去掉\xFF\xFE ,在处理文本第二行的时候提示
UTF-16:Unrecognised BOM 2c ……   因为第二行是不带BOM信息的,所以
把全文合并到一个变量中,做一次处理就可以得到正确的结果



论坛徽章:
3
CU十二周年纪念徽章
日期:2013-10-24 15:41:34子鼠
日期:2013-12-14 14:57:19射手座
日期:2014-04-25 21:23:23
发表于 2013-12-30 21:53 |显示全部楼层
这个不错  支持下

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2013-12-31 08:49 |显示全部楼层
Mark~                                    

论坛徽章:
1
双子座
日期:2013-11-06 17:18:01
发表于 2013-12-31 09:09 |显示全部楼层
支持一下。

论坛徽章:
5
丑牛
日期:2014-01-21 08:26:26卯兔
日期:2014-03-11 06:37:43天秤座
日期:2014-03-25 08:52:52寅虎
日期:2014-04-19 11:39:48午马
日期:2014-08-06 03:56:58
发表于 2013-12-31 15:03 |显示全部楼层
支持 {:2_172:}

论坛徽章:
0
发表于 2013-12-31 20:47 |显示全部楼层
本帖最后由 paktc 于 2013-12-31 22:01 编辑

这里咨询大家一个问题,
在尝试写UTF-16BE文本的时候,\r\n 写入文本后,编码总是不正确。
其他字符比如 tab 都正常,这个问题如何解决?

代码保存为ANSI格式
  1. use Encode;
  2. open WRT,'>:encoding(utf-16)','encoding[utf-16].txt' or die "$!";
  3. print WRT decode('gb2312',"中        文\r\n");      # 1
  4. close WRT;
复制代码
好像是activeperl的问题?在ubuntu写了个脚本(用utf-8格式保存的),

  1. #!/usr/bin/perl -s
  2. use Encode;
  3. chdir "~/桌面";
  4. open WRT,">encoding(UTF-16):crlf","new.x";
  5. print WRT decode('utf-8',"中文\n");
  6. close WRT;
复制代码
生成的txt文件复制到windows查看,换行正常。
同样的脚本复制到windows执行,行尾就变成了两个黑色符号。
……

Using :encoding and :crlf together?
The :crlf PerlIO layer doesn't like the :encoding layer.

在这里找到了最终答案 关键词 "perl  write utf-16 text crlf"
Writing a Unicode file via perl ...
http://blogs.msdn.com/b/brettsh/archive/2006/06/07/620986.aspx

论坛徽章:
0
发表于 2019-03-08 10:03 |显示全部楼层
留个记号,备用。
虽然用处理过许多中文文件,但有些东西记不住,经常折腾好半天才解决。

论坛徽章:
0
发表于 2019-05-06 00:01 |显示全部楼层
支持一下。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP