配置文件的选择
一、文件相关操作
不同模式打开文件的完全列表:
模式 | 描述 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件不存在则创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件不存在则创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
有文件config.txt如下,要读取文件中的值
m_url=https://onetouch-partner.aba.com/login.htm? b_url=https://onetouch-partner.aba.com/ptnBasedata/crm/ c_url=https://onetouch-partner.aba.com/ptnBasedata/crm/customerList.htm titls=[公司名称,创建时间,访问时间,生成时间,签约情况,跟进情况,公司地址,联系人姓名,电话,企业邮箱,联系人邮箱] filename=客户信息 word=IDL2020888**
file 对象方法
f.read([size]):size 未指定则返回整个文件,如果文件大小 >2 倍内存则有问题,f.read()将整个文件内容作为一个字符串,读到文件尾时返回""(空字串)。
f.readline():返回一行。
f.readlines([size]) :返回包含size行的列表, size 未指定则返回全部行。
for line in f: print line :通过迭代器访问。
f.write("hello\n"):如果要写入字符串以外的数据,先将他转换为字符串。
f.close() 关闭文件
绝对路径就是文件的真正存在的路径,是指从硬盘的根目录(盘符)开始,进行一级级目录指向文件。
相对路径就是以当前文件为基准进行一级级目录指向被引用的资源文件。
../ 表示当前文件所在的目录的上一级目录
./ 表示当前文件所在的目录(可以省略)
/ 表示当前站点的根目录(域名映射的硬盘目录)
py文件与要读取文件在统一目录下可直接读取,不用家路径
python中转义用\,路径用/,因此windows路径可以是/(与linux一致)也可以用\\,Unix和Web用正斜杠/,Windows用反斜杠,但是现在Windows
python读文件需要输入的目录参数,以下path都是正确的:
path1 = r"C:\Windows\temp\readme.txt" # "\"为字符串中的特殊字符,加上r后变为原始字符串,则不会对字符串中的"\t"、"\r" 进行字符串转义
path2 = "c:\\windows\\temp\\readme.txt" # 用一个"\"取消第二个"\"的特殊转义作用,即为"\\"
path3 = "c:/windows/temp/readme.txt" # 用正斜杠做目录分隔符也可以转到对应目录,并且在python中path3的方式也省去了反斜杠\转义的烦恼
二、常用配置文件格式
适合人类编写:ini > toml > yaml > json > xml > plist
可以存储的数据复杂度:xml > yaml > toml ~ json ~ plist > ini
ini,上古时代的配置文件,优点是简单,缺点也是因为简单。
XML,某段时期的特定产物,优点是某段时期很通用,缺点就是很难阅读,一不小心写错。
JSON,网络传输下的产品,大家发现它竟然意外的好用,做配置文件的话,易写也易读,你的代码基本自带解释器,缺点嘛,不能写注释。但写缺点,其实不过是加个字段来写注释的事,属于强行帮它安个缺点。1.可读性强,2.易上手,确定1.不支持注释
YML,后面出来的配置文件,优点是能写注释,Spring boot框架默认配置格式,不用你额外写什么代码就能帮你搞定了解释。缺点嘛,不适合写复杂点的配置文件,配置文件也不能写对象化的配置。yaml有点1.支持数据格式比较多样,2.支持注释容易发生空格引发的惨案。缺点:1.空格不利于修改编写,2.不容易上手
三、txt
f = open(file) # 打开文件 content = f.read() print(content) f.close() # 关闭文件 #read with open('sample1.txt') as f: content = f.read() print(content) #readline读取该文件: with open('a.txt') as f: print(f.readline()) print(f.readline(5)) #readlines方法没有参数: with open('a.txt') as f: print(f.readlines()) f = open('sample3.txt','w') # 打开文件 content = f.write('hello,my friends!\nthis is python big data analysis') f.close() # 关闭文件 with open('sample3.txt','w') as f: f.write('hello,my friends!\nthis is python big data analysis')
def read_myconfig(): ss=[] f = open("config.txt",'r',encoding='utf-8') #返回一个文件对象 # print(f) for line in f: ss.append(line.split('=')[1].strip("\n")) f.close() print(ss) read_myconfig() def read_myconfig(): ss=[] f = open("config.txt",'r+',encoding='utf-8') #返回一个文件对象 line = f.read() #调用文件的 readline()方法 f.close() # print(line.split('\n')) for i in (line.split('\n')): ss.append(i.split('=')[1])#以每行的换行划分,将每行作为列表的一个元素。 print(ss) read_myconfig() def read_myconfig(): ss=[] f = open("config.txt",'r+',encoding='utf-8') #返回一个文件对象 line = f.readline() #调用文件的 readline()方法 # print(line) while line: ss.append(line.split()[0].split('=')[1])#先过滤掉行末的换行 line = f.readline() f.close() print(ss) read_myconfig() def read_myconfig(): ss=[] f = open("config.txt",'r',encoding='utf-8') #返回一个文件对象 line = f.readlines() #调用文件的 readline()方法 f.close() # print(line) for i in line: ss.append(i.split('=')[1].strip("\n")) print(ss) read_myconfig() def read_myconfig(): f = open("config.txt",'r',encoding='utf-8') #返回一个文件对象 line = f.readlines() #调用文件的 readline()方法 f.close() # print(line) ss=[i.split('=')[1].strip("\n") for i in line] print(ss) read_myconfig()
四、py
用Python变量作为配置文件格式
把配置直接用变量的形式写到一个模块中,在需要读取配置的地方直接import模块就能得到配置变量:
配置文件示例:
# config.py
listen_port = 4444
use_epoll = True
...
在读取配置的地方:
import config
port_num = config.listen_port
if config.use_epoll:
...
优点
直接用python变量作为配置文件格式的优点是显然的:
l 不用任何解释器来解释配置文件。Python解释器本身就可以。
l 使用自然、方便,直接可用,和其他变量的使用没有任何区别。
l 学习成本低,不需要学习其他配置文件的格式和语法。
缺点
方便之处在于不用parser,缺点也在于不用parser。通常情况,配置文件是人负责写,程序负责读。但如果配置文件既要让人写也要让代码写。比如程序的配置可以直接改配置文件,同时也提供了一套界面让用户在界面上修改配置。在界面上修改的配置,最终也是反映到配置文件中,这就要求代码修改配置文件了。此时会有少许不便。
五、ini
.ini、.txt配置文件使用方法是一致的,只是一个后缀的区别,这里以ini配置文件来介绍,这类配置文件我们使用内置configparser库来使用,它可以实现配置文件的写入、更新、删除、读取等操作非常方便,建议使用这种方式。新建一个config.ini的配置文件内容如下:
[mysql] name = admin host = 255.255.255.0 proxy = 6037 password = 123456 pool = true time = 3 #其中[]中的是section节点,该节点下的等式是键值对
常用方法如下:
# -*- coding: utf-8 -*- import configparser config = configparser.ConfigParser() config.read("Config.ini", encoding="utf-8") config.sections() # 获取section节点 config.options('mysql') # 获取指定section 的options即该节点的所有键 config.get("mysql", "name") # 获取指定section下的options config.items("mysql") # 获取section的所用配置信息 config.set("mysql", "name", "root") # 修改值 config.has_section("mysql") # 是否存在该section config.has_option("mysql", "password") # 是否存在该option config.add_section("redis") # 添加section节点 config.set("redis", "name", "redis_admin") # 设置指定section 的options config.remove_section("redis") # 整个section下的所有内容都将删除 config.remove_option("mysql", 'time') # 删除section下的指定options config.write(open("Config", "w")) # 保存config
六,json
{ "GENERAL": { "domains": "tourism", "seed": "20230301" }, "logging": { "screen level": "results", "file_level": "results", "file": "auto" } }
读取,实现了文件配置,缺点是无法写注释、无法写过长或复杂的参数配置,容易出错。
import json with open('configs.json') as j: cfg = json.load(j)['logging'] print(cfg) # {'file': 'auto', 'file_level': 'results', 'screen_level': 'results'}
六、ymal
1、大小写敏感
2、用缩进表示层级关系,缩进只能使用空格,不能用 TAB 字符,缩进的空格数量不重要,但是同一层级的元素左侧必须对齐
# YAML
one:
two: 2
three:
four: 4
five: 5
// 以上的内容转成 JSON 后
"one": {
"two": 2,
"three": {
"four": 4,
"five": 5
}
}
3、用 # 表示注释,只支持单行注释
4、一个文件中可以包含多个文件的内容
- 用“ --- ”即三个破折号表示一份内容的开始
- 用“ ... ”即三个小数点表示一份内容的结束(非必需)
---
# 这是第一份内容
one: 1
# 其他内容...
...
---
# 这是第二份内容
two: 2
# 其他内容...
5、对象(Mapping)
表示以键值对(key: value)形式出现的数据
- 使用“冒号+空格”来分开键与值
# YAML
key: value
// JSON
"key": "value"
- 支持多层嵌套(用缩进表示层级关系)
# YAML
key:
child-key1: value1
child-key2: value2
// JSON
"key": {
"child-key1": "value1",
"child-key2": "value2",
}
- 支持流式风格( Flow style)的语法(用花括号包裹,用逗号加空格分隔,类似 JSON)
# YAML
key: { child-key1: value1, child-key2: value2 }
// JSON
"key": { "child-key1": "value1", "child-key2": "value2" }
- 使用问号“?”声明一个复杂对象,允许你使用多个词汇(数组)来组成键
# YAML
?
- keypart1
- keypart2
:
- value1
- value2
6、数组(Sequence)
- 一组以区块格式(Block Format)(即“破折号+空格”)开头的数据组成一个数组
# YAML
values:
- value1
- value2
- value3
// JSON
"values": [ "value1", "value2", "value3" ]
- 同时也支持内联格式(Inline Format)来表达(用方括号包裹,逗号加空格分隔,类似 JSON)
# YAML
values: [value1, value2, value3]
// JSON
"values": [ "value1", "value2", "value3" ]
- 支持多维数组(用缩进表示层级关系)
# YAML
values:
-
- value1
- value2
-
- value3
- value4
// JSON
"values": [ [ "value1", "value2"], ["value3", "value4"] ]
7、字符串(String)
- 字符串一般不需要用引号包裹,但是如果字符串中使用了反斜杠“\”开头的转义字符就必须使用引号包裹
# YAML
strings:
- Hello without quote # 不用引号包裹
- Hello
world # 拆成多行后会自动在中间添加空格
- 'Hello with single quotes' # 单引号包裹
- "Hello with double quotes" # 双引号包裹
- "I am fine. \u263A" # 使用双引号包裹时支持 Unicode 编码
- "\x0d\x0a is \r\n" # 使用双引号包裹时还支持 Hex 编码
- 'He said: "Hello!"' # 单双引号支持嵌套"
// JSON
"strings":
[ "Hello without quote",
"Hello world",
"Hello with single quotes",
"Hello with double quotes",
"I am fine. ☺",
"\r\n is \r\n",
"He said: 'Hello!'" ]
- 对于多行的文字,YAML 提供了两种特殊的语法支持
保留换行(Newlines preserved)
使用竖线符“ | ”来表示该语法,每行的缩进和行尾空白都会被去掉,而额外的缩进会被保留
# YAML
lines: |
我是第一行
我是第二行
我是吴彦祖
我是第四行
我是第五行
// JSON
"lines": "我是第一行\n我是第二行\n 我是吴彦祖\n 我是第四行\n我是第五行"
折叠换行(Newlines folded)
使用右尖括号“ > ”来表示该语法,只有空白行才会被识别为换行,原来的换行符都会被转换成空格
# YAML
lines: >
我是第一行
我也是第一行
我仍是第一行
我依旧是第一行
我是第二行
这么巧我也是第二行
// JSON
"lines": "我是第一行 我也是第一行 我仍是第一行 我依旧是第一行\n我是第二行 这么巧我也是第二行"
8、布尔值(Boolean)
- “true”、“True”、“TRUE”、“yes”、“Yes”和“YES”皆为真
- “false”、“False”、“FALSE”、“no”、“No”和“NO”皆为假
# YAML
boolean:
- true # True、TRUE
- yes # Yes、YES
- false # False、FALSE
- no # No、NO
// JSON
"boolean": [ true, true, false, false ]
9、整数(Integer)
- 支持二进制表示
# YAML
int:
- 666
- 0001_0000 # 二进制表示
// JSON
"int": [ 666, 4096 ]
10、浮点数(Floating Point)
- 支持科学计数法
# YAML
float:
- 3.14
- 6.8523015e+5 # 使用科学计数法
// JSON
"float": [ 3.14, 685230.15 ]
11、空(Null)
- “null”、“Null”和“~”都是空,不指定值默认也是空
# YAML
nulls:
- null
- Null
- ~
-
// JSON
"nulls": [ null, null, null, null ]
12、时间戳(Timestamp)
- YAML 也支持 ISO 8601 格式的时间数据
这里使用 JavaScript 对象进行对比
# YAML
date1: 2020-05-26
date2: 2020-05-26T01:00:00+08:00
dete3: 2020-05-26T02:00:00.10+08:00
date4: 2020-05-26 03:00:00.10 +8
// JavaScript
date1: Tue May 26 2020 08:00:00 GMT+0800 (中国标准时间),
date2: Tue May 26 2020 01:00:00 GMT+0800 (中国标准时间),
dete3: Tue May 26 2020 02:00:00 GMT+0800 (中国标准时间),
date4: Tue May 26 2020 03:00:00 GMT+0800 (中国标准时间)
13、数据重用与合并
- 为了保持内容的简洁,避免过多重复的定义,YAML 提供了由锚点标签“&”和引用标签“*”组成的语法,利用这套语法可以快速引用相同的一些数据...
# YAML
a: &anchor # 设置锚点
one: 1
two: 2
three: 3
b: *anchor # 引用锚点
// JSON
"a": {
"one": 1,
"two": 2,
"three": 3
},
"b": {
"one": 1,
"two": 2,
"three": 3
}
- 配合合并标签“<<”使用可以与任意数据进行合并,你可以把这套操作想象成面向对象语言中的继承...
# YAML
human: &base # 添加名为 base 的锚点
body: 1
hair: 999
singer:
<<: *base # 引用 base 锚点,实例化时会自动展开
skill: sing # 添加额外的属性
programer:
<<: *base # 引用 base 锚点,实例化时会自动展开
hair: 6 # 覆写 base 中的属性
skill: code # 添加额外的属性
// JSON
"human": { "body": 1, "hair": 999 },
"singer": { "body": 1, "hair": 999, "skill": "sing" },
"programer": { "body": 1, "hair": 6, "skill": "code" }
GENERAL: domains: tourism seed: 20230301 logging: screen_level: results file level: results file: auto
使用yaml的时候,我们不使用load()去加载,而是使用safe_load(),这样去加载即可:
import yaml from pprint import pprint with open("config.yaml", "r") as config: cfg = yaml.safe_load(config) pprint(cfg) pprint(cfg['logging']) pprint(cfg['logging']['file']) #输出 {'GENERAL': {'domains': 'tourism', 'seed': 20230301}, 'logging': {'file': 'auto', 'file level': 'results', 'screen_level': 'results'}} {'file': 'auto', 'file level': 'results', 'screen_level': 'results'} 'auto'