如何在普通C89中读取字符长度的UTF-8字符串?

时间:2011-04-01 18:17:05

标签: c unicode c89

我正在用普通C89编写一个自定义的跨平台简约TCP服务器。 (但我也会接受POSIX特定的答案。)

服务器使用UTF-8字符串,但从不查看它们。它将所有字符串视为不可变二进制blob。

但是现在我需要接受来自客户端的UTF-8字符串,它不知道如何以字节为单位计算它们的大小。客户端只能以字符传输字符串长度。 (更新:客户端使用JavaScript,“字符长度”实际上是String.length()返回的内容。我假设它是实际的UTF-8字符,而不是其他字符。)

我不想在我的小服务器上添加大量依赖项。是否有一种强大而简洁的方式来读取此数据报? (为了这个问题,让我们说它是从FILE *读取的。)

U<CRLF>       ; data type marker (actually read by dispatching code)
<SIZE><CRLF>  ; UTF-8 string size in characters
<DATA><CRLF>  ; data blob

示例:

U
7
Юникод!

更新

一批数据可以包含多个数据报,因此近似读取不起作用,我需要读取确切数量的字符。

实际的UTF-8数据可能包含任何字符,所以我不能选择一个字符作为终结符 - 我不希望在数据中将其转义为混乱。

6 个答案:

答案 0 :(得分:9)

根据信息here,写一个UTF-8“读者”非常容易。 UTF-8的设计非常简单。

从本质上讲,你开始阅读字符,直到你读到客户告诉你的字符数。您知道在给定UTF-8编码定义的情况下,您已经阅读了整个字符,特别是:

  

如果字符是由just编码的   一个字节,高位为0和   其他位给出代码值(in   范围0..127)。如果角色是   由一个以上的序列编码   byte,第一个字节有多少个   将'1'位作为总数   序列中的字节数,后跟   一个'0'位,以及后续的字节   都标有前导“10”位   图案。

答案 1 :(得分:2)

嗯,JavaScript字符串的长度属性似乎计算代码点,而不是字符,正如您所看到的那样(但等等!它不是代码点):

> s1='\u0061\u0301'
'á'
> s2='\u00E1'
'á'
> s1.length
2
> s2.length
1
>

虽然这与V8有关。环顾四周似乎这就是ECMAScript标准所要求的:

https://forums.teradata.com/blog/jasonstrimpel/2011/11/javascript-string-length-and-internationalizing-web-applications

另外,检查ECMA-262,在PDF的第40-41页上,它说“字符串的长度是其中的元素数量(即16位值)”,然后继续说明元素是UTF-16单位。可悲的是,这不是“代码点”。基本上,这使得字符串长度属性相当无用。环顾四周,我发现了这个:

How can I tell if a string contains multibyte characters in Javascript?

答案 2 :(得分:1)

字符?还是代码点?两者不一样。 Unicode很复杂。您可以计算UTF-8字符串的所有这些不同内容:长度(以字节为单位),代码点中的长度,字符长度,字形长度以及字形集群中的长度。对于任何给定的字符串,所有这些都可能会有所不同!

我的第一个倾向是告诉那个破碎的客户离开。但假设你不能这样做,你需要问一下客户究竟在做什么。在字节之后计算的最简单的事情是代码点 - 毕竟这是UTF-8编码的。之后?字符,但您需要具有组成代码点的表,以便您可以识别构成字符的代码点序列。如果客户计算字形或字形集群,那么你就是一个受伤的世界。但很可能客户端会计算代码点或字符。如果它计算代码点,那么只计算具有二进制值10xxxxxx和0xxxxxxx的字节(尽管您可能希望实现足够的UTF-8以防止过长的序列)。如果它对字符进行计数,那么您需要识别组合标记并将它们计为相关非组合代码点的一部分。

答案 3 :(得分:0)

如果你得到的长度与你得到的字节数不匹配,你有几个选择。

  1. 一次读取一个字节并将它们组合成字符,直到您得到匹配的字符数。

  2. 添加已知终结符并完全跳过字符串大小。只需一次读取一个字节,直到您读取终结符序列。

  3. 读取标题中列出的字节数(因为这是最小数字)。弄清楚你是否有足够的角色。如果没有,请阅读更多内容!

答案 4 :(得分:0)

如果DATA不能包含CRLF,那么您似乎可以将CRLF用作成帧分隔符。只需忽略SIZE并读取直到CRLF。

答案 5 :(得分:0)

这看起来就像我需要的东西。希望我早些发现它:

http://bjoern.hoehrmann.de/utf-8/decoder/dfa/