創用 CC 授權條款
除非另有註明,本網站的著作Fygul Hether製作,以創用CC 姓名標示-非商業性-禁止改作 4.0 國際 授權條款釋出。

2017年7月29日 星期六

configparser:Python的設置檔解析器

有時我們寫了某個程式,希望它啟動後讀取一些程式的設置,或是程式結束之前儲存某些資料,譬如程式的狀態,以利下回程式啟動時可以取用。在Python這類儲存程式設置或狀態的作法可有幾種現成的選擇,pickle, JSON, shelve或是configparser。如果希望設置檔中的內容可方便人工編輯與人眼閱讀,就像Windows上使用的.ini檔那般,那麼configparser是最好的選擇。

Python標準函式庫中的configparser模組有個ConfigParser類別,可方便對.ini檔內容的操作,以下就用一個簡單範例做說明:

設置檔範例

假定作業環境是Windows平台,主程式名為myapp.py,在程式相同目錄的位置有個設置檔myapp.ini,其內容如下:


[default]
; 主視窗左緣/上緣位置
left = 100
top = 50
# 資料輸出目錄
output = D:\temp\myapp_output

[http]
host = www.myapps.com
port = 88

[save]
count = 0

其中有三個設置區段section:default, http與save,分別用來記錄程式主視窗的位置、http連線時的主機與埠號,還有計數器。

以default區段內容而言,第2行以半形';'符號開頭的是註釋;註釋也可用'#'開頭,如第5行。left = 100即指定等號右端100的值(value)給等號左端left這個鍵(key),其形式為key = value,或寫成key=value,等號左右無空格亦可。

如果看倌清楚Python的字典dictionary用法,使用configparser一點也不難,注意一下上述範例的格式,可用字典表示為:


{
    'default': {'left': '100', 'top': '50', 'output': 'D:\temp\myapp_output'},
    'http': {'host': 'www.myapps.com', 'port': '88'},
    'save': {'count': '0'}
}

而ConfigParser處理設置檔的作法,就類似使用字典般的作法。

注意到像left = 100,雖然我們實際所要的值100是個數字,但儲存在文字檔時是視為字串,待讀取後才轉換為數字。這與JSON類似,數值都是以字串型態儲存,但在設置檔中,不需在值的兩端加上字串的 ' 符號。

這裡也故意用output儲存一個Windows上的目錄路徑,在設置檔中無需寫成d:\\temp\\myapp_output。也可用 / 字元做目錄符號。這裡故意用 \ 字元,是因為若利用檔案總管的位址列複製路徑的字串,就是這種形式。

讀取設置

假定現在要讓程式啟動後,讀取設置檔中的值,為求簡化這裡只是簡單的把其中幾個值輸出在螢幕上。


from configparser import ConfigParser
import os

cfg = ConfigParser()
cfg.read('myapp.ini')
print(int(cfg['default']['left']))
print(cfg['default'].getint('top'))
output_filename = os.path.join(cfg['default']['output'], 'my_output.html')
print(output_filename)
print(cfg['http'].get('host'))

從上面可以看到讀取設置的方式,就如同從字典中讀取資料一般,與字典稍有不同的是,ConfigParser另有提供getint(), getfloat(), getboolen()方法供讀取資料時順便指定要轉換成哪種資料型態。當然若轉換失敗,程式是會丟出錯誤訊息的。若懶得用get()之類的方法,可以都用cfg['section']['key']這種形式,再依需要轉換即可,這樣做在形式上不僅較統一,也可少打幾個字。

如果要取得區段、鍵或是值的列表呢?


section_list = cfg.sections()

keys_of_default_section = cfg['default'].keys()
values_of_default_section = cfg['default'].values()

後二者與字典的用法一模一樣。所以也可以像字典的用法這麼做:


for key in cfg['default']:
    print(key)

print('port' in cfg['http'])

寫入設置

延續上面,讀取了其中的檔案設置,做了一些顯示的工作。這裡,在程式結束之前,記錄一下程式的執行次數,即把save區段count鍵右邊的值加1。


counter = int(cfg['save']['count'])
counter += 1
cfg['save']['count'] = str(counter)

with open('myapp.ini', 'w', encoding='cp950') as f:
    cfg.write(f)

提醒

如果你照著上述方法寫入設置檔之後,打開myapp.ini一看會發現註釋不見了。是的,它寫入時是不會保留那些註釋的,它只是把cfg類似字典的內容寫入檔案。

個人的習慣是把程式的設置檔與儲存狀態的輸出檔分開成兩個檔案放,譬如myapp.cfg做設置檔,myapp.ini做儲存程式狀態的檔案。通常程式的設置若有很多項目,需要大量人工編輯工作時,往往會需要加上註釋說明。這類僅需人工編輯,而程式不會寫進去的的設置部分,我習慣分開放,就像Apache Web Server的設置檔,都是人工編輯設置,輸出並不會寫到它的設置檔。而程式要輸出的內容,就另存到另一個檔案,讀與寫分開放,這樣可以避免混亂。再者,寫的檔案也不見得要用.ini檔的格式,本文最前面提到的那幾種都可以考慮。

如果非把讀與寫存在同一檔案不可,又想保存原始的註釋說明,有一種變通方式,就是事先做個sample樣本檔,把註釋說明都寫在裡頭,實際用的檔案則是樣本檔的複製。譬如樣本檔的檔名是myapp_sample.ini,複製成myapp.ini做實際使用的檔案。有些軟體的設置也是使用類似的方式,會提供個樣本設置檔,實際使用的設置檔要自行從它複製來用 。

這段給不明白為何.ini檔要用這三個字母做為副檔名的人參考。ini是initialization初始化的開頭三個字母,.ini檔代表了程式啟動時所要讀取初始化資料的檔案,也就是設置檔。那為何寫入資料的檔也要稱.ini檔?因為往往寫入的資料是程式的某些狀態,下回程式啟動初始化時也會用到,用習慣了,即使不是這類資料,也照例這麼做了。當然了,這只是約定成俗的作法,並不是強制性的規定,我自己就習慣使用.cfg的副檔名來代表人工編輯的設置檔,雖然Windows平台的.cfg有其它一般使用者不怎麼常用到的作用。

以上是configparser的簡單使用,若需要完整說明,請參考Python說明文件

上述的範例myapp.py代碼重新整理列出如下:


from configparser import ConfigParser
import os

cfg = ConfigParser()
cfg.read('myapp.ini')
print(int(cfg['default']['left']))
print(cfg['default'].getint('top'))
output_filename = os.path.join(cfg['default']['output'], 'my_output.html')
print(output_filename)
print(cfg['http'].get('host'))

section_list = cfg.sections()

keys_of_default_section = cfg['default'].keys()
values_of_default_section = cfg['default'].values()

for key in cfg['default']:
    print(key)

print('port' in cfg['http'])

counter = int(cfg['save']['count'])
counter += 1
cfg['save']['count'] = str(counter)

with open('myapp.ini', 'w', encoding='cp950') as f:
    cfg.write(f)

今日正好Netsat尼莎(1709)襲台,盼望可補充點雨水,但別帶來災害。西南邊那個熱帶性低氣壓也來湊熱鬧,Noru (1705)你就不用來了。

沒有留言:

張貼留言