- 论坛徽章:
- 0
|
本帖最后由 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格式的文本。
用"中文"两个字作为示例,先做一个简单的解析,稍后作为参考:- use Encode;
- $text="中文";
- printf "“$text”一词在三种状态下的编码数据:\n";
- printf("%-25s","原GB2312编码:");
- xcode($text,'x');
- printf("%-25s","根据其编码信息,解码为统一码:");
- xcode(decode('gb2312',$text),'x');
- printf("%-25s","按UTF-8编码:");
- xcode(encode('utf8',decode('gb2312',$text)),'x');
- print "\n";
- <STDIN>;
- #一个用来显示编码的函数
- sub xcode {
- # xcode("string",'Mode'); Mode = x(hex), b(bin), d(int)
- for my $v ( split(//,$_[0]) ) {
- print sprintf ("%l$_[1] ",ord($v));
- }
- print "\n" if (!defined $_[2]);
- }
复制代码
“中文”一词在三种状态下的编码数据:
原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文本形式保存)- use Encode;
- #生成utf-8文本
- #正确的方法1
- open WRT,">:utf8","UTF-8 1.txt";
- print WRT decode('gb2312',"中文"); #通过decode解码为“统一码”
- close WRT; #然后perl以:utf8形式写入文本时帮我们将“统一码”转为UTF-8编码
- #错误示例
- open WRT,">:utf8","UTF8 ERR.txt";
- print WRT "中文"; #此时"中文"的编码还是GB2312的形式
- close WRT; #perl将其当做“中间数据”直接转为utf8将得到错误的结果
- #正确的方法2
- open WRT,">:raw","UTF8 2.txt";
- print WRT encode("utf-8",decode('gb2312',"中文")); #用:raw模式,直接以UTF-8编码的形式写入。
- close WRT;
复制代码 用notepad打开三种方法输出的文本显示如下:
也就是说,要让perl输出时做出正确的转换,先要把数据转化为 “通用” 的数据。
或者就干脆不要让perl帮你做转换,自己用函数编码出正确的UTF-8格式的“中文”编码,
并以:raw 形式写入文本。
下面是读取、在终端输出的操作:(脚本以ANSI格式保存)
错误的示例:- use Encode;
- open READ,"<:utf8","UTF-8 1.txt";
- $text=<READ>;
- print $text;
- close READ;
- <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', ... 并打印到终端
- use Encode;
- open READ,"<:raw","UTF-8 1.txt";
- $text=<READ>;
- print encode('gb2312',decode('utf-8',$text));
- close WRT;
- <STDIN>;
复制代码 windows下读取带BOM(\xef\xbb\xbf 位于文件头)的UTF-8文本
方法有几种:
一、"<:raw"模式读入文本数据,去掉\xef\xbb\xbf 对剩下的数据转码。
二、用 <:utf8模式读取(<:raw读取后进行decode也行) perl不会帮你去掉\xef\xbb\xbf
而是像对待所有文本一样,对其转码,变成了\x{feff} ,这个时候去掉\xfe\xff 就是了。- use Encode;
- #第一种方法
- open FILE, "<:raw","UTF-8 with BOM.txt";
- $raw=<FILE>;
- $raw=~s/\xef\xbb\xbf//;
- print encode('gb2312',decode('utf-8',$raw));
- close FILE;
- #第二种方法A
- open FILE, "<:utf8","UTF-8 with BOM.txt";
- $data=<FILE>;
- $data=~s/\x{feff}//;
- print encode('gb2312',$data);
- close FILE;
- #第二种方法B
- open FILE, "<:raw","UTF-8 with BOM.txt";
- $raw=<FILE>;
- $data=decode('utf-8',$raw);
- $data=~s/\x{feff}//;
- print encode('gb2312',$data);
- close FILE;
- <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信息的,所以
把全文合并到一个变量中,做一次处理就可以得到正确的结果
|
|