波斯语中的 QString

8

我收到了一个需要支持波斯语的Qt项目。数据从服务器发送,使用第一行获取QByteArray并使用第二行将其转换为QString:

    QByteArray readData = socket->readAll();
    QString DataAsString = QTextCodec::codecForUtfText(readData)->toUnicode(readData);

当数据用英语发送时,一切正常,但当使用波斯语时,

被替换为
سلام

I get

سÙ\u0084اÙ\u0085

我提到了这个过程,是为了避免有人建议使用.tr制作多语言应用程序。这完全与文本和解码有关,而不是翻译方法。我的操作系统是Windows 8.1(如果需要知道的话)。

当服务器发送“سلام”时,我得到了这个十六进制值。

0008d8b3d984d8a7d985

顺便说一下,服务器在开头发送了两个额外的字节,原因我不清楚。所以我使用以下方法将其删除:

DataAsString.remove(0,2);

在将其转换为QString后,十六进制值的开头会多出一些额外的内容。


3
请尝试使用QString DataAsString = QString::fromUtf8(readData)代替。codecForUtfText会查找字节序标记,如果找不到它会假定为Latin-1编码。 - Igor Tandetnik
请问您能否以十六进制的形式发布readData的值? - scopchanov
我的意思是将__readData__实际内容转换为十六进制(即_socket->readAll()所得到的结果),并通过编辑问题将其放入其中。另外,请在回复我的时候使用_@_和我的用户名,否则我将看不到你的回复。 - scopchanov
1
@scopchanov 我将文本“سلام”放入Notepad++中,因为我的浏览器和Notepad++支持UTF-8(或其他兼容的Unicode编码)。十六进制转储结果为“d8 b3 d9 84 d8 a7 d9 85”。(我想复制相应的Latin-1输出,但在完成之前Notepad++崩溃了。) - Scheff's Cat
2
请使用QByteArray::toHex()将您的dataRead转换为十六进制转储,打印出来,并将结果添加到您的问题中(使用[编辑])。如果它与我在Notepad++中得到的相同,则您的编码是UTF-8。如果有什么不同,有人可能能够从数字中猜出它。 - Scheff's Cat
显示剩余2条评论
2个回答

10

我实在太好奇了,等不及回复就自己玩了一会儿:

我复制了文本سلام(英语中为“Hello”),并将其粘贴到Nodepad++中(在我的情况下使用UTF-8编码)。然后我切换到十六进制视图,得到了:

snapshot of Notepad++ - hex dump of "سلام"

右侧的ASCII转储看起来有些类似于OP意外得到的内容。这让我相信readData中的字节是用UTF-8编码的。因此,我取出了暴露的十六进制数字并编写了一个小样例代码:

testQPersian.cc:

#include <QtWidgets>

int main(int argc, char **argv)
{
  QByteArray readData = "\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85";
  QString textLatin1 = QString::fromLatin1(readData);
  QString textUtf8 = QString::fromUtf8(readData);
  QApplication app(argc, argv);
  QWidget qWin;
  QGridLayout qGrid;
  qGrid.addWidget(new QLabel("Latin-1:"), 0, 0);
  qGrid.addWidget(new QLabel(textLatin1), 0, 1);
  qGrid.addWidget(new QLabel("UTF-8:"), 1, 0);
  qGrid.addWidget(new QLabel(textUtf8), 1, 1);
  qWin.setLayout(&qGrid);
  qWin.show();
  return app.exec();
}

testQPersian.pro:

SOURCES = testQPersian.cc

QT += widgets

在Windows 10上使用cygwin编译并测试:

$ qmake-qt5 testQPersian.pro

$ make

$ ./testQPersian

snapshot of testQPersian

再次,使用Latin-1输出的结果与OP得到的结果以及Notepad++显示的结果有些相似。

使用UTF-8输出的结果提供了预期的文本(因为我提供了适当的UTF-8编码作为输入)。

也许,ASCII/Latin-1输出的差异有点令人困惑。存在多个字符字节编码,它们在下半部分(0 ... 127)共享ASCII,但在上半部分(128 ... 255)具有不同的字节含义。(查看ISO/IEC 8859以了解我的意思。这些已经作为本地化引入,然后Unicode成为本地化问题的最终解决方案。)

波斯字符肯定都具有超过127的Unicode代码点。(Unicode也共享前128个代码点的ASCII。)这些代码点在UTF-8中被编码为多个字节的序列,其中每个字节都具有MSB(最高有效位-位7)设置。因此,如果这些字节(意外地)使用任何ISO8859编码进行解释,则上半部分变得相关。因此,根据当前使用的ISO8859编码,这可能会产生不同的字形。


一些续篇:

OP发送了以下快照:

Snapshot (provided by OP)

因此,看起来他得到的是

00 08 d8 b3 d9 84 d8 a7 d9 85

而不是

d8 b3 d9 84 d8 a7 d9 85

可能的解释:

服务器首先发送一个16位长度00 08,解释为大端16位整数:8,然后是8个以UTF-8编码的字节(看起来与上面所玩的一样)。 (据我所知,对于二进制网络协议来说,使用大端是很常见的,因为这可以防止发送方和接收方具有本地不同的字节顺序。)更多阅读请参见:htons(3)- Linux man页面

在i386上,主机字节顺序是最低有效字节优先,而Internet上使用的网络字节顺序是最高有效字节优先。


OP声称该协议使用DataOutput-writeUTF

向输出流写入两个长度信息字节,后跟字符串s中每个字符的修改后的UTF-8表示形式。如果s为null,则抛出NullPointerException。字符串s中的每个字符都将转换为一个、两个或三个字节组,具体取决于字符的值。

因此,解码可能如下所示:

QByteArray readData("\x00\x08\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85", 10);
//QByteArray readData = socket->readAll();
unsigned length
  = ((uint8_t)readData[0] <<  8) + (uint8_t)readData[1];
QString text = QString::fromUtf8(dataRead.data() + 2, length);
  1. readData中提取前两个字节,并将它们组合成length(解码大端16位整数)。

  2. 余下的dataRead被转换为QString,提供之前提取的length。因此,跳过了readData的前两个长度字节。


我尝试使用您的代码,但在UI和控制台上都给了我空值""。 QString textLatin1 = QString::fromLatin1(readData); QString textUtf8 = QString::fromUtf8(readData); qDebug() << textUtf8 << "\n" << textLatin1; emit(MessageSent("Clint 说:" + textLatin1 + "\n" + textUtf8)); @符号和换行符不起作用。 - Steve Moretz
image img - Steve Moretz
@stevemoretz 我将其制作为GUI应用程序(使用QLabel),因为我确定我的Windows 10控制台无法打印UTF-8。(我曾经为此苦苦挣扎。即使明确设置为UTF-8,也只会打印出无意义的字符。)相反,GUI对Unicode有很好的支持。(MS在内部使用UTF-16。)如果您想在Windows控制台上打印,请设置包含所需字形的代码页,并相应地本地化QString。如果您在Linux上,则可能要好得多。即使在cygwin中,xterm也支持UTF-8,并且UTF-8的控制台输出效果良好。 - Scheff's Cat
@stevemoretz,您能否在您的端上使用我的代码重现结果吗? - Scheff's Cat
我已将其标记为答案,但仍不知道如何删除前两个字节。我应该下载这个库 #include <arpa/inet.h> 吗?难道没有更简单的方法吗?我应该使用哪些函数?为什么 readData.remove(0,2); 没有起作用! - Steve Moretz
显示剩余17条评论

0

你需要像这样使用utf8发送你的数据

    mysocket->write(message.toUtf8());

并接收并转换为 utf8,如下所示:

    QByteArray Data = mysocket->readAll();
    QString DataAsString = QString::fromUtf8(Data);

您的数据将被发送而不会变得无法识别


你好,Arash。问题在于服务器在Android上,所以你不能使用这个答案。感谢你的尝试。 - Steve Moretz

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