这个工具类使用 Python 的 configparser
模块操作 .properties
文件,核心是将 .properties
格式适配为 configparser
支持的 .ini
格式。
核心代码解释
1. 类初始化与配置解析
python
class Properties:
def __init__(self, file_path: str, encoding: str = 'utf-8'):
self.file_path = file_path
self.encoding = encoding
self.config = configparser.ConfigParser(
allow_no_value=True, # 允许无值的键
delimiters=('=',), # 使用等号作为分隔符
comment_prefixes=('#', '!'), # 支持 # 和 ! 开头的注释
inline_comment_prefixes=('#', '!') # 支持行内注释
)
self.config.optionxform = str # 保留键的大小写
self._section = 'DEFAULT' # 默认使用 DEFAULT 部分
self.read() # 读取文件内容
configparser.ConfigParser
是核心解析器optionxform = str
防止键名被转为小写- 添加
[DEFAULT]
部分适配.properties
格式
2. 读取文件
python
def read(self) -> None:
try:
with open(self.file_path, 'r', encoding=self.encoding) as f:
# 添加 [DEFAULT] 部分使 configparser 能解析
config_content = f"[{self._section}]\n" + f.read()
self.config.read_string(config_content)
except FileNotFoundError:
# 文件不存在时创建空配置
self.config[self._section] = {}
- 读取文件内容并在前面添加
[DEFAULT]
部分 read_string
方法从字符串解析配置
3. 保存文件
python
def save(self) -> None:
with open(self.file_path, 'w', encoding=self.encoding) as f:
# 写入时跳过 [DEFAULT] 部分,保持 .properties 格式
for key, value in self.config.items(self._section):
if value is None:
f.write(f"{key}\n")
else:
f.write(f"{key}={value}\n")
- 保存时去掉
[DEFAULT]
部分,恢复.properties
格式 - 处理无值的键(如
key=
或单独的key
)
新增功能实现
1. 遍历所有键值对
python
def items(self) -> List[Tuple[str, str]]:
"""获取所有键值对的列表"""
return list(self.config.items(self._section))
def keys(self) -> List[str]:
"""获取所有键的列表"""
return list(self.config.options(self._section))
def values(self) -> List[str]:
"""获取所有值的列表"""
return [value for _, value in self.config.items(self._section)]
2. 清空文件内容
python
def clear(self) -> None:
"""清空 properties 文件的所有内容"""
self.config[self._section] = {}
self.save()
完整代码
以下是添加了遍历和清空功能的完整代码:
python
import configparser
from typing import Dict, Optional, Union, List, Tuple
class Properties:
"""处理 .properties 文件的工具类,使用 configparser 实现"""
def __init__(self, file_path: str, encoding: str = 'utf-8'):
"""
初始化 Properties 工具类
Args:
file_path: properties 文件路径
encoding: 文件编码,默认为 utf-8
"""
self.file_path = file_path
self.encoding = encoding
self.config = configparser.ConfigParser(
allow_no_value=True,
delimiters=('=',),
comment_prefixes=('#', '!'),
inline_comment_prefixes=('#', '!')
)
self.config.optionxform = str # 保留键的大小写
self._section = 'DEFAULT' # 默认使用 DEFAULT 部分
self.read()
def read(self) -> None:
"""读取 properties 文件内容"""
try:
with open(self.file_path, 'r', encoding=self.encoding) as f:
# 添加默认 section 以兼容 .properties 格式
config_content = f"[{self._section}]\n" + f.read()
self.config.read_string(config_content)
except FileNotFoundError:
# 文件不存在,创建空配置
self.config[self._section] = {}
def get(self, key: str, default: Optional[str] = None) -> Optional[str]:
"""
获取指定键的值
Args:
key: 键名
default: 键不存在时的默认值
Returns:
键对应的值,或默认值
"""
return self.config.get(self._section, key, fallback=default)
def set(self, key: str, value: str) -> None:
"""
设置或修改键值对
Args:
key: 键名
value: 值
"""
self.config.set(self._section, key, value)
def remove(self, key: str) -> None:
"""
删除指定键
Args:
key: 键名
"""
self.config.remove_option(self._section, key)
def set_batch(self, items: Union[Dict[str, str], List[Tuple[str, str]]]) -> None:
"""
批量设置键值对
Args:
items: 字典或元组列表形式的键值对
"""
if isinstance(items, dict):
for key, value in items.items():
self.set(key, value)
elif isinstance(items, list):
for key, value in items:
self.set(key, value)
def save(self) -> None:
"""保存当前配置到文件"""
with open(self.file_path, 'w', encoding=self.encoding) as f:
# 写入时跳过 section 头,保持 .properties 格式
for key, value in self.config.items(self._section):
if value is None:
f.write(f"{key}\n")
else:
f.write(f"{key}={value}\n")
def items(self) -> List[Tuple[str, str]]:
"""获取所有键值对的列表"""
return list(self.config.items(self._section))
def keys(self) -> List[str]:
"""获取所有键的列表"""
return list(self.config.options(self._section))
def values(self) -> List[str]:
"""获取所有值的列表"""
return [value for _, value in self.config.items(self._section)]
def clear(self) -> None:
"""清空 properties 文件的所有内容"""
self.config[self._section] = {}
self.save()
def __getitem__(self, key: str) -> str:
"""通过 [] 语法获取值"""
return self.get(key)
def __setitem__(self, key: str, value: str) -> None:
"""通过 [] 语法设置值"""
self.set(key, value)
def __contains__(self, key: str) -> bool:
"""检查键是否存在"""
return self.config.has_option(self._section, key)
def __str__(self) -> str:
"""返回所有键值对的字符串表示"""
return '\n'.join([f"{k}={v}" for k, v in self.config.items(self._section)])
使用示例
以下是如何使用新增功能的示例:
python
# 初始化并读取文件
props = Properties('config.properties')
# 遍历所有键值对
for key, value in props.items():
print(f"{key} = {value}")
# 获取所有键
all_keys = props.keys()
print("所有键:", all_keys)
# 获取所有值
all_values = props.values()
print("所有值:", all_values)
# 清空文件
props.clear()
print("文件已清空")
# 添加新键值对
props['new_key'] = 'new_value'
props.save()
工作原理总结
这个工具类通过以下方式实现 .properties
文件操作:
-
格式适配:
- 读取时在文件内容前添加
[DEFAULT]
部分,使其符合.ini
格式 - 保存时去掉
[DEFAULT]
部分,恢复.properties
格式
- 读取时在文件内容前添加
-
核心操作:
configparser
负责解析和生成配置optionxform = str
保持键的原始大小写allow_no_value=True
支持无值的键
-
新增功能:
items()
/keys()
/values()
方法提供遍历功能clear()
方法清空所有配置并保存
这样就可以方便地操作 .properties
文件,无需了解底层实现细节。
DEFAULT
部分是 Python configparser
模块的一个特殊概念,源于 .ini
配置文件格式的设计。让我详细解释它的作用、必要性以及历史渊源:
1. DEFAULT 部分的作用
在 .ini
格式中,[DEFAULT]
是一个特殊的全局 section,它的作用是:
- 为所有其他 section 提供默认值
- 定义所有 section 都能继承的公共配置
例如:
ini
[DEFAULT]
host = localhost
port = 3306
[database]
name = mydb
user = root
[test]
name = testdb
这里:
[database]
实际继承了host=localhost
和port=3306
[test]
同样继承了host
和port
,但覆盖了name
2. 为什么在 .properties
文件中需要 DEFAULT?
.properties
文件本身没有 [section]
的概念,它是扁平的键值对结构 。但 configparser
模块必须有一个 section 才能工作,因此我们:
- 读取时 :人为添加
[DEFAULT]
部分,让configparser
能解析 - 保存时 :去掉
[DEFAULT]
部分,保持.properties
格式
这是一种适配技巧 ,让 configparser
能处理 .properties
文件。
3. 历史渊源
.ini
格式的起源
.ini
(Initialization)格式最早出现在 Windows 3.1 系统中,用于存储程序配置- 它使用
[section]
分组,支持层级化配置 [DEFAULT]
作为特殊 section,提供全局默认值
.properties
格式的起源
.properties
是 Java 语言的标准配置格式- 它是简单的
key=value
结构,没有 section 概念 - 通常通过
java.util.Properties
类加载
Python 的 configparser
- Python 的
configparser
模块最初设计用于解析.ini
格式 - 为了兼容
.properties
,我们需要通过[DEFAULT]
做适配
4. 必须使用 DEFAULT 吗?
是的 ,在使用 configparser
时必须有一个 section:
- 如果不指定
[DEFAULT]
,configparser
会抛出MissingSectionHeaderError
- 其他替代方案(如自定义解析器)会更复杂,失去使用标准库的优势
可以将 [DEFAULT]
视为 .properties
文件的隐式容器 ,它只是让 configparser
正常工作的必要手段。
5. 实际影响
使用 [DEFAULT]
对 .properties
文件没有副作用:
- 读取和保存时自动处理
[DEFAULT]
,文件本身不会改变 - 所有键值对仍按原始格式存储
- 工具类的 API(如
get()
、set()
)对用户保持透明,无需关心底层实现
总结
DEFAULT
部分是 configparser
模块的特性,用于适配 .ini
格式:
- 在处理
.properties
文件时,它是必要的适配层 - 不会影响文件内容,只是内存中的解析结构
- 让我们能用标准库处理
.properties
,避免手写解析器的复杂性
理解这一点后,你可以专注于使用工具类的 API,而无需担心 [DEFAULT]
的底层细节。