文章摘要
Unicode虽好,但并非所有字符都适合用于数据结构和协议中的文本字段。RFC 9839针对这一问题,明确了哪些字符应被排除,并提供了三个较为合理的字符子集供选择。该RFC特别为软件和网络领域的设计者编写,建议在设计新系统时参考,以避免使用如JSON中的“问题字符”导致的潜在问题。
文章总结
RFC 9839与不良Unicode字符
Unicode是一种广泛使用的字符编码标准,但在设计包含文本字段的数据结构或协议时,并非所有Unicode字符都适合使用。为此,Paul Hoffman与作者共同起草了一份IETF草案,并于2025年正式发布为RFC 9839。该RFC详细解释了哪些Unicode字符是不良字符,并提供了三个可供选择的字符子集。
问题背景
RFC 9839主要关注“问题字符”,以下是一个典型的例子:假设你设计了一个使用JSON的协议,其中一个字段是username,你可能会收到如下消息:
json
{
"username": "\u0000\u0089\uDEAD\uD9BF\uDFFF"
}
解析后,username字段包含四个Unicode字符:
- U+0000:空字符,在某些编程语言中会引发问题。
- U+0089:C1控制字符,功能复杂且难以处理。
- U+DEAD:未配对的代理字符,UTF-16编码中才有意义,UTF-8中不应使用。
- U+7FFFF:非字符,Unicode中明确禁止在数据传输中使用。
这些字符都属于“问题字符”,RFC 9839正式定义了这些问题,并提供了明确的排除标准。
与PRECIS框架的对比
早在2025年之前,IETF就发布了RFC 8264,即PRECIS框架,用于处理国际化字符串的预处理、执行和比较。PRECIS框架更为复杂,涵盖了更多潜在的不良Unicode问题,但由于其复杂性和对特定Unicode版本的依赖,开发者较少采用。
相比之下,RFC 9839更为简洁,适合那些希望快速排除问题字符的开发者。
工具与实现
作者还编写了一个Go语言库,用于验证文本字段是否符合RFC 9839定义的三个字符子集,代码可在GitHub上找到。
总结
RFC 9839为处理不良Unicode字符提供了简单而实用的解决方案,尤其适合那些使用JSON等数据格式的开发者。虽然PRECIS框架更为全面,但RFC 9839的简洁性使其更易于应用。
致谢
RFC 9839的发布离不开众多专家和同行的讨论与改进,作者特别感谢共同编辑Paul Hoffman以及所有参与贡献的人士。
个人建议
尽管作者成功推动了RFC 9839的发布,但他建议其他开发者优先考虑通过IETF工作组来制定标准,因为这种方式更为高效和可靠。
评论总结
评论主要围绕Unicode字符的处理和标准化展开,观点多样且涉及多个方面。以下是总结:
Unicode的复杂性与处理挑战:
- 多位评论者提到Unicode的复杂性,认为其像“野生的丛林”,处理时需要特别小心某些字符(如控制字符、未配对的代理项等)。
- 引用:
- "Unicode feels like a wild jungle of complexity." (评论2)
- "I have had real-world programs broken by blind assumption of 'does not deliberately contain controls'." (评论3)
JSON序列化与字符过滤:
- 有评论建议JSON序列化库应提供过滤“不良字符”的选项,以避免潜在问题。
- 引用:
- "Seems like libraries that serialize to JSON should have an option to filter out these bad characters." (评论1)
字符限制与标准化:
- 部分评论者支持对Unicode字符进行限制,特别是用户名和密码等场景,以避免安全问题。
- 引用:
- "I’d rather disallow whatever the latest emoji to hit the streets is from usernames than potentially allow it to screw up every page that displays usernames." (评论5)
- "I think there should be a restriction in the standard on how many Unicode scalar values a graphical unit can have." (评论6)
字符分类与工具:
- 有评论提到Unicode已经定义了字符的“通用类别”,并建议使用现有工具(如Python的
unicodedata或Go的unicode.IsPrint)来处理字符。 - 引用:
- "Unicode already defines a 'General Category' for all code points." (评论7)
- "how does this compare to Go
unicode.IsPrint(r rune)?" (评论10)
- 有评论提到Unicode已经定义了字符的“通用类别”,并建议使用现有工具(如Python的
字符的合法性与实际应用:
- 部分评论者质疑某些字符被标记为“不良”的原因,认为某些控制字符(如NUL、EOF)在实际应用中仍有合法用途。
- 引用:
- "Excluding all of 'legacy controls' not just as literals but also escaped strings seems too much." (评论9)
- "I was not able to understand why these code points are bad." (评论8)
UTF-8与UTF-16的处理差异:
- 有评论指出UTF-8和UTF-16在处理无效字符时的差异,并建议在处理外部数据时谨慎转换。
- 引用:
- "The worst part about UTF-16 is that invalid UTF-16 is fundamentally different than invalid UTF-8." (评论3)
总结:评论者普遍认为Unicode处理复杂且充满挑战,支持在某些场景下对字符进行限制,但也强调某些字符在实际应用中仍有合法用途。现有工具和标准(如RFC 8264)为处理这些问题提供了参考,但具体实现仍需谨慎。