gpt4 book ai didi

mysql - 使用perl从MySql获取utf8mb4字符串的长度

转载 作者:行者123 更新时间:2023-11-29 12:13:20 24 4
gpt4 key购买 nike

我编写了一个小型 Perl 函数,它接受一个字符串并检查其长度(不含空格)。基本代码如下:

sub foo
{
use utf8;
my @wordsArray = split(/ /, $_[0]));
my $result = length(join('', @wordsArray));
return $result;
}

当我向此函数提供包含特殊字符(例如希伯来字母)的字符串时,它似乎工作得很好。当我使用来自 MySql 列的值(字符集为 utf8mb4)时,问题就开始了:在这种情况下,计算的值高于上一个示例中的值。

我可以猜测为什么会出现这种行为:特殊字符在表中以 4 字节的方式写入,因此每个字母在 utf8 编码中计算为两个字符。

有谁知道如何解决上述问题,以便我从定义为 utf8mb4 的数据库表中获得正确数量的字符?

编辑:

有关上述代码的更多信息:

用作函数参数的 DB 列的类型为 VARCHAR(1000),排序规则为 utf8mb4_unicode_ci。我通过配置如下的 MySql 连接获取行:

$mySql = DBI->connect(
"DBI:mysql:$db_info{'database'}:$db_info{'hostname'};mysql_multi_statements=1;",
"$db_info{'user'}",
"$db_info{'password'}",
{'RaiseError' => 1,'AutoCommit' => 0});
...
$mySql->do("set names utf8mb4");

示例数据值是“שלום עולם”(希伯来语意思是“Hello World”)。

1) 当调用 foo($request->{VALUE}); (其中 VALUE 是来自 DB 的列数据)时,结果为 16(其中每个希伯来字符被计为两个字符) ,并且忽略它们之间的一个空格)。本例中的转储器是:

$VAR1 = "\327\251\327\234\327\225\327\235 \327\242\327\225\327\234\327\235";

2) 当调用 foo("שלום עולם"); 时:

  • 当声明use utf8;时,结果是8(因为该字符串中有8个可见字符)。本例中的转储器(Useqq=1)是:

    $VAR1 = "\x{5e9}\x{5dc}\x{5d5}\x{5dd}\x{5e2}\x{5d5}\x{5dc}\x{5dd}";

  • 当不声明`use utf8;'时,结果是16,与从DB发送值的情况类似:

    $VAR1 = "\327\251\327\234\327\225\327\235\327\242\327\225\327\234\327\235";

看起来我需要找到一种方法,在开始使用之前将接收到的值转换为 UTF8。

最佳答案

MySQL 所称的 utf8 是 UTF-8 的有限子集,每个字符仅允许三个字节,并且覆盖最大 0xFFFF 的代码点。即使 utf8mb4 也没有涵盖完整的 UTF-8 范围,该范围支持最长 6 个字节的编码字符

结果是,来自 utf8utf8mb4 列的任何数据都只是 Perl 中的 UTF-8 字符串,两个数据库之间应该没有区别编码

我猜测您尚未为 DBI 句柄启用 UTF-8,因此所有内容都被视为字节序列。当您进行 connect 调用时,您应该启用 mysql_enable_utf8,这应该类似于

my $dbh = DBI->connect($dsn, $user, $password, { mysql_enable_utf8 => 1 });
<小时/>

通过附加数据,我可以看到您从数据库检索的字符串确实是 UTF-8 编码的

但是,如果我对其进行解码,那么首先我会从您的 foo 子例程和我自己的子例程中获得非空格字符数,而不是 9;而且您应该从数据库中获取字符,而不是字节

我怀疑您可能首先将编码字符串写入数据库。下面是一个简短的程序,它创建一个 MySQL 表,向其中写入两条记录(一条字符串和一条编码字符串)并检索所写入的内容。您将看到唯一有所不同的是 mysql_enable_utf8 的设置。无论原始字符串是否经过编码,以及是否使用 SET NAMES utf8mb4

,行为都是相同的

进一步的实验表明mysql_enable_utf8SET NAMES utf8mb4将使DBI写入数据正确,但后者对读取没有影响

我建议您的解决方案应该是在读取或写入时仅使用mysql_enable_utf8

您还应该仅在所有程序的顶部使用 utf8。错过这一点意味着您不能在代码中使用任何非 ASCII 字符

use utf8;
use strict;
use warnings;

use DBI;
use open qw/ :std :encoding(utf-8) /;

STDOUT->autoflush;

my $VAR1 = "\327\251\327\234\327\225\327\235 \327\242\327\225\327\234\327\235";

my $dbh = DBI->connect(
qw/ DBI:mysql:database=temp admin admin /, {
RaiseError => 1,
PrintError => 0,
mysql_enable_utf8 => 1,
}
) or die DBI::errstr;

$dbh->do('SET NAMES utf8mb4');

$dbh->do('DROP TABLE IF EXISTS temp');
$dbh->do('CREATE TABLE temp (value VARCHAR(64) CHARACTER SET utf8mb4)');

my $insert = $dbh->prepare('INSERT INTO temp (value) VALUES (?)');
$insert->execute('שלום עולם');
$insert->execute($VAR1);

my $values = $dbh->selectcol_arrayref('SELECT value FROM temp');
printf "string: %s foo: %d\n", $_, foo($_) for @$values;

sub foo2 {
$_[0] =~ tr/ //c;
}

sub foo {
length join '', split / /, $_[0];
}

使用 mysql_enable_utf8 => 1 输出

string: שלום עולם  foo: 8
string: שלום עולם foo: 8

输出为mysql_enable_utf8 => 0

string: ש××× ×¢×××  foo: 16
string: ש××× ×¢××× foo: 16

关于mysql - 使用perl从MySql获取utf8mb4字符串的长度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30290384/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com