【问题标题】:Perl string substitution garbles Unicode stringPerl 字符串替换乱码 Unicode 字符串
【发布时间】:2014-12-03 19:52:39
【问题描述】:

当 regexp 仅包含 ascii 字符时,UTF-8 编码字符串的字符串替换工作正常,但当 regexp 包含非 ascii 时会产生乱码输出。

my $str = "¿más?";

$str =~ s/[?]//g; 
print "$str\n";

==> ¿más

$str =~ s/[¿]//g; 
print "$str\n";

==> 米

更新:上面的答案清楚地表明我最初的问题的框架很差。答案集中在 STDOUT 上,但在我的实际问题中,我没有打印到 STDOUT。 (我这样做只是为了简化问题陈述)。在实际问题中,我从 sqlite 存储中检索数据并使用数据作为文件名来搜索文件系统。当我对检索到的数据应用清理例程时,某些文件名会出现乱码。

查看这一点的一种方法可能是进一步简化示例:

my $str = "más";

$str =~ s/[?]//g; 
print "$str\n";

==> 更多

$str =~ s/[¿]//g; 
print "$str\n";

==> 米

现在您可以看到@ikegami 的解释不适用。关于第二个 s/// 的某些东西会造成问题。公平地说,这两个答案都解决了上述问题 - 但任何额外的见解将不胜感激!

更新 2:根据要求,添加了 sprintf 的矢量标志输出。注意:还将目标替换字符从 ¿ 更改为 ¡ - 我现在认为我上面的代码(如 @ikegami 建议的那样)一定是复制不正确。

my $str = "más";
printf "%v02X\n", $str;

==> 6D.C3.A1.73

$str =~ s/[!]//g; 
printf "%v02X\n", $str;

==> 6D.C3.A1.73

print "$str\n";

==> 更多

$str =~ s/[¡]//g; 
printf "%v02X\n", $str;

==> 6D.C3.73

print "$str\n";

==> 米

【问题讨论】:

  • 尽管您在更新中声明了什么,但我没有提到 STDOUT。
  • @ikegami - 添加了您要求的信息;另请参阅有关 ¿ 与 ¡ 的说明

标签: perl unicode


【解决方案1】:

您将源代码视为 UTF-8,但除非您告诉 Perl 它是 UTF-8,否则它会将其视为 US-ASCII。

你说你有以下几点:

my $str = "más";
printf "%v02X %s\n", $str, $str;
$str =~ s/[!]//g; 
printf "%v02X %s\n", $str, $str;
$str =~ s/[¡]//g; 
printf "%v02X %s\n", $str, $str;

但你确实给 Perl 提供了以下等价物:

my $str = "m\xC3\xA1s";
printf "%v02X %s\n", $str, $str;   # 6D.C3.A1.73 (the UTF-8 of más)
$str =~ s/[!]//g; 
printf "%v02X %s\n", $str, $str;   # 6D.C3.A1.73 (the UTF-8 of más)
$str =~ s/[\xC2\xA1]//g;           # Replaces either of these bytes
printf "%v02X %s\n", $str, $str;   # 6D.C3.73 (garbage)

您想要以下内容:

use utf8;                             # Source is encoded using UTF-8
use open ':std', ':encoding(UTF-8)';  # Terminal provides and expects UTF-8.

my $str = "más";
printf "U+%v04X %s\n", $str, $str;   # U+006D.00E1.0073 (the Unicode of más)
$str =~ s/[¡]//g;                    # Aka s/[\x{0041}]//g
printf "U+%v04X %s\n", $str, $str;   # U+006D.00E1.0073 (the Unicode of más)

您提到您没有从源代码中获取字符串并且您没有输出 STDOUT,但修复方法是相同的:解码输入和编码输出。

【讨论】:

  • perlopentut 模型':encoding(UTF-8)' 其中use open 模型':encoding(utf8)'。这两者之间有显着区别吗?如果有的话,你推荐哪一个?
  • @Miller,UTF-8 是一种标准编码。 UTF8 是 Perl 特有的宽松变体。
  • 根据问题的变化进行了更新。
  • 谢谢,@ikegami!很高兴了解 sprintf 的矢量标志。
【解决方案2】:

使用utf8 指定源代码的编码并使用binmode 指定输出:

#!/usr/bin/env perl
use strict;
use warnings;
use utf8;

binmode STDOUT, ':encoding(UTF-8)';

my $str = "¿más?";

$str =~ s/[?]//g; 
print "$str\n";

$str = "¿más?";
$str =~ s/[¿]//g; 
print "$str\n";

输出:

¿más
más?

【讨论】:

  • 它有效(谢谢!),但 STDOUT 与 s/// 有什么关系?
  • 您正在打印结果(默认为 STDOUT),因此您也必须指定该编码。
  • 但第一次替换后打印结果很好。
  • 观察@ikegami 的答案以获得潜在的解释。无论哪种方式,解决方案都是一样的。
猜你喜欢
  • 2011-11-26
  • 2011-04-22
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 2014-10-06
  • 2013-06-25
  • 1970-01-01
  • 2011-03-04
相关资源
最近更新 更多