Perl Unicode 全攻略:

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)."
"; 
      print "$message is $message
";
      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 "
";
Encode::_utf8_on($str);
print length($str) . "
";
Encode::_utf8_off($str);
print length($str) . "
";
[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
";
print length($a)."
";

$a =~ s/W+//g;

print "$a is $a
";

[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
";
print length($a)."
";

$a =~ s/W+//g;

print "$a is $a
";

[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
";
print length($a)."
";

$a =~ s/W+//g;

print "$a is $a
";

[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
";
print length($a)."
";

$a =~ s/W+//g;

print "$a is $a
";

my $flag=Encode::is_utf8($a);
print "$flag is $flag
";
[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
";
print length($a)."
";

$a =~ s/W+//g;

print "$a is $a
";

my $flag=Encode::is_utf8($a);
print "$flag is $flag
";
[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也已经打开。

原文地址:https://www.cnblogs.com/hzcya1995/p/13349822.html