Python 解析 ini 配置文件
楔子
在開發過程中,配置文件是少不了的,只不過我們有時會將 py 文件作爲配置文件(config.py),然後在其它的模塊中直接導入。這樣做是一個好主意,不過配置文件是有專門的格式的,比如:ini, yaml, toml 等等。
而對於 Python 而言,也都有相應的庫來解析相應格式的文件,下面我們來看看 ini 文件要如何解析。
ini 文件
先來了解一下 ini 文件的格式:
[satori]
name = 古明地覺
age = 16
where = 東方地靈殿
[koishi]
name = 古明地戀
age = 15
where = 東方地靈殿
[marisa]
name = 霧雨魔理沙
age = 17
where = 魔法森林
; 以分號或井號開頭表示註釋,不影響
ini 文件總分可以分爲三塊,分別是:
-
section:就是寫在 [] 裏面的內容,可以把它理解爲一個段;
-
parameter:以 key = value 的形式出現,比如 age = 16,那麼 age 就是 key、16 就是 value,注意:每個 section 都有自己的 parameter;
-
註釋:以分號開頭,無影響,會被忽略掉。
可以看到結構還是比較清晰的,那麼 Python 要如何解析呢?Python 解析 ini 文件需要使用一個名叫 configparser 的庫,這個庫是自帶的,我們可以直接用。
import configparser
# 實例化一個 ConfigParser 實例
config = configparser.ConfigParser()
# 打開 ini 文件
config.read("cfg.ini", encoding="utf-8")
# 獲取所有的 section
print(config.sections())
"""
['satori', 'koishi', 'marisa']
"""
# 獲取某一個 section 的所有 parameter
print(config["satori"])
"""
<Section: satori>
"""
# 我們可以像操作字典一樣操作 parameter
print(list(config["satori"]))
"""
['name', 'age', 'where']
"""
print(list(config["satori"].values()))
"""
['古明地覺', '16', '東方地靈殿']
"""
print(list(config["satori"].items()))
"""
[('name', '古明地覺'), ('age', '16'), ('where', '東方地靈殿')]
"""
# 獲取某個 key 對應的 value
# 如果 key 不存在則拋出 KeyError
print(config["marisa"]["where"])
"""
魔法森林
"""
# 也可以調用 get 方法
# 在 key 不存在時,指定一個默認值
print(config["marisa"].get("age"))
"""
17
"""
# 我們發現 age 居然是一個字符串
# 因爲默認解析得到的都是字符串
print(config["marisa"]["age"].__class__)
"""
<class 'str'>
"""
# 可以通過 getint 獲取
# 會將 value 轉成整型,但轉化失敗的話會報錯
# 除了 getint 之外,還有 getfloat、getboolean
print(config["marisa"].getint("age") == 17)
"""
True
"""
# 最後也可以直接轉成字典
print(dict(config["koishi"]))
"""
{'name': '古明地戀', 'age': '15', 'where': '東方地靈殿'}
"""
print(dict(config))
"""
{'DEFAULT': <Section: DEFAULT>,
'satori': <Section: satori>,
'koishi': <Section: koishi>,
'marisa': <Section: marisa>}
"""
print({k: dict(v) for k, v in config.items()})
"""
{'DEFAULT': {},
'satori': {'name': '古明地覺',
'age': '16',
'where': '東方地靈殿'},
'koishi': {'name': '古明地戀',
'age': '15',
'where': '東方地靈殿'},
'marisa': {'name': '霧雨魔理沙',
'age': '17',
'where': '魔法森林'}}
"""
可以看到還是比較容易的,因爲 ini 這種文件格式本身就很簡單。除了讀取文件,我們還可以進行寫入。
import configparser
# 實例化一個 ConfigParser 類的實例
config = configparser.ConfigParser()
config["basic"] = {"Host": "127.0.0.1",
"Port": "8888",
"Username": "satori"}
config["thread"] = {}
config["thread"]["name"] = "my_thread"
config["thread"]["num"] = "3"
with open("cfg.ini", "w", encoding="utf-8") as f:
config.write(f)
雖然成功寫入了,但是我們看到結果變成了小寫。是的,對於 parameter 來說,無論是大寫還是小寫,寫入文件的時候都會變成小寫。然後讀取也是,無論 ini 文件中是大寫還是小寫,讀取之後都會變成小寫。
注意:大小寫不敏感只是針對於 parameter,對於 section 來說還是區分大小寫的。
特殊格式
我們上面配置的 parameter 中的 key, value 都是一個普通的單詞,但其實我們還可以配置的更加複雜一些。
我們操作一波,看看能否正常解析。
import configparser
# 實例化一個 ConfigParser 實例
config = configparser.ConfigParser()
# 打開 ini 文件
config.read("cfg.ini", encoding="utf-8")
print(dict(config["簡單值"]))
"""
{'鍵': '值',
'鍵 裏面 有空格': '合法',
'值 裏面 有空格': '也 合 法',
'等號 周圍 有 空格': '仍然合法',
'你也使用': '代替等號'}
"""
print(dict(config["所有值都是字符串"]))
"""
{'這是字符串': '123',
'這也是字符串': '3.14',
'整數、浮點數、布爾值都是字符串': 'true'}
"""
# true True yes 都可以轉成布爾值 True
# false False no 都可以轉成布爾值 False
print(config["所有值都是字符串"].getboolean(
'整數、浮點數、布爾值都是字符串'))
"""
True
"""
print(dict(config["值佔多行"]))
"""
{'洪世賢': '你怎麼穿品如的衣服啊\n還用人東西'}
"""
print(dict(config["值爲空字符串"]))
"""
{'key1': '', 'key2': ''}
"""
結果是正常的,但是很明顯上面這種做法有點閒的沒事了,以後就統一寫成 key = value 的形式即可。另外如果 ini 文件中只有 key 沒有 value 的話,默認是報錯的,但可以通過一個參數改變這一點:
import configparser
# "key =" 這種形式不叫沒有值,它是有值的,值爲空字符串
# "key" 這種形式纔是沒有值,解析的時候默認會報錯
# 可以通過一個參數改變這一點
config = configparser.ConfigParser(allow_no_value=True)
config.read_string(
"""
[mysqld]
user = mysql
skip-bdb
""")
print(dict(config["mysqld"]))
"""
{'user': 'mysql', 'skip-bdb': None}
"""
除此之外,name 之間還可以發生引用。
import configparser
config = configparser.ConfigParser()
# 可以通過 %(key)s 的方式對同一個 section 中的其它 key 進行引用
# 所以如果想表示一個 % 的話,需要寫兩個 %,因爲涉及到轉義
config.read_string("""
[section1]
user = 古明地覺
age = 16
info = %(user)s--%(age)s
percent = 80%%
""")
print(dict(config["section1"]))
"""
{'user': '古明地覺', 'age': '16',
'info': '古明地覺--16', 'percent': '80%'}
"""
還是很簡單的,如果想引用其它的 section 中的 name 要怎麼做呢?
import configparser
# 指定該參數之後,我們就不能通過 %(name)s 的方式引用了
# 需要使用 ${name} 這種格式,顯然更方便了
config = configparser.ConfigParser(
interpolation=configparser.ExtendedInterpolation()
)
config.read_string("""
[DEFAULT]
默認的 = 自動加入到每一個 section 中
[section1]
user = 古明地覺
age = 16
info = ${user} -- ${age}
[section2]
info = ${section1:user}, ${section1:age}
""")
print(dict(config["section1"]))
"""
{'user': '古明地覺',
'age': '16',
'info': '古明地覺 -- 16',
'默認的': '自動加入到每一個 section 中'}
"""
print(dict(config["section2"]))
"""
{'info': '古明地覺, 16',
'默認的': '自動加入到每一個 section 中'}
"""
小結
以上就是 ini 文件的一些簡單用法,以後我們在寫配置的時候,不妨使用一些專門用來表示配置的文件格式,不一定非要寫在 py 文件裏面。
而且使用 ini 等配置文件的一個好處就是,即便不懂 Python 的人也能看懂;或者這個配置文件不一定是要由你來寫,可能是別人寫,而那個人不用 Python,但是通過 ini 文件的話就省去了溝通的成本。
後續我們繼續介紹其它種類的配置文件,比如 yaml, toml,因爲 ini 雖然簡單,但表達能力還是很有限的。而 yaml 和 toml 的表達能力要更豐富,應用領域也要更廣一些。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/SZexbcAhVTe1X_dnAD8_hw