我写了一个小的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个可见字符)。在这种情况下,Dumper(Useqq = 1)是:
$ VAR1 =“\ x {5e9} \ x {5dc} \ x {5d5} \ x {5dd} \ x {5dd} \ x {5dc} \ x {5dd}”; < / p>
当没有声明`use utf8;'时,结果为16,类似于从DB发送值的情况:
$ VAR1 =“\ 327 \ 251 \ 327 \ 234 \ 327 \ 225 \ 327 \ 235 \ 327 \ 242 \ 327 \ 225 \ 327 \ 234 \ 327 \ 235”;
在开始使用它之前,我需要找到一种将接收到的值转换为UTF8的方法。
答案 0 :(得分:1)
MySQL调用utf8
是UTF-8的有限子集,每个字符只允许三个字节,并覆盖高达0xFFFF的代码点。甚至utf8mb4
也不包括完整的UTF-8范围,它支持长达6个字节的编码字符
结果是来自utf8
或utf8mb4
列的任何数据都只是Perl中的UTF-8字符串,两个数据库编码之间应该没有区别
我猜你没有为你的DBI
句柄启用UTF-8,因此所有内容都被视为一个字节序列。进行mysql_enable_utf8
通话时应启用connect
,这应该看起来像
my $dbh = DBI->connect($dsn, $user, $password, { mysql_enable_utf8 => 1 });
通过附加数据,我可以看到您从数据库中检索的字符串确实是UTלוםםUT UT <
但是,如果我对它进行解码,那么首先我从你的foo
子程序和我自己的子程序中获得非空格字符数,而不是9;你也应该从数据库中获取 characters ,而不是字节
我怀疑您可能首先将编码的字符串写入数据库。这是一个简短的程序,它创建一个MySQL表,向它写入两条记录(一个字符串和一个编码的字符串)并检索它所写的内容。你会看到唯一有所作为的是mysql_enable_utf8
的设置。无论原始字符串是否已编码,以及是否有SET NAMES utf8mb4
进一步的实验表明, mysql_enable_utf8
或 SET NAMES utf8mb4
会让DBI正确地写数据,但是后者对阅读
我建议您的解决方案是在阅读或撰写时仅使用mysql_enable_utf8
您还应use 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