plist_from_xml仅支持utf-8无bom且首行为

用 libplist 读取二进制/ASCII PList 文件时,为什么 plist_from_xml 总返回 NULL
因为 Apple 的 PList 格式分两种:XML(人类可读)和 Binary(默认 macOS/iOS 使用),而 plist_from_xml 只处理 XML 格式,且**严格要求输入是 UTF-8 编码、无 BOM、首行必须是 <?xml 声明**。直接用它读二进制文件或带 BOM 的 XML 文件必然失败。
实操建议:
- 先用
file my.plist或检查前 4 字节判断格式:bplist开头是二进制,<?xm开头才可能是 XML - 二进制 PList 统一用
plist_from_bin;XML 用plist_from_xml,但务必确保输入 buffer 是纯 UTF-8、不含 BOM、且以<?xml起始 - 读文件后别直接传给解析函数——先用
fread读入内存,再用memchr检查前 8 字节是否含bplist00,决定调用哪个解析入口
plist_t 解析后如何安全遍历嵌套的 dict 和 array
PList 的结构本质是树,plist_t 是 opaque 指针,不能直接解引用。常见错误是把 plist_dict_next_item 返回值当 key 名用,或误用 plist_array_get_item 索引越界。
实操建议:
- 遍历
dict:用plist_dict_get_keys_vals一次性拿到所有 key/value 指针数组,避免反复调用next_item导致迭代器失效 - 访问
array:先用plist_array_get_size获取长度,再循环调用plist_array_get_item,索引从 0 到size - 1 - 类型校验必须做:每次取值后立刻用
plist_get_node_type判断类型,比如PLIST_STRING才能调plist_get_string_val,否则触发未定义行为
解析后的字符串值为什么中文乱码或显示为空
libplist 内部字符串统一用 UTF-8 存储,但某些旧版 macOS 导出的 PList(尤其通过 defaults export)可能混用 MacRoman 编码,或字段值实际是 data 类型(Base64 编码的二进制块),而非 string。
实操建议:
- 先确认节点类型:若为
PLIST_DATA,需用plist_get_data_val取出原始字节,再按实际协议(如 CFPropertyListCreateFromXMLData 要求)解码 - 若确定是
PLIST_STRING但内容异常,用strlen检查长度是否为 0,或用十六进制打印前 16 字节,确认是否含非法控制字符 - 不要假设所有字符串都是 C 风格 null-terminated:libplist 的
plist_get_string_val返回的是 const char*,但长度由第二个参数length输出,必须用该值而非strlen
释放 plist_t 时程序崩溃或内存泄漏
plist_free 不是简单的 free() 替代品——它递归释放整棵树,但如果某个节点被多次 plist_dict_set_item 插入,或手动调用 plist_copy 后忘记配对释放,就会 double-free 或漏释放。
实操建议:
- 遵循“谁分配谁释放”原则:只有
plist_from_xml/plist_from_bin返回的根节点需要plist_free;子节点(如plist_dict_get_item取出的)是借用指针,禁止单独free - 插入节点前用
plist_copy显式复制:比如要把一个已存在的plist_t塞进新 dict,必须先plist_copy,否则原节点被移走,旧树结构损坏 - 调试时开启 libplist 的 debug 模式(编译时加
-DDEBUG),它会在plist_free时检查引用计数异常
真正麻烦的是 PList 中嵌套的 date 和 uid 类型——它们没有对应的标准 C 类型映射,必须用 plist_get_date_val 或 plist_get_uid_val 提取原始 struct,且时间戳是 CFAbsoluteTime(相对于 2001-01-01),不是 Unix time,这点很容易被忽略。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











