1 / 3
Caption Text
2 / 3
Caption Two
3 / 3
Caption Three margin testing

Sunday, July 26, 2009

处理 UTF-8 编码文件时输出和写入乱码问题

解决处理 UTF-8 编码XML文件时,输出和写入乱码问题

以前用 Perl 处理 UTF-8 编码的 XML 文件时,总是遇到奇怪的问题:

单独输出读取的中文 XML 信息,没有问题,但是一旦和其他中文字符(比如定义的中文变量)混合输出就会出现乱码

eg:
$xml->{"chunk"}{"comment"} 包含了中文信息
my $cnstr="变量中的中文信息";

## 出现乱码
print $xml->{"chunk"}{"comment"} . $cnstr ;

## 正常
print $xml->{"chunk"}{"comment"};
print $cnstr;


仔细研究了下 UTF8 下中文处理 [转载]Perl中的unicode问题及解决办法(译),现在总算找到原因了:

UTF-8 编码的 XML 文件默认就含有 utf8 标记,而一般自定义的变量不含,两者混合输出时,Perl 会认为两者都不含或都含(看谁在前) utf8 标记,于是乱码就产生了。

解决办法:
读取 XML 文件后,用 Encode::_utf8_off($str) 来去掉 utf8 标记
写入 XML 文件前,用 Encode::_uft8_on($str) 加上 utf8 标记

如:
### 读取
$comment = $xml->{"chunk"}{"comment"};
Encode::_utf8_off($comment);
...

### 写入
$comment= param('comment'); ## 获取网页提交的信息
Encode::_utf8_on($comment);
$xml->{"chunk"}{"comment"} = $comment;
....

Perl笔记: UTF8下中文处理

Perl笔记: UTF8下中文处理

发信人: happierbee (吾生也有涯,而知也无涯), 信区: Perl
标 题: UTF8下中文处理
发信站: 水木社区 (Mon Sep 11 21:22:53 2006), 站内

我来总结一下吧。先建一个文件test.txt, 写上 中文 两个字。运行下面一个程序:

use strict;
use warnings;
use Encode;
my $str_code = "中文";
my $file = "test.txt";

open(my $fh, $file) || die "Can't open file $file: $!";
chomp(my $str_file = <$fh>);
close $fh;

open(my $fh_utf8, "<:utf8", $file) || die "Can't open file $file: $!";
chomp(my $str_fileu = <$fh_utf8>);
close $fh_utf8;

test_utf8($str_code, "code");
test_utf8($str_file, "file");
test_utf8($str_fileu, "file(utf8 layer)");
# print $str_code, "\n";
# print $str_file, "\n";
# print $str_fileu, "\n";

Encode::_utf8_on($str_code);
Encode::_utf8_on($str_file);
test_utf8($str_code, "code");
test_utf8($str_file, "file");

sub test_utf8 {
my ($str, $type) = @_;
if ( Encode::is_utf8($str) ) {
print "String from $type, utf8 flag is on."
} else {
print "String from $type, utf8 flag is off."
}
my @chars = split('', $str);
# print join("\t", @chars), "length: ", scalar(@chars);
if (scalar(@chars)==2) {
print " Can match chinese character.";
} else {
print " Can't match chinese character.";
}
print "\n";
}

结果如下:
String from code, utf8 flag is off. Can't match chinese character.
String from file, utf8 flag is off. Can't match chinese character.
String from file(utf8 layer), utf8 flag is on. Can match chinese character.
Turn on utf flag:
String from code, utf8 flag is on. Can match chinese character.
String from file, utf8 flag is on. Can match chinese character.

所以如果要想使用汉字作为一个字符的特性,就要在 open 里指明 io layer 为
utf8,同样,输出指明 utf8,然后在脚本里写 use utf8。
这样你就不用担心 utf8 下的乱码,又能享受汉字做为一个字符来写正则表达式的
的畅快。

如果不知道我上面说的意思,我就给一个例子:

use strict;
use warnings;
use Encode;
use utf8;

my $file = "test.txt";
my $outfile = "test-out.txt";

# open(my $fh, $file) || die "Can't open file $file: $!";
# open(my $out, '>:utf8', $outfile) || die "Can't open file $outfile: $!";

# select $out;

open(my $fh, '<:utf8', $file) || die "Can't open file $file: $!";

binmode STDOUT, ":utf8";

while (<$fh>) {

if (/[汉]/) {
unless (Encode::is_utf8($_)) {

Encode::_utf8_on($_);
}
print $_;

}

}

test.txt 中的内容是:"求\n汉\n汁\n汗\n汕\n江\n"(每个汉字一行的意思)。
这个例子中,有好几个组合:
1. 不使用 utf8 指令,open 中也不使用 utf8。
这时所有行都匹配。因为正则表达式中是三个字节(我假定你也用 utf8 来编码
代码,如果不是,可以不匹配任何行),而所有输入的行中都含有这三个字节中
的一个,事实上 test.txt 是我精心挑选的,前两个字节都相同的汉字。
2. 不使用 utf8 指令, open 中使用 utf8.
不能匹配任何行。因为输入文件中是汉字,而正则表达式是三个字节。
3. 使用 utf8 指令,open 中不使用 utf8
也不能匹配任何行。因为正则表达式中是一个汉字,而输入文件是一个个的字节。
4. 使用 utf8 指令,open 中也使用 utf8
正确匹配一行。

至于输出的 warning,这确实这是一个小小的 warning,不会影响结果。如果不
想有这个 warning,最好的办法是用 binmode 或者在 open 中指定使用 utf8
layer。或者对于 warning 的字符串用 Encode 模块中 _utf8_on 函数,强制
加上 utf8 flag.

perl 中文处理技巧

perl 中文处理技巧

Perl从5.6开始已经开始在内部使用utf8编码来表示字符,也就是说对中文以及其他语言字符的处理应该是完全没有问题的。我们只需要利用好Encode这个模块便能充分发挥Perl的utf8字符的优势了。

下面就以中文文本的处理为例进行说明,比如有一个字符串"测试文本",我们想要把这个中文字符串拆成单个字符,可以这样写:

use Encode;
$dat="测试文本";
$str=decode("gb2312",$dat);
@chars=split //,$str;
foreach $char (@chars) {
print encode("gb2312",$char),"\n";
}

结果大家试一试就知道了,应该是令人满意的。

这里主要用到了Encode模块的decode、encode函数。要了解这两个函数的作用我们需要清楚几个概念:
  1. Perl字符串是使用utf8编码的,它由Unicode字符组成而不是单个字节,每个utf8编码的Unicode字符占1~4个字节(变长)。
  2. 进入或离开Perl处理环境(比如输出到屏幕、读入和保存文件等等)时不是直接使用Perl字符串,而需要把Perl字符串转换成字节流,转换过程中使用何种编码方式完全取决于你(或者由Perl代劳)。一旦Perl字符串向字节流的编码完成,字符的概念就不存在了,变成了纯粹的字节组合,如何解释这些组合则是你自己的工作。

我们可以看出如果想要Perl按照我们的字符概念来对待文本,文本数据就需要一直用Perl字符串的形式存放。但是我们平时写出的每个字符一般都被作为纯ASCII字符保存(包括在程序中明文写出的字符串),也就是字节流的形式,这里就需要encode和decode函数的帮助了。

encode函数顾名思义是用来编码Perl字符串的。它将Perl字符串中的字符用指定的编码格式编码,最终转化为字节流的形式,因此和Perl处理环境之外的事物打交道经常需要它。其格式很简单:
$octets = encode(ENCODING, $string [, CHECK])
$string:  Perl字符串
encoding: 是给定的编码方式
$octets:  是编码之后的字节流
check:   表示转换时如何处理畸变字符(也就是Perl认不出来的字符)。一般不需使用
编码方式视语言环境的不同有很大变化,默认可以识别utf8、ascii、ascii-ctrl、 iso-8859-1等。

decode函数则是用来解码字节流的。它按照你给出的编码格式解释给定的字节流,将其转化为使用utf8编码的Perl字符串,一般来说从终端或者文件取得的文本数据都应该用decode转换为Perl字符串的形式。它的格式为:
$string = decode(ENCODING, $octets [, CHECK])
$string、ENCODING、$octets和CHECK的含义同上。

现在就很容易理解上面写的那段程序了。因为字符串是用明文写出的,存放的时候已经是字节流形式,丧失了本来的意义,所以首先就要用 decode函数将其转换为Perl字符串,由于汉字一般都用gb2312格式编码,这里decode也要使用gb2312编码格式。转换完成后Perl 对待字符的行为就和我们一样了,平时对字符串进行操作的函数基本上都能正确对字符进行处理,除了那些本来就把字符串当成一堆字节的函数(如vec、 pack、unpack等)。于是split就能把字符串切成单个字符了。最后由于在输出的时候不能直接使用utf8编码的字符串,还需要将切割后的字符用encode函数编码为gb2312格式的字节流,再用print输出。

Featured Post

Windows和Ubuntu双系统完全独立的安装方法

http://www.ubuntuhome.com/windows-and-ubuntu-install.html  | Ubuntu Home Posted by Snow on 2012/06/25 安装Windows和Ubuntu双系统时,很多人喜欢先安装windows,然...