当我写Perl的格式时,如何使用Unicode字符?

4
基本上,我有一个数据库,从中获取$lastname$firstname$rid$since$times$ip
使用Perl脚本,我格式化数据并通过电子邮件发送。由于$lastname$firstname可能包含特殊字符(例如ä、ü、ß、é等),因此我首先解码这些字符串。
my $fullname = decode("utf8", $lastname) . ', ' . decode("utf8", $firstname);
my $send = swrite(<<'END', $ip, $fullname, $rid, $since, $times);
@<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<<<<<< @>>END

没有使用decode时,特殊字符会变成垃圾字符(如ä变成À),其他部分正常。
使用decode后,除了包含特殊字符的名称行有几个多余的<之外,一切都很好。
为什么会这样?我该如何去掉它们?
编辑:swrite来自perldoc perlform
sub swrite {
  my $format = shift;
  $^A = '';
  formline($format, @_);
  return $^A;
}

编辑2: 问题不在于终端或标准输出。我使用:
use Mail::Sender;
use vars qw($sender);
#...
$sender->MailMsg({to => $mailto, 
  cc=> "", 
  bcc => "", 
  subject => "subject", 
  msg => $send});

当接收电子邮件时,字符显示得很糟糕。

编辑3:
我获取的数据已经乱码。我得到的是'À'而不是'ä',这就是为什么我的格式失败的原因,因为在使用解码时字符数减少了。


1
Perl没有提供swrite函数。你是使用来自perldoc perlform的函数还是其他函数?如果没有这些信息,任何帮助都只会是猜测。 - Chas. Owens
抱歉,我忘记添加swrite的代码了。 - undefined
你使用的终端/操作系统是什么?这可能会影响到一些事情。 - Paul Nathan
看起来你的问题是电子邮件客户端无法处理UTF-8纯文本。你可能需要编写电子邮件消息作为MIME多部分,并将文本类型指定为UTF-8。 - Chas. Owens
@Paul:我使用运行在Debian“Lenny”上的xen虚拟机(2.6.18-6-xen-amd64)。数据库是PostgresSQL 7.4版本。 - undefined
5个回答

4
那里的问题在于“format”引擎无法理解你的UTF-8编码,它认为每个字节都是一个字符。我不确定是否可以让“formline”(“swrite”的底层机制)支持Unicode,但可以尝试以下方法:
use open qw( :std :encoding(UTF-8) );

这旨在尽可能广泛地应用UTF-8编码。

使用此功能可能需要跳过您的decode用法。


我在./test.pl的第一行得到一个“太晚了,无法使用“-CSD”选项”的错误。我做错了什么? - undefined
稍微研究了一下,似乎在某个时候使用 shebang 行上的 -C 变得不再受支持。编辑以提供我希望是替代方案。 - chaos

4

我的最小测试案例似乎证明格式处理Unicode没有问题:

perl -MEncode -e 'formline("X@<<X", Encode::decode("utf-8","ほげぼげ")); print $^A'

输出结果是三个字符,正如预期。但无论如何,format已经被严重弃用了。现在是时候使用其他替代方案了。

3
如果您正在使用来自perldoc perlformswrite函数,那么您的问题可能是STDOUT未设置为UTF-8或终端无法处理UTF-8。对于第一种情况,您有几个选项。第一个是使用binmode告诉STDOUT期望UTF-8:
#!/usr/bin/perl

use strict;
use warnings;

use Carp;

sub swrite {
    croak "usage: swrite PICTURE ARGS" unless @_;
    my $format = shift;
    $^A = "";
    formline($format, @_);
    return $^A;
}

my $fmt = "@<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<<<<<< @>>";

binmode STDOUT, ":utf8";

my ($ip, $rid, $since, $times) = qw/1.1.1.1 5 2009-08-19 20/;
my $firstname = "Ch\x{e4}s";
my $lastname  = "\x{d6}wens";
my $fullname  = "$lastname, $firstname";
my $send      = swrite $fmt, $ip, $fullname, $rid, $since, $times;
print "$send\n";

另一个选项是将PERL_UNICODE环境变量设置为SDL(这类似于命令行上的chaos的-CSD):
PERL_UNICODE=SDL perl script.pl

或者

export PERL_UNICODE=SDL
perl script.pl

还有其他方法可以告诉STDOUT期望UTF-8,但我现在想不起来了(很久以前我在.profile中加入了export PERL_UNICODE=SDL)。

如果问题出在终端上,那么你需要正确配置它或使用另一个终端。上面的代码适用于正确配置的终端,因此你可以将其用作测试。


实际上我没有使用终端或标准输出。请查看我的更新问题。 - undefined

3
我从未有过学习格式的兴趣。这不是一个好答案,因为我无法提供任何关于您问题和/或潜在解决方案的见解,但其他人已经做过了。我将提供两个替代建议。
第一种是Perl6::Form,它应该是一个更好的format,虽然我直到今天才使用它。另一方面,我使用过Text::Table,它非常适用于创建纯文本表格(大多数时候我只生成HTML,但电子邮件仍然是纯文本更好的地方)。 Perl6::Form示例:
#!/usr/bin/perl

use strict;
use warnings;

use Perl6::Form;

my @data = (
    ['127.0.0.1', 'Johnny Smithey', 'JLNSJIV', 14, 5],
    ['127.0.0.2', 'Ömer Seyfettin Şınas', 'OSS3', 25, 5],
);

for my $data_ref ( @data ) {
    print format_data($data_ref);
}

sub format_data {
    my ($data) = @_;
    return form
        '{<<<<<<<<<<<<<<<} {<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} ' .
        '{<<<<<<<<<<} {<<<<<<<<<<<<<<} {>>}',
        @$data;
}

Text::Table示例:

#!/usr/bin/perl

use strict;
use warnings;

use Text::Table;

my %common_options = (
    align => 'left',
    title_align => 'center',
);

my $sep = \' ';

my $table = Text::Table->new(
    {
        title  => 'IP Address',
        sample => '<' x 15,
        %common_options,
    },
    $sep,
    {
        title => 'Full Name',
        sample => '<' x 34,
        %common_options,
    },
    $sep,
    {
        title => 'RID',
        sample => '<' x 10,
        %common_options,
    },
    $sep,
    {
        title => 'Since',
        sample => '<' x 14,
        %common_options,
    },
    $sep,
    {
        title => 'Times',
        sample => '>' x 2,
        align => 'right',
        title_align => 'center'
    },
);

$table->rule('');

$table->load(
['127.0.0.1', 'Johnny Smith-Jones', 'JLNSJIV', '20090814010203', 5],
['127.0.0.2', 'Ömer Seyfettin Şınas', 'OSS3', '20071211101112', 3],
['192.168.172.144', 'Jane Doe', 'JD156', '20080101010101', 1],
);

print $table->table;

1
对于有关 Perl6::Form 的更多信息,请参见 https://dev59.com/kkXRa4cB1Zd3GeqPtJud#237031。+1 - draegtun

1

我不知道格式或swrite,但我知道你的电子邮件问题。

您在收到的电子邮件中看到的字符是UTF-8。但是,您的邮件程序默认设置为显示其他内容(如Windows-1252或Latin-1)。

解决方案是在电子邮件中添加一个标题,通知邮件程序有关字符编码的信息,以便它可以正确地显示它。您需要添加到电子邮件中的标题是:

Mime-version: 1.0
Content-type: text/plain; charset="UTF-8"

(或者使用其他字符集,确保它与电子邮件正文相对应)

此外,您可能希望将电子邮件编码为类似于“引用可打印”的7位编码,并添加相应的标题:

Content-transfer-encoding: quoted-printable

使用MIME::QuotedPrint模块可以进行最后一次编码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接