从MySql

时间:2015-05-17 18:15:10

标签: mysql string perl utf8mb4

我写了一个小的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的方法。

1 个答案:

答案 0 :(得分:1)

MySQL调用utf8是UTF-8的有限子集,每个字符只允许三个字节,并覆盖高达0xFFFF的代码点。甚至utf8mb4也不包括完整的UTF-8范围,它支持长达6个字节的编码字符

结果是来自utf8utf8mb4列的任何数据都只是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