如何使用Python解析并修改带全局作用域的INI文件?
用Python解析带全局作用域与.include指令的INI文件
完全可以用Python实现这个需求,ConfigParser本身不支持全局键和.include指令,但我们可以通过预处理内容来解决这两个问题,下面是具体方案:
核心思路
- 处理全局作用域:ConfigParser要求所有键必须属于某个section,我们可以把顶部的全局键临时放到一个自定义section(比如
[__global__])里,让ConfigParser正常加载,之后再单独提取这部分作为全局配置。 - 处理.include指令:递归解析文件中的
.include行,把对应文件的内容插入到当前位置,默认按当前INI文件的目录来查找包含的文件。
完整代码实现
1. 解决大小写敏感问题的ConfigParser子类
默认ConfigParser会把键名转成小写,这个子类可以保留原键名的大小写:
from configparser import ConfigParser class CaseSensitiveConfigParser(ConfigParser): def optionxform(self, optionstr): return optionstr
2. 递归解析.include指令的函数
import os def resolve_includes(file_path, content=None): # 读取文件内容(未传入则直接读取目标文件) if content is None: with open(file_path, 'r', encoding='utf-8') as f: content = f.readlines() resolved_content = [] base_dir = os.path.dirname(file_path) for line in content: stripped_line = line.strip() # 识别并处理.include指令 if stripped_line.startswith('.include'): include_file = stripped_line.split(maxsplit=1)[1] # 拼接包含文件的完整路径 full_include_path = os.path.join(base_dir, include_file) # 递归解析嵌套的包含文件 with open(full_include_path, 'r', encoding='utf-8') as f: resolved_content.extend(resolve_includes(full_include_path, f.readlines())) else: resolved_content.append(line) return resolved_content
3. 加载自定义INI文件的函数
def load_custom_ini(file_path): # 先解析所有嵌套的.include指令 resolved_lines = resolve_includes(file_path) # 预处理全局键,移到临时section processed_lines = [] in_section = False global_key_lines = [] for line in resolved_lines: stripped_line = line.strip() # 判断是否进入了某个section if stripped_line.startswith('[') and stripped_line.endswith(']'): in_section = True # 如果之前有未处理的全局键,先写入临时section if global_key_lines: processed_lines.append('[__global__]\n') processed_lines.extend(global_key_lines) processed_lines.append('\n') global_key_lines = [] processed_lines.append(line) # 未进入section且是有效键值对(排除注释和空行) elif not in_section and '=' in stripped_line and not stripped_line.startswith((';', '#')): global_key_lines.append(line) else: processed_lines.append(line) # 处理文件末尾可能残留的全局键 if global_key_lines: processed_lines.append('[__global__]\n') processed_lines.extend(global_key_lines) # 加载到大小写敏感的ConfigParser config = CaseSensitiveConfigParser() config.read_string(''.join(processed_lines)) # 提取全局配置并移除临时section global_config = dict(config['__global__']) if '__global__' in config else {} if '__global__' in config: del config['__global__'] return global_config, config
4. 修改并保存INI文件的函数
def save_custom_ini(file_path, global_config, config, original_includes=None): with open(file_path, 'w', encoding='utf-8') as f: # 写入全局键 for key, val in global_config.items(): f.write(f'{key} = {val}\n') f.write('\n') # 保留原有的.include指令(如果需要的话) if original_includes: for include_line in original_includes: f.write(f'{include_line}\n') f.write('\n') # 写入各个section的配置 for section in config.sections(): f.write(f'[{section}]\n') for key, val in config.items(section): f.write(f'{key} = {val}\n') f.write('\n')
使用示例
# 加载目标INI文件 global_cfg, section_cfg = load_custom_ini('your_file.ini') # 修改配置内容 global_cfg['global_key'] = 'new_global_value' section_cfg.set('section', 'key', 'new_modified_value') # 提取原文件中的.include指令(用于保存时保留原引用) original_includes = [] with open('your_file.ini', 'r', encoding='utf-8') as f: for line in f: stripped = line.strip() if stripped.startswith('.include'): original_includes.append(stripped) # 保存修改后的文件 save_custom_ini('modified_file.ini', global_cfg, section_cfg, original_includes)
注意事项
- 如果包含的文件也有全局键或
.include指令,递归函数会自动处理嵌套; - 代码保留了原INI文件的注释、空行和键名大小写;
- 如果不需要保留
.include指令,保存时可以跳过original_includes相关的代码块。
内容的提问来源于stack exchange,提问作者Georgii
相关产品推荐
相关产品推荐

