PHP/MySQL会截断包含非ASCII字符的字符串

3
我有一个带有翻译功能的页面(这里)。我的问题是,当我将语言翻译成法语时,单词被剪切了,因为页面没有正确解释这些单词。我查看了与我的问题相关的帖子,但它们都没有起作用。
在我的页面中,我添加了以下内容:
  • header ('Content-Type:text/html; charset=WINDOWS-1252'); -> 这只是为了在启动时坚持编码。我认为这个是可选的,但我仍然使用它。
  • <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
  • 等价翻译从数据库表名获取:标签标签的表类型是InnoDB,默认字符集为utf8 -- UTF-8 Unicode
é之后的字符被剪切。是否需要做些什么才能正确显示这些字符?谢谢!

4
也许您可以在应用程序中始终使用UTF-8,而不是不断地在传统编码和Unicode之间进行转换? - Joey
什么意思是“剪切é之后的字符”? - Raffaele
在从数据库读取数据并输出到HTML之间,您是否调用任何字符串操作函数?也许您使用了PHP的一些字符串函数,它们不支持Unicode(例如strlen而不是mb_strlen)。 - martinstoeckli
例如,我将把"Reference"翻译成法语,对应的应该是"Référence",但页面上只显示为"R"。 - kimbebot
@Raffaele - 你说的链接页面是什么意思?我已经在我的问题中提供了示例页面的链接。 - kimbebot
显示剩余3条评论
3个回答

2
我认为在多语言应用程序的后端使用Unicode编码,前端使用代码页没有任何意义。你要么在整个项目中使用相同的编码方式,要么手动在UTF-8windows-1252之间进行转换。
我认为你并没有阅读问题。标签被截断来自数据库,否则您的浏览器将显示垃圾字符。因此,这不是PHP/HTML的问题,而是MySQL的问题。对于èéàòì等情况,MySQL当然可以从UTF-8转换为CP1252(latin1)。但是,如果不是这种情况(例如我们尝试将相同的字符串从UTF-8转换为CP1251),MySQL将显示问号?
在您的情况下,我认为这是一个输入问题,即标签在数据库中被截断。这怎么可能?您的PHP和MySQL可能是UTF8,但是当您的浏览器以这样的字符集从加载了该字符集的页面提交表单时,它会发送windows-1252字符串。在您的PHP脚本中,您应该在将其插入数据库之前将此字符串转换为UTF-8,或者使用SET NAMES 'CP1252'连接到MySQL。由于您没有这样做,因此您最终尝试插入一堆无效的UTF-8字节,因此MySQL截断字符串,您的标签为空。这里附上一个测试用例。以下是test表格。
CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

这里是PHP部分。请注意,此脚本采用UTF-8编码,因此其中出现的每个文本字符串都具有相同的编码。

// This is a UTF-8 file, so my editor uses UTF-8 and thus each literal
// string is a UTF-8 string, since PHP only has binary strings.
$label = "Référence";

// Now let's translate this string as if it came from a browser submitting
// a form loaded from a cp1252 encoded page
$src = mb_convert_encoding($label, "CP1252", "UTF-8");

// But connect as if I were UTF-8
$db = new PDO('mysql:host=localhost;dbname=test;charset=utf8',
    'test', 'test');

// Insert the string
$stmt = $db->prepare('INSERT INTO test (name) VALUES ( ? )');
$stmt->bindValue(1, $src);
$stmt->execute();

// Read it
header("content-type: text/plain; charset=windows-1252");
foreach($db->query('SELECT * FROM test') as $row)
    echo $row['name'] . "\n";

你如何进行恢复?你可以使用cp1252字符集连接MySQL并让MySQL为你翻译,或者在你的脚本中转换字符串。在正确获取数据后,你需要提取它们并将其放在HTML页面上。这时你会遇到同样的问题,但是相反:在CP1252文档中显示UTF-8字符串。数据库中的字节不适用,因为UTF-8是一种可变长度编码,而在CP1252中,一个字符恰好为1个字节长。如果你直接将这些字节放入页面中,浏览器将会显示一些随机的乱码。因此,你要么连接到指定CP1252字符集的数据库,使MySQL负责转换并给你正确的字节,要么在PHP端自己转换字节。或者你最好自己做一个决定:在所有地方使用相同的编码方式。我建议使用UTF-8,因为今天这是正确的做法,但你也可以选择CP1252,因为它可以表示英语和法语字符(并且节省一些存储空间,但我认为这不是一个问题)。

1

我的建议是在整个过程中使用相同的编码方式。在头部和元标记中都使用UTF-8作为字符集。


它仍然没有真正解释为什么部分内容丢失。这不可能仅因为页面编码的原因发生。 - Joey
好的,所以我已经更改了标题和元标记值为header('Content-Type: text/html; charset=utf-8');<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 但仍然无法正常工作。 - kimbebot

0

在我看来,你的数据似乎没有正确存储在数据库中。如果你正在使用mysqli,你可以尝试在读写数据库之前设置连接对象的字符集。

// tells the mysqli connection to deliver UTF-8 encoded strings.
$db = new mysqli($dbHost, $dbUser, $dbPassword, $dbName);
$db->set_charset('utf8');

如需其他数据库,请参见{{link1:PHP和MySQL的UTF-8}}。也许需要重新插入法语文本(使用此设置),因为现有文本现在可能无效。

您链接的示例页面已正确使用UTF-8(文件格式)进行编码,尽管您的元标记有点不正确:

<!meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

<! 不是被注释掉的,你需要写成 <!--。最好只声明一次为 UTF-8,并移除其他的元标签。


这对我来说似乎是一个“抄袭答案”。此外,问题明确说明他想要在前端使用cp1252,因此页面被正确地UTF-8编码只是一些随机性。你可以在整个页面上看到<meta>,所以代码肯定非常混乱。 - Raffaele
@Raffaele - 我从未想过要成为一个小偷,我需要一些时间来输入答案,所以我没有看到你的答案。与你的答案相反,我想指出的是,必须将字符集设置为连接对象,而不是在数据库配置中进行设置。因为我看到了示例中不同的元标记,我认为kimbebot并不反对使用UTF-8,并且这些不同的标记会干扰。如果您查看链接的文章,您会发现这不是一个即兴回答,我自己也遇到了这个问题,并花了相当长的时间来撰写这篇文章。 - martinstoeckli
@Raffaele - 顺便说一下,阅读你的答案也需要一些时间。但我喜欢它,所以你有我的投票... - martinstoeckli
抱歉,我不是故意要听起来那么严厉的 :) 问题在于我花了相当长的时间进行实验并得出了答案。我不仅仅陈述了标签在数据库中被截断的事实,还给出了原因。如果你仔细阅读我的回答,你会注意到我提到的问题是连接声明字符集和PHP字符串编码之间的差异,所以你需要更改其中一个。我生气了,因为我的回答发表后仅仅10分钟,你就发表了你的回答,而你的回答真的没有对我的回答做出任何贡献。无论如何,如果我伤害了你,我很抱歉 :) 和平 - Raffaele
@Raffaele - 没有造成任何损失,我想帮忙,你也想帮忙,问题清晰地指向这个方向。继续撰写这样有用的答案,再见! - martinstoeckli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接