执行失败:字符串值不正确:“\xE4rvine...”使用 mariadb 和 perl DBD

execute failed: Incorrect string value: '\xE4rvine...' with mariadb and perl DBD

提问人:Alex Regan 提问时间:10/22/2022 更新时间:10/23/2022 访问量:199

问:

三年多前,我发布了一个类似的问题,与我认为字符集不正确而无法将数据插入表有关,而今天我使用不同的数据使用相同的脚本也遇到了同样的问题。

问题出在 Ilpo J rvinen 中的德语字符(带有变音符号的“a”?

执行失败:字符串值不正确:“\xD6sterl...”使用 mariadb 和 perl DBD

DBD::mysql::st execute failed: Incorrect string value: '\xE4rvine...' for column `lsv6`.`xyz_content`.`fulltext` at row 1 at myscript.pl line 467.

我正在尝试处理具有不同字符集的电子邮件,但显然我的脚本没有正确编码。从电子邮件:

Content-type: text/plain; charset="iso-8859-1"
Content-transfer-encoding: quoted-printable

完整的脚本太长,无法在此处发布,但我相信这些是涉及数据库的相关部分。

sub db_connect($) {
    ...
    return DBI->connect("DBI:mysql:database=$DB{'db'};host=$DB{'host'}", $DB{'user'}, $DB{'pass'}, { PrintError => 1, RaiseError => 1, mysql_enable_utf8mb4 => 1 } )
    ...
}

$fullText               = $dbh->quote($fullText);
my $sql                 = <<EOF;
INSERT INTO xuxgc_content (title, alias, introtext, `fulltext`, state, catid, created, created_by, created_by_alias, modified, modified_by, checked_out, checked_out_time, publish_up, publish_down, images, urls, attribs, version, ordering, metakey, metadesc, metadata, access, hits, language)
VALUES ($title, "$title_alias", $introText, $fullText, $state, $catid, $created, $created_by, $created_by_alias, $modified, $modified_by, $checked_out, $checked_out_time, $publish_up, $publish_down, $images, $urls, $attribs, $version, $ordering, $metakey, $metadesc, $metadata, $access, $hits, $language);
EOF

my $sth = $dbh->prepare($sql);
$sth->execute();
db_disconnect($dbh);

如果我去掉非 ASCII 字符,它可以正常工作:

$fullText               =~ s/\xE4//g;                                                                                                                                                                              
$fullText               =~ s/\xFC//g;                                                                                                                                                                              
$fullText               =~ s/\xE5//g;                    

我应该使用 iso-8859-1 编码而不是 utf8mb4 吗?

PERL UTF-8 DBI ISO-8859-1 UTF8MB4

评论

0赞 ikegami 10/22/2022
如果你添加,它是否有效$fullText = decode( "iso-8859-1", $fullText );
0赞 Alex Regan 10/23/2022
是的,这奏效了,谢谢。我需要做多少其他解码/引用?我在 DBI->connect 中将编码类型更改为 utf8mb4 - 我是否需要知道正在使用的电子邮件的字符集是什么?

答:

2赞 ikegami 10/23/2022 #1

$fullText是使用 ISO-8859-1 编码的文本字符串。在使用它之前,需要将其解码为文本字符串。

DBD::mysql 期望对字符串进行适当的编码以进行连接。在您的情况下,这是 UTF-8。[1] 因此,您需要将查询编码为字符串字节,然后再将其传递给 DBD::mysql。

# Convert bytes to text
my $fullText = decode( "iso-8859-1", $fullText_latin1 );

# Convert text to SQL string literal
my $fullText_lit = $dbh->quote( $fullText );

my $sql = "... $fullText_lit ...";

# Convert text to bytes
my $sql_utf8 = encode( "UTF-8", $sql );

my $sth = $dbh->prepare( $sql_utf8 );

请注意,DBD::mysql 存在 Unicode 错误

Perl 有两种字符串的内部存储格式:“降级”和“升级”。字符串使用哪种内部存储格式并不重要。但是 DBD::mysql(以及我所知道的所有其他 DBD)访问内部字符串缓冲区,而不检查使用了两种存储格式中的哪一种。据说执行此操作的代码会受到Unicode错误的影响。

所以我之前说的不太对。DBD::mysql 期望字符串的内部缓冲区为连接进行适当的编码。 始终以降级格式生成字符串,并且此类字符串的内部缓冲区始终完全包含该字符串。encode

这意味着只要您使用正确的编码对传递给 DBD::mysql 的字符串进行编码,就不会有问题。encode


  1. 作为连接选项传递的效果之一是将编码连接设置为 UTF-8,就像使用 一样。mysql_enable_utf8mb4 => 1SET NAMES utf8mb4

评论

0赞 Alex Regan 10/24/2022
我认为有些事情我仍然不明白。我能请你为我更清楚地说明这一点吗?此特定电子邮件使用 ISO-8859-1 编码以支持二进制字符,但其他电子邮件使用 UTF-8 编码。您似乎在说对文本进行编码,但您上面给出的示例是对其进行解码。我是否需要区分两者来决定是否进行编码/解码?Joomla 似乎也需要 UTF-8,这就是我使用 mysql_enable_utf8mb4 的原因,对吗?
1赞 ikegami 10/24/2022
回复“此特定电子邮件使用 ISO-8859-1 编码以支持二进制字符”,这毫无意义。ISO-8859-1 是一种对文本进行编码的方法。它说那是0x61,是0x62,等等ab
1赞 ikegami 10/24/2022
回复“Joomla 似乎也需要 UTF-8,这就是我使用 mysql_enable_utf8mb4 的原因”,这做了两件事:1) 将用于与数据库通信的文本编码设置为 UTF-8。这很好,因为这意味着您可以与 db 交换任何 Unicode 字符。2)它使来自数据库的字符串被自动解码。但。它不会导致传递给数据库的字符串被自动编码,因此需要在答案中显示。这是一个糟糕的 API,因为它可以追溯到 Perl 的 Unicode 支持之前。encode
1赞 ikegami 10/24/2022
回复“你似乎在说对文本进行编码,但你上面给出的例子是解码它”,你总是想使用解码的文本。因此,您需要对编码的输入进行解码。我说你需要解码电子邮件的正文,我做到了。(不过,解析消息的库应该已经为您完成了此操作。因此,您需要对不会为您编码的输出进行编码。我说你需要对传递给数据库的字符串进行编码,我做到了。(同样,DBD:;MySQL应该为你做这件事,但由于上面提到的历史原因,它没有这样做。