【问题标题】:Properly displaying UTF-8 chars in Perl在 Perl 中正确显示 UTF-8 字符
【发布时间】:2020-04-22 08:55:10
【问题描述】:

我正在运行为 MSWin32-x64-multi-thread 构建的 perl 5,版本 24,subversion 3 (v5.24.3) (带有 1 个已注册补丁,详情请参阅 perl -V)(活动状态)。

尝试解析以 UTF-8 编码的 HTML 页面:

$request = new HTTP::Request('GET', $url);
$response = $ua->request($request);
$content = $response->content();

我使用 INDEX 和 SUBSTR 函数将 $content 解析为一个巨大的字符串,效果很好。 HTML 页面包含值为 ÖBB 的字符串,我需要将其插入数据库中,与 ÖBB 完全相同 当我打印它并插入数据库时​​,我得到了一些 ascii 字符,而不是 Ö。

注意:这个问题与数据库无关; MySQL 可以很好地处理 utf-8,所以如果我插入值“ÖBB”,它不会有问题。

我在这里和其他论坛上查看了大量类似的问题/答案,但我并不明智。

使用 utf-8 和 binmode(STDOUT, ":utf8") 对我不起作用... 非常感谢可以解决问题的代码 sn-p,谢谢。

【问题讨论】:

  • 如何将 HTML 页面(字符串)放入程序中?
  • 你使用什么数据库?什么司机?你如何连接到数据库?
  • 考虑使用utf8::all,它试图使 Perl 尽可能地对 UTF8 友好。
  • 当您说“打印”时,您的意思是打印到控制台吗?在这种情况下,请确保您的 Unicode 代码页处于活动状态。在命令行中键入chcp。如果它说“65001”,那么你在这方面很好。如果没有,请输入 chcp 65001 以激活正确的代码页。
  • 您的数据库是否配置为支持 UTF8?您没有说明您使用的数据库。如果您使用 MYSQL DB,请参阅以下instruction。如果您在 MS Windows 中不要忘记在终端窗口 chcp 65001 中更改代码页。

标签: perl utf-8


【解决方案1】:

当您捕获网页并将其输出到屏幕时,涉及到几个组件。

暂时让我们假设您使用 Windows 并在 终端 窗口中运行以下脚本。

首先你需要确认你的终端支持UTF8编码。输入命令chcp,看看它是否会输出65001

如果有则设置,如果没有则发出以下命令chcp 65001

使用命令perl script_name.pl 运行脚本,您应该会在终端窗口中获​​得带有ÖBB 的输出

use strict;
use warnings;

use utf8;
use feature 'say';

use HTTP::Tiny;

my $url = shift || 'https://www.thetrainline.com/en/train-companies/obb';

my $response = HTTP::Tiny->new->get($url);

if ($response->{success}) {
   my $html = $response->{content};

   $html =~ m/(<p>Planning.+pets.<\/p>)/;

   say $1;
}

要将数据以 UTF8 编码存储在 database 中,database 应配置为支持 UTF8 编码。

如果是 MYSQL 数据库,命令应如下所示

CREATE DATABASE mydb
  CHARACTER SET utf8
  COLLATE utf8_general_ci;

请参阅以下 MYSQL 文档webpage

【讨论】:

  • 使用chcp 65001 是不够的。您还需要使用use open ':std', ':encoding(UTF-8)';
  • 可能是,但就我而言,它与我提到的设置配合得很好。
  • 不,它没有。它要么打印垃圾 (say chr(0xE9);) 要么警告 (say chr(0x2660);),它只适用于仅包含 ASCII 字符的字符串,这绝对不是 OP 想要的。
  • 那么您已经证明了您有两个错误。 1)你没有解码你的输入,2)你没有编码你的输出。它恰好“起作用”,因为网页使用与终端相同的编码进行编码,但情况并非总是如此。
  • 问题是关于 UTF-8 输出,不管输入的编码如何。您认为所有网页都使用 UTF-8 的假设是不正确的。
【解决方案2】:

解码输入;编码输出。


首先,您不会对输入进行解码。

$response-&gt;content 返回可以采用任何编码的原始内容。如果是 HTML,则使用 $response-&gt;decoded_content(); 获取解码后的响应。


其次,您可能没有对输出进行编码。

您没有指定使用的数据库驱动程序。大多数 DBI 驱动程序都有一个您需要指定的选项。例如,对于 MySQL,您希望

my $dbh = DBI->connect(
   'dbi:mysql:...',
   $user, $password,
   {
      mysql_enable_utf8mb4 => 1,
      ...
   },
);

你提到了use utf8;。这告诉 Perl 您的源代码是使用 UTF-8 而不是 ASCII 编码的。如果您的源代码使用 UTF-8 编码,请使用它。

这与您的问题没有直接关系。


你提到了binmode(STDOUT, ":utf8")。这是一种非常糟糕的写作方式

use open ':std', ':encoding(UTF-8)';

上面处理了 STDIN、STDOUT 和 STDERR,并在编译时处理。它还为在 pragma 范围内打开的文件设置默认值。

但这是假设终端需要 UTF-8。如果您使用chcp 65001,就会出现这种情况。对于处理终端期望的任何编码的版本,您可以使用以下内容:

BEGIN {
   require Win32;
   my $cie = "cp" . Win32::GetConsoleCP();
   my $coe = "cp" . Win32::GetConsoleOutputCP();
   my $ae  = "cp" . Win32::GetACP();

   binmode(STDIN,  ":encoding($cie)");
   binmode(STDOUT, ":encoding($coe)");
   binmode(STDERR, ":encoding($coe)");

   require open;
   "open"->import(":encoding($ae)");
}

This 有更多细节。

这与您的问题没有直接关系。

【讨论】:

    【解决方案3】:

    这是有效的:

    use Win32::API;
    binmode(STDOUT, ":unix:utf8");
    $SetConsoleOutputCP= new Win32::API( 'kernel32.dll', 
    'SetConsoleOutputCP', 'N','N' );
    $SetConsoleOutputCP->Call(65001);
    

    这一切都在表面上,我只是忽略了它;-)

    为了让 MySQL db 正常工作并接受 utf-8 编码的字符串,必须启用此连接参数: mysql_enable_utf8 => 1,

    【讨论】:

    • binmode(STDOUT, ":unix:utf8");use open ':std', ':encoding(UTF-8)'; 的错误版本。 // mysql_enable_utf8 应该是 mysql_enable_utf8mb4 // SetConsoleOutputCP 由 Win32 提供;无需使用 Win32::API。 (相当于使用上述chcp
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多