【问题标题】:Mixed UTF-8 and latin1 tables with PDO带有 PDO 的混合 UTF-8 和 latin1 表
【发布时间】:2012-11-17 08:44:51
【问题描述】:

有一个现有的数据库/表,我无法更改字符集。这些表使用排序规则“latin1_swedish_ci”,但其中存储了 UTF-8 数据。例如字符串“fußball”(德国足球)被保存为“fußball”。这是我无法改变的部分。

我的整个脚本在 UTF-8 上工作得很好,它有自己的 UTF-8 表,我使用带有 UTF-8 连接的 PDO(mySQL) 进行查询。但有时我必须查询一些“旧”的 latin1 表。是否有任何“酷”的方法来解决这个问题而不是发送 SET NAMES。

这是我在 stackoverflow 上的第一个问题! :-)

【问题讨论】:

  • 为什么不能更改字符集?
  • 因为如果我要更改它,我将不得不更改数百个其他已经在工作的脚本。 :s

标签: php mysql character-encoding pdo


【解决方案1】:
  1. 实际上很容易认为数据是以一种方式编码的,而实际上它是以其他方式编码的:这是因为任何直接检索数据的尝试都会导致首先转换为数据库连接的字符集,然后转换为输出介质的字符集——因此您应该首先通过SELECT BINARY myColumn FROM myTable WHERE ...SELECT HEX(myColumn) FROM myTable WHERE ... 验证存储数据的实际编码。

  2. 一旦您确定您在 Windows-1252 编码列中存储了 UTF-8 编码数据(即您看到 0xc39f 应该是字符 ß),那么您真正想要的是从列中删除编码信息,然后告诉 MySQL 数据实际上被编码为 UTF-8。如ALTER TABLE Syntax 中所述:

    警告

    CONVERT TO 操作在字符集之间转换列值。如果您有一个字符集中的列(如latin1),但存储的值实际上使用了其他一些不兼容的字符集(如utf8),这不是你想要的。在这种情况下,您必须对每个此类列执行以下操作:

    更改表 t1 更改 c1 c1 BLOB; 更改表 t1 更改 c1 c1 文本字符集 utf8;

    这样做的原因是当您与BLOB 列进行转换时没有转换。

  3. 今后 MySQL 将根据需要正确地将选定数据转换为连接字符集的数据。也就是说,如果连接使用 UTF-8,则无需转换;而使用 Windows-1252 的连接将接收转换为该字符集的字符串。

  4. 不仅如此,MySQL 中的字符串比较也会正确执行。例如,如果您当前使用 UTF-8 字符集连接并搜索'fußball',您将不会得到任何结果;而你会在上面的修改之后。

  5. 您提到的必须更改大量遗留脚本的陷阱仅适用于那些遗留脚本使用不正确的连接字符集(例如,告诉 MySQL 他们使用 Windows-1252 而他们实际上是发送并期望接收 UTF-8 格式的数据)。您真的无论如何都应该解决这个问题,因为它可能会导致各种恐怖事件。

【讨论】:

  • 首先感谢您的详细回复。我做了第 1-2 步,但正如预期的那样,旧脚本不再工作了,因为它们做了一些 utf8_encode/decode/stripslashes/htmlentities..
【解决方案2】:

我通过在我的 DB 类中创建另一个数据库句柄来解决它,它使用 latin1,所以每当我需要查询“旧表”时,我都可以使用

$pdo    = Db::getInstance();
$pdo->legacyDbh->query("MY QUERY");
# instead of
$pdo->dbh->query("MY QUERY");

如果有人有更好的解决方案,也不要碰桌子.. :-)

【讨论】:

  • 如果正确执行是不可能的,这几乎是解决方案。数据已使用错误的字符集存储,使用相同的错误字符集检索它恰好返回您想要的数据。请参阅kunststube.net/frontback 了解原因。在继续之前,请用旧的 IBM 键盘拍打原始开发人员。
  • 好吧,只要您的数据的错误编码在连接字符集中有效,并且此后的任何转换在存储字符集中有效,它就可以工作。这非常容易破坏,因为对数据的查询会产生意想不到的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-15
  • 2014-05-17
  • 1970-01-01
  • 2013-09-18
  • 2018-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多