我发现,char*
在QByteArray
有NULL字节。码:
QByteArray arr;
QDataStream stream(&arr, QIODevice::WriteOnly);
stream << "hello";
Run Code Online (Sandbox Code Playgroud)
查看调试器变量视图:
我不明白为什么我一开始会有三个空字节。我知道[3]
字节是字符串长度。我可以删除最后一个字节吗?我知道它是一个以Null结尾的字符串,但是对于我的应用程序,我必须具有原始字节(在存储长度的开头一个字节)。
当我使用QString时,对我来说更奇怪:
QString str = "hello";
[rest of code same as above]
stream << str;
Run Code Online (Sandbox Code Playgroud)
它的末尾没有null,因此我认为在每个char通知下一个字节为char之前,可能为null字节吗?
只是两个问题:
我不明白为什么一开始有三个空字节。
它是一个固定大小的 uint32_t(4 字节)标头。它有四个字节,因此可以指定长达 (2^32-1) 个字节的数据长度。如果它只是一个字节,那么它只能描述最长 255 个字节的字符串,因为这是可以容纳在单个字节中的最大整数值。
我可以删除最后一个字节吗?我知道它是空终止字符串,但对于我的应用程序,我必须有原始字节(以一个字节开始存储长度)。
当然,只要稍后解析数据数组的代码不依赖于尾随 NUL 字节的存在即可正常工作。
对我来说更奇怪的是,当我使用 QString [...] 时,它末尾没有 null ,所以我认为每个 char 通知下一个字节是 char 之前可能有 null 字节?
根据Qt 序列化文档页面,QString 被序列化为:
- If the string is null: 0xFFFFFFFF (quint32)
- Otherwise: The string length in bytes (quint32) followed by the data in UTF-16.
Run Code Online (Sandbox Code Playgroud)
如果你不喜欢这种格式,你可以这样做,而不是直接序列化 QString
stream << str.toUtf8();
Run Code Online (Sandbox Code Playgroud)
相反,这样 QByteArray 中的数据将采用更简单的格式(UTF-8)。
为什么有这么多空字节?
当编码的长度值很小时,它们被用在固定大小的头字段中;或者指示以 NUL 结尾的 C 字符串的结尾。
我如何删除它,包括最后一个空字节?
您可以按照您的首选格式添加字符串(没有 NUL 终止符,但具有单个长度的标头字节),如下所示:
const char * hello = "hello";
char slen = strlen(hello);
stream.writeRawData(&slen, 1);
stream.writeRawData(hello, slen);
Run Code Online (Sandbox Code Playgroud)
...但是如果您可以选择,我强烈建议您将 NUL 终止符字节保留在字符串末尾,原因如下:
前面的单个长度字节会将字符串长度限制为 255 个字节(或更短),这是一个不必要的限制,将来可能会困扰您。
避免 NUL 终止符字节实际上不会节省任何空间,因为您添加了一个字符串长度字节来补偿。
如果存在 NUL 终止符字节,您可以简单地将指向字符串第一个字节的指针直接传递给任何需要 C 风格字符串的代码,并且它将能够立即使用该字符串(无需任何数据转换步骤) )。如果您依赖于不同的约定,则最终必须先复制整个字符串,然后才能将其传递给该代码,这样您就可以将 NUL 字节附加到字符串的末尾,以便需要 C 字符串的代码可以使用它。这会导致 CPU 效率低下并且容易出错。