为什么 Perl DBI 转义从 MySQL 检索到的值?

Why is Perl DBI escaping values retrieved from MySQL?

我在 MySQL 中有一个包含撇号 () 和省略号 (...) 的值:

$ /bin/echo "select alias from url_alias where source = 'node/12024'" | \
  mysql --skip-column-names -D cat36ia_d7prod

输出:

forum/technical-discussion/nagging-questions-i’ve-been-too-embarrassed-ask…

当我使用 Perl DBIDBD::mysql 检索值时,值已更改:

$ perl -MDBI -MDBD::mysql -e
      '$dbh=DBI->connect( "DBI:mysql:database=my_db",nick );
       $v=$dbh->selectrow_array(qq|select alias from url_alias where source = "'node/12024'"|);
       print "$v\n";'

输出:

forum/technical-discussion/nagging-questions-i?ve-been-too-embarrassed-ask?

Perl 为什么要这样做?我可以覆盖它吗?

您可能需要告诉 DBI 在与数据库对话时使用 UTF8。

$dbh=DBI->connect(
   'DBI:mysql:database=my_db', $user, $pass,
   { mysql_enable_utf8 => 1 }
);
  1. 告诉 Perl 如何编码输出。

    use open ':std', ':encoding(UTF-8)';
    
  2. 使用

    从数据库中以文本形式获取数据
    DBI->connect("DBI:mysql:database=my_db", $user, $pass, {
       mysql_enable_utf8 => 1,
    })
    

问:为什么 Perl 会这样做?我可以覆盖它吗?

它没有被转义。这是字符集翻译问题的症状。问号字符是当代码点未映射到目标字符集中的任何其他字符时使用的默认字符。


关于 Perl 为什么这样做的简短回答可能是:默认情况下,Perl 使用 ascii 字符集输出到 STDOUT。由于 ASCII 仅支持 U+00EF 以内的代码点,因此所有其他代码点(例如,字符 128 到 255)都将转换为问号字符。

关于如何覆盖此行为的简短回答可能是:通过在您的 perl 程序中包含如下一行来指定 STDIN、STDOUT 和 STDERR 使用 utf8 编码而不是 ascii:

use open qw(:std :utf8);

另一个潜在的问题是 MySQL 会话 character_set_client 变量的设置;数据库连接可能正在使用 latin1 字符集,但 database/server/column 字符集可能是 utf8,因此也可能在那里进行字符集转换。

并且可以指定要在数据库连接中使用的字符集,以避免不需要的字符集转换。


作为理解字符集的起点,您应该掌握以下两个参考资料:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

What Every Programmer Absolutely, Positively Needs To Know About Encodings And Character Sets To Work With Text