Perl Unicode 全攻略:
耐心看完本文,相信你今后在unicode处理上不同再有问题:
在Perl看来,字符串只有两种形式。一种是octets,即8位序列,也就是我们通常说的字节数组
另一种utf8 编码的字符串,perl 管它叫string.
也就是说:Perl 只认识两种编码:ascii(octets) 和utf8 (字符串)
utf8 flag:
那么 perl 如何却动一个字符串是octets还是utf8编码的字符串呢?
perl 可没有什么智能,它完全是靠字符串的utf8 flag.
在perl 内部,字符串结构由两部分组成:数据和utf8 flag,比如字符串"中国"在perl内部的存储是这样:
utf8 flag 数据
On 中国
如果 utf8 flag 是on的话,perl 就会把中国当成utf8 字符串来处理,
如果 utf8 flag 为off,perl就会把他当成octets来处理。
所有的字符串相关的函数包括正则表达式都会受utf8 flag的影响,让我们来看个例子
[root@jxglapp1 pfwx-master]# cat mojo.pl
use lib "./lib";
use mojo::Client;
use Data::Dumper;
use Mojolicious::Lite;
use JSON qw/encode_json decode_json/;
use Encode;
no strict;
no warnings;
use JSON;
use POSIX;
use JSON::RPC::Client;
use Data::Dumper;
use URI::Escape;
my $client = mojo::Client->new();
print Dumper($client);
get '/api/SMSsendx' => sub {
my $c = shift;
my $sourceip=$c->param('sourceip');
my $message = $c->param('message');
print "Length==";
print length($message)."\n";
print "\$message is $message\n";
sleep 30;
app->start;
[root@jxglapp1 pfwx-master]# vim mojo.pl
[root@jxglapp1 pfwx-master]# morbo mojo.pl
Server available at http://127.0.0.1:3000
$VAR1 = bless( {
'client_version' => '1.0'
}, 'mojo::Client' );
[Mon Mar 13 08:23:47 2017] [debug] GET "/api/SMSsendx"
[Mon Mar 13 08:23:47 2017] [debug] Routing to a callback
Length==2
$message is 中国
$flag===1
------------------------------------------------------------
[oracle@oadb utf-8]$ cat a3.pl
use Encode;
use strict;
my $str = "中国";
print length($str);
print "\n";
Encode::_utf8_on($str);
print length($str) . "\n";
Encode::_utf8_off($str);
print length($str) . "\n";
[oracle@oadb utf-8]$ perl a3.pl
6
2
6
这里我们使用Encode模块的_utf8_on 函数和_utf8_off 函数来开关字符串"中国"的utf8 flag.
可以看到,utf8 flag打开的时候,"中国"被当成utf8 字符串处理,所以其长度为2.
utf8 flag关闭的时候,"中国"被当成octets(字节数组)处理,出来的长度为6
在看看正则表达式:
[oracle@oadb utf-8]$ cat a4.pl
use Encode;
use strict;
my $a = "1中国2";
print "\$a is $a\n";
print length($a)."\n";
$a =~ s/\W+//g;
print "\$a is $a\n";
[oracle@oadb utf-8]$ perl a4.pl
$a is 1中国2
8
$a is 12
[oracle@
[oracle@oadb utf-8]$ cat a4.pl
use Encode;
use strict;
my $a = "1中国2";
Encode::_utf8_off($a);
print "\$a is $a\n";
print length($a)."\n";
$a =~ s/\W+//g;
print "\$a is $a\n";
[oracle@oadb utf-8]$ perl a4.pl
$a is 1中国2
8
$a is 12
[oracle@oadb utf-8]$ cat a4.pl
use Encode;
use strict;
my $a = "1中国2";
Encode::_utf8_on($a);
print "\$a is $a\n";
print length($a)."\n";
$a =~ s/\W+//g;
print "\$a is $a\n";
[oracle@oadb utf-8]$ perl a4.pl
Wide character in print at a4.pl line 7.
$a is 1中国2
4
Wide character in print at a4.pl line 12.
$a is 1中国2
此时匹配不到:
如何确定一个字符串的utf8 flag 是否开启?使用Encode::is_utf8($str)
这个函数并不是用来检测一个字符串是不是utf8 编码,而是仅仅看看它的utf8 flag是否开启
[oracle@oadb utf-8]$ cat a4.pl
use Encode;
use strict;
my $a = "1中国2";
#Encode::_utf8_on($a);
print "\$a is $a\n";
print length($a)."\n";
$a =~ s/\W+//g;
print "\$a is $a\n";
my $flag=Encode::is_utf8($a);
print "\$flag is $flag\n";
[oracle@oadb utf-8]$ perl a4.pl
$a is 1中国2
8
$a is 12
$flag is
[oracle@oadb utf-8]$ cat a4.pl
use Encode;
use strict;
my $a = "1中国2";
Encode::_utf8_on($a);
print "\$a is $a\n";
print length($a)."\n";
$a =~ s/\W+//g;
print "\$a is $a\n";
my $flag=Encode::is_utf8($a);
print "\$flag is $flag\n";
[oracle@oadb utf-8]$ perl a4.pl
Wide character in print at a4.pl line 7.
$a is 1中国2
4
Wide character in print at a4.pl line 12.
$a is 1中国2
$flag is 1
unicode 转码:
如果你有一个字符串"中国",它是gb2312编码的。如果它的utf-8 flag是关闭的,
它就会被当成octets来处理,length()会返回4,这通常不是你想要的。
而 如果你开启它的utf8 flag,则它会被当做utf8 编码的字符串来处理。
由于它本来的编码是gb2312的,不是utf-8的,这就可能导致错误发生。
解决的方法很显然,如果你的字符串本来不是utf8编码的,应该先把它转成utf8编码,并且
使它的utf8 flag处于开启状态。对于一个gb2312编码的字符串,你可以使用:
程序代码:
$str = Encode::decode("gb2312", $str);
字符串连接:
. 是字符串连接操作符,连接两个字符串时,如果两个字符串的utf8 flag都是off,
那么结果字符串也是off。
如果其中任何一个字符串的utf8 flag是on的话,那么结果字符串的utf8 flag将是on.
perl unicode编程基本原则
对于任何要处理的unicode 字符串, 1)把它的编码转换成utf8
2) 开启它的utf8 flag
字符串来源:
为了应用上面说到的基本原则,我们首选要知道字符串本来的编码和utf8 flag开关情况,这里我们讨论几种情况:
1) 命令行参数和标准输入。
从命令行参数或标准输入(STDIN)来的字符串, 它的编码跟locale有关。
如果你的locale是zh_CN或zh_CN.gb2312, 那么进来的字符串就是gb2312编码,
如果你的locale是zh_CN.gbk, 那么进来的编码就是gbk, 如果你的编码是zh_CN.UTF8, 那进来的编码就是utf8。
不管是什么编码, 进来的字符串的utf8 flag都是关闭的状态。
2)
你的源代码里的字符串。
这要看你编写源代码用的是什么编码,在editplus里, 你可以通过“文件”->“另存为”查看和更改编码。 在linux下, 你可以cat一个源代码文件, 如果中文正常显示, 说明源代码的编码跟locale是一致的。源代码里的字符串的utf8 flag同样是关闭的状态。
如果你的源代码里含有中文, 那么你最好遵循这个原则: 1) 编写代码时使用utf8编码, 2)在文件的开头加上“use utf8;”语句。这样, 你源代码里的字符串就都会是utf8编码的, 并且utf8 flag也已经打开。