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

时间:2009-08-19 18:25:14

标签: perl email unicode format

基本上我有一个数据库,我从中获取$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,则特殊字符为垃圾(ä变为Ã),其余为OK。 使用decode,一切都很好,除了名称中包含特殊字符的行有几个<太多。

为什么?我该如何删除它们?

修改:swrite来自perldoc perlform

sub swrite {
  my $format = shift;
  $^A = '';
  formline($format, @_);
  return $^A;
}

EDIT2: 问题不在于终端和STDOUT。我用:

use Mail::Sender;
use vars qw($sender);
#...
$sender->MailMsg({to => $mailto, 
  cc=> "", 
  bcc => "", 
  subject => "subject", 
  msg => $send});

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

编辑3:
我得到的数据已经被扰乱了。我得到'Ã'而不是'ä',这就是我的格式失败的原因,因为使用解码时字符的数量会减少。

5 个答案:

答案 0 :(得分:4)

问题是format引擎不理解您的UTF-8;它认为每个字节都是一个字符。我实际上并不知道你是否可以formlineswrite的基本机制)说Unicode,但试试这个:

use open qw( :std :encoding(UTF-8) );

尝试尽可能广泛地应用UTF-8编码。

您可能需要在此处跳过decode用法。

答案 1 :(得分:4)

我的最小测试用例似乎认为格式处理Unicode就好了:

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

输出为三个字符,如预期的那样。但无论如何,format被严重弃用。是时候改用其他东西了。

答案 2 :(得分:3)

如果您使用swrite中的perldoc perlform功能,则问题是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(这类似于命令行上混乱的-CSD):

PERL_UNICODE=SDL perl script.pl

export PERL_UNICODE=SDL
perl script.pl

还有其他方式可以告诉STDOUT期待UTF-8,但我无法记住它们(我将export PERL_UNICODE=SDL放在我的.profile中很久以前)。

如果问题是您的终端,那么您需要正确配置它或获得不同的终端。上面的代码适用于正确配置的终端,因此您可以将其用作测试。

答案 3 :(得分: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;

答案 4 :(得分:1)

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

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

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

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

(或另一个字符集,确保它与电子邮件正文相对应)

此外,您可能希望将电子邮件编码为7bit编码,例如“quoted-printable”,并添加相应的标题:

Content-transfer-encoding: quoted-printable

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