PyQt5中如何正确存取list/dict/int等类型变量到本地文件
问题根因
原repr()逐行写txt的方案失效,核心是两个硬伤:
- 读取逻辑只拿到了原始文本,没有做类型还原:
readlines()返回的每一行都是纯字符串,还带末尾的换行符,直接把字符串赋值给原本是int/list/dict类型的变量,自然类型不匹配;如果直接对字符串做list转换,会把字符串逐字符拆成单个元素,就是出现的逐字符拆分异常。 - 分隔符和内容冲突:用
\n作为不同配置项的分隔符,但存的自定义代码本身就包含大量\n换行,写入时这些内置换行会把单个配置项拆成多行,读取时行索引完全错位,内容自然对不上,还会残留转义字符。
可行实现方案
pickle完全适用于当前PyQt5桌面应用场景,除此之外也可以根据需求选JSON方案,两种都比手写txt拼接可靠。
方案1:pickle序列化(实现成本最低,无转义问题)
pickle是Python标准库自带的二进制序列化工具,能直接还原int、list、dict、str等原生Python类型,不需要手动处理转义、换行问题,缺点是生成的文件是二进制格式,用户无法直接打开编辑。
存储代码:
import pickle # 把所有配置项打包成字典,不需要手动拼换行 config = { "score": score, "ocount": ocount, "option": option, "option_ans": option_ans, "matchname": matchname, "scenario": scenario, "_Options": _Options, "_Optionselected": _Optionselected, "eInfo": eInfo, "sVar": sVar, "cusotm": cusotm, "survey1": survey1, "survey_ans": survey_ans, "eInfoName": eInfoName, "sVarName": sVarName, "tempcode": tempcode, "usercode": usercode } # 用二进制模式写入 with open(f"{configName}.pkl", "wb") as f: pickle.dump(config, f)
读取代码:
with open(f"{configName}.pkl", "rb") as f: config = pickle.load(f) # 直接按键取值,类型和存储时完全一致,不需要额外转换 score = config["score"] ocount = config["ocount"] usercode = config["usercode"] # 其余配置按相同方式取即可
注意:不要加载来源不可信的pickle文件,存在任意代码执行风险,仅加载自己应用生成的配置文件没有问题。
方案2:JSON存储(纯文本格式,支持人工编辑)
如果需要让用户可以直接打开配置文件手动修改,就用JSON格式,同样是Python标准库自带能力,当前用到的int、list、dict、str类型都在JSON的原生支持范围内,不会有兼容问题。
存储代码:
import json config = { # 配置字典结构和pickle方案完全一致 "score": score, "ocount": ocount, # 其余配置项同上 "usercode": usercode } # 指定utf-8编码,ensure_ascii=False避免中文乱码,indent=4格式化输出方便阅读 with open(f"{configName}.json", "w", encoding="utf-8") as f: json.dump(config, f, ensure_ascii=False, indent=4)
读取代码:
with open(f"{configName}.json", "r", encoding="utf-8") as f: config = json.load(f) # 按键取值即可,类型自动还原
临时兼容原txt方案的方法
如果一定要沿用txt存储格式,需要解决分隔符冲突和类型还原两个问题:
- 替换分隔符:用一个不会出现在用户代码里的特殊字符串(比如
===CUSTOM_CONFIG_SPLIT_FLAG===)代替\n作为配置项分隔符 - 类型还原:读取到每个配置项的字符串后,用
ast.literal_eval()做安全的字面量转换,不要直接用原始字符串,也不要用不安全的eval()
示例代码:
import ast SPLIT_FLAG = "===CUSTOM_CONFIG_SPLIT_FLAG===" # 写入 with open(f"{configName}.txt", "w", encoding="utf-8") as f: write_content = SPLIT_FLAG.join([ repr(score), repr(ocount), repr(option), repr(option_ans), repr(matchname), repr(scenario), repr(_Options), repr(_Optionselected), repr(eInfo), repr(sVar), repr(cusotm), repr(survey1), repr(survey_ans), repr(eInfoName), repr(sVarName), repr(tempcode), repr(usercode) ]) f.write(write_content) # 读取 with open(f"{configName}.txt", "r", encoding="utf-8") as f: raw_content = f.read() raw_items = raw_content.split(SPLIT_FLAG) tmp = [ast.literal_eval(item) for item in raw_items] # 按写入顺序取值即可 score = tmp[0] ocount = tmp[1] usercode = tmp[-1]
该方案稳定性差,一旦分隔符出现在用户输入的代码或配置值里,就会出现解析错误,优先选择前两种方案。
内容的提问来源于stack exchange,提问作者SCH
相关产品推荐
相关产品推荐

