在解析UTF-8时防止超长形式

时间:2011-09-13 09:47:39

标签: unicode utf-8

我一直在研究另一种UTF-8解析器作为个人练习,虽然我的实现工作得很好,但它拒绝了大多数格式错误的序列(用U + FFFD替换它们),我似乎无法弄清楚如何实施拒绝超长表格。谁能告诉我怎么做?

伪代码:

let w = 0, // the number of continuation bytes pending
    c = 0, // the currently being constructed codepoint
    b,     // the current byte from the source stream
    valid(c) = (
        (c < 0x110000) &&
        ((c & 0xFFFFF800) != 0xD800) &&
        ((c < 0xFDD0) || (c > 0xFDEF)) &&
        ((c & 0xFFFE) != 0xFFFE))
for each b:
    if b < 0x80:
        if w > 0: // premature ending to multi-byte sequence
            append U+FFFD to output string
            w = 0
        append U+b to output string
    else if b < 0xc0:
        if w == 0: // unwanted continuation byte
            append U+FFFD to output string
        else:
            c |= (b & 0x3f) << (--w * 6)
            if w == 0: // done
                if valid(c):
                    append U+c to output string
    else if b < 0xfe:
        if w > 0: // premature ending to multi-byte sequence
            append U+FFFD to output string
        w = (b < 0xe0) ? 1 :
            (b < 0xf0) ? 2 :
            (b < 0xf8) ? 3 :
            (b < 0xfc) ? 4 : 5;
        c = (b & ((1 << (6 - w)) - 1)) << (w * 6); // ugly monstrosity
    else:
        append U+FFFD to output string
if w > 0: // end of stream and we're still waiting for continuation bytes
    append U+FFFD to output string

3 个答案:

答案 0 :(得分:3)

如果你保存你需要的字节数(所以保存初始值w的第二个副本),你可以比较代码点的UTF32值(我想你正在调用它{ {1}})具有用于对其进行编码的字节数。你知道的:

c

(我希望我在左栏做了正确的数学!十六进制数学不是我的强点:-))

正如旁注:我认为存在一些逻辑错误/格式错误。 U+0000 - U+007F 1 byte U+0080 - U+07FF 2 bytes U+0800 - U+FFFF 3 bytes U+10000 - U+1FFFFF 4 bytes U+200000 - U+3FFFFFF 5 bytes U+4000000 - U+7FFFFFFF 6 bytes 如果w = 0会发生什么? (例如,如果要解码if b < 0x80 if w > 0)?当您发现非法代码点时,是否应该重置A

答案 1 :(得分:1)

一旦你有了解码后的字符,你可以通过查看最高位集来判断正确编码时应该有多少字节。

如果最高设置位的位置<= 7,则UTF-8编码需要1个八位字节。
如果最高设置位的位置<= 11,则UTF-8编码需要2个八位字节 如果最高设置位的位置<= 16,则UTF-8编码需要3个八位字节 等

如果保存原始w并将其与这些值进行比较,您就可以判断编码是正确还是过长。

答案 2 :(得分:1)

我最初认为,如果在解码一个字节w > 0 && c == 0之后的任何时间点,你都有一个超长的形式。然而,正如Jan指出的那样,它比那更复杂。最简单的答案可能是有xanatos之类的表,只拒绝超过4个字节的任何内容:

if c < 0x80 && len > 1 ||
   c < 0x800 && len > 2 ||
   c < 0x10000 && len > 3 ||
   len > 4:
 append U+FFFD to output string