我一直在研究另一种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
答案 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