相同参数调用addingPercentEncoding为何编码结果不一致?
问题
以下Swift代码示例:
let path1 = "[path:test]" var set = CharacterSet() set.formUnion(.urlPathAllowed) let encodedPath1 = path1.addingPercentEncoding(withAllowedCharacters: set) let encodedPath2 = path1.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) print("Encoded Path 1: \(String(describing: encodedPath1))") print("Encoded Path 2: \(String(describing: encodedPath2))")
运行后输出结果如下:
Encoded Path 1: Optional("%5Bpath:test%5D")
Encoded Path 2: Optional("%5Bpath%3Atest%5D")
可以看到两次调用addingPercentEncoding的编码结果不一致,第一个示例中的:未被编码,第二个示例中的:被编码为%3A,这是为什么?
解答
这是因为Swift的CharacterSet对.urlPathAllowed这类预定义集合采用了特殊内部优化:它们被实现为反转集合——不会直接存储所有允许的字符(Unicode字符数量庞大,这样做效率极低),而是存储一小部分被禁止的字符,默认所有其他字符都属于允许范围。
当你创建一个空的CharacterSet(这是标准的「包含型」集合,初始不包含任何字符),再调用formUnion(.urlPathAllowed)时,这个操作会把反转集合转换为标准的包含型集合。但转换过程无法完全复刻原反转集合的逻辑,最终生成的集合意外包含了原.urlPathAllowed所排除的字符(比如:),所以第一个示例中的:不会被编码。
而直接使用.urlPathAllowed时,它会按照原反转集合的规则工作::属于被禁止的字符,因此会被编码。
解决方法
- 若只需使用
.urlPathAllowed的默认规则,直接引用即可,像第二个示例那样。 - 若需要基于
.urlPathAllowed扩展允许的字符,不要从空集合开始,而是直接复制预定义集合再修改:
var set = CharacterSet.urlPathAllowed // 比如添加允许冒号 set.insert(":")
内容的提问来源于stack exchange,提问作者user501836
相关产品推荐
相关产品推荐

