Python Parsers
這邊摘要紀錄 Python 中相關的 Parser 工具
api | Doc Type |
---|---|
Configparser | *.ini |
ElementTree | *.xml |
pandas | *.csv |
openpyxl | *.xls |
argparse | command line |
ConfigParser ini 檔案解析
ini 檔類似一般的 property 檔,但是有再分不同 sections 區塊。
大多是使用在 Windows 作業系統下。
沒有嚴格的檔案規範,也沒有專用的副檔名。
是一種有點自由的設定檔。
ini 基本重點摘要如下:
- 需 Unicode 編碼
- 註解用 # 或 ; 起始。
- 允許使用佔位符或是資料內插。內插語法為 %(attribute)。
- 脫逸字元為 %
- 內容可以以等數的空白進行縮排
ini 檔的範例
1.DEFAULT 區塊中的屬性可作為屬性的預設值。但前提是:
當程式欲取出某一 section 下不存在的 key 的屬性值時,
而 DEFAULT section 有設定的話,則可依 DEFAULT 值代用。2.sections/attributions 可以縮排方便閱讀。文件是這樣說,實際上似乎應該避免。
Indentation 是有條件的,不小心就會出錯,不建議使用。
Section Indentation 似乎只能在曹串的 sections 中使用,意即頂層 section 不可以 indent。
與 nested section 同階層的其他 attributions 也必須 indent。3.multiline values 直接斷行條列即可。
4.空值時屬性後仍須加上冒號或等號,不然會拋出 ParsingError。
ini 檔的範例
範例來源: configparser — Configuration file parser
[DEFAULT]
PI = 3.1415926535
Euler = 2.7182818284
Speed_of_light= 299792458
Avogadro = 6.022140 *10^23
[Simple Values]
key=value
spaces in keys=allowed
spaces in values=allowed as well
spaces around the delimiter = obviously
you can also use : to delimit keys from values
[All Values Are Strings]
values like this: 1000000
or this: 3.14159265359
are they treated as numbers? : no
integers, floats and booleans are held as: strings
can use the API to get converted values directly: true
[Multiline Values]
chorus: I'm a lumberjack, and I'm okay
I sleep all night and I work all day
[No Values]
key_without_value :
empty string value here =
# : or = is required, otherwise configparser.ParsingError
[You can use comments]
# like this
; or this
# By default only in an empty line.
# Inline comments can be harmful because they prevent users
# from using the delimiting characters as parts of values.
# That being said, this can be customized.
[Sections Can Be Indented]
can_values_be_as_well = True
does_that_mean_anything_special = False
purpose = formatting for readability
multiline_values = are
handled just fine as
long as they are indented
deeper than the first line
of a value
# Did I mention we can indent comments, too?
常用函數
config.read_file(ini_file) | 讀取單一個 ini 檔 |
config.read([file_list]) | 一次讀取多個 ini 檔 |
config.read_string(content_of_ini_file) | 讀取 plain text 內容,不同次讀取的資料會整合在一起。 |
config.read_dict(dict_json) | 讀取 dict 結構內容,一樣是可以分段讀資料,然後整合在一起。 |
config.get(path, sub_path) | 依階層路徑取屬性值,預設為 str。無法單獨抓出 Section。Legacy API. |
config.getboolean() | 取值並 cast 為布林 |
config.getint() | 取值並 cast 為數值 |
config.sections() | 列出階層內的 sections, string |
config.items() | 取 config 下的所有元素, 可解包成 key, value pairs |
config.items(sec_attr_name) | 取指定階層下的所有元素, 可解包成 key, value pair |
config['sec']['attr'] | 依階層路徑取值或設值,預設為 str。可以只提取 Section。 |
config.add_section(section_name) | 增加一個 Section。Legacy API. |
config.set(section_name, attribute_name, value) | Section 下增加一個 attribute。Legacy API. |
- 怎麼會這樣,我反倒是比較喜歡 OO 一些的 Legacy API,還好有保留向下相容。
ini 檔案讀取方式
ini exercise
[DEFAULT]
PI = 3.1415926535
Euler = 2.7182818284
Speed_of_light= 299792458
Avogadro = 6.022140 *10^23
[Mail]
mail.disable=true
mailTempalte.from=dummy+from@google.com
mailTempalte.fromname=Insect Totem
[DataSource]
jdbc.driverClassName=org.postgresql.Driver
jdbc.url.host=192.168.10.10
jdbc.url.port=5432
jdbc.url.schema=totem
jdbc.url=jdbc:postgresql://192.168.10.10:5432/totem
jdbc.username=totem
jdbc.password=password
jdbc.db.authority=
[Forbidden.account]
backlist=Ade
Ed
Edd
取值相關 Syntax
- 以 configparser.ConfigParser() 取得 Util
- config.sections() 列出所有 sections
- config['section_name'], 以 dict style 語法取值
- 預設情形下 ConfigParser 讀取出的屬性值皆為 string,可依需要自行轉換型別,或採用取值並轉換的相關 API。
- 操作時:
- section name 是 case sensitive
- attribute name 是 case insensitive
- 可用 in 語法檢查屬性值或 section 是否存在。檢查時仍須注意 section name 是 sensitive
import configparser, os
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
print(config.sections())
# ['Mail', 'DataSource', 'Forbidden.account']
print(config['Mail']) # <Section: Mail>
print(config['Mail']['MaIl.DiSaBlE']) # true
print(type(config['Mail']['MaIl.DiSaBlE'])) # <class 'str'>
print(type(config['DataSource']['jdbc.url.port'])) # <class 'str'>
print('Mail' in config) # True
print('Mail.Disable' in config['Mail']) # True
ini 檔案屬性讀取方式與轉型
- 使用 python built-in functions 轉型
- 使用 ConfigParser 的 getXX() 轉型
關於 config.getboolean() 的解析:
True: '1', 'yes', 'true', 'on' 預設解析為 True False: '0', 'no', 'false', 'off' 預設解析為 False
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
datasource = config['DataSource']
print(datasource.getint('jdbc.url.port')) # 5432
print(type(datasource.getint('jdbc.url.port'))) # <class 'int>
print(int(datasource['jdbc.url.port'])) # built-in
mail = config['Mail']
print(mail.getboolean('mail.disable')) # True
print(type(mail.getboolean('mail.disable'))) # <class 'bool'>
print(config.get('Mail', 'mail.disable')) # 使用 get() 可以寫路徑
ini 檔案屬性預設值
- 分為兩類: DEFAULT 與 Fallback,都是用在查無屬性設定時。
- DEFAULT: 類似系統 Global 參數概念,若 section 中查無屬性時接下來查詢的大池塘。
- Fallback: 當向上查詢屬性至 DEFAULT section 仍查無時,此時可以給定一個當查無屬性時回傳的 fallback value。
- 註: 空值仍視為有該屬性,所以不適用於此處的預設值。
DEFAULT Section
1.DEFAULT 區塊中的屬性可作為屬性的預設值。但前提是:
當程式欲取出某一 section 下不存在的 key 的屬性值時,
而 DEFAULT section 有設定的話,則可依 DEFAULT 值代用。
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
print(config['DEFAULT']['PI']) # 3.1415926535
print('PI' in config['DataSource']) # DEFAULT always in
print(config['Mail']['PI']) # 3.1415926535
Fallback value
同樣是用在查無屬性時,若連 DEFAULT section 也查無的話,可以在取值時先給予預設。
fallback value 必須以 get() 方式取出時才可設定。
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
dataSource = config['DataSource']
print( 'jdbc.db.authority' in dataSource)
print(config.get('DataSource', 'jdbc.db.authority' )) # 注意是有值, 空字串
print(dataSource.get('jdbc.db.authority', fallback='read only')) # 注意是有值, 空字串,所以沒 fallback
print(dataSource.get('attribute_not_exists')) # None
print(dataSource.get('attribute_not_exists', fallback='fallback value')) # fallback value
print(config.get('DataSource', 'attribute_not_exists', fallback='fallback value')) # fallback value
屬性內插(Interpolation) : BasicInterpolation
內插指的是 ini 檔中允許屬性值串接。
被內差的屬性須以 %( attr_name )s 修飾。小括號 前加一個 %, 後補一個 s ,別懷疑,除了%外, 後面還要帶一個 s 。
脫逸字元為 % 百分比符號。
預設採用的 Interpolation 為 BasicInterpolation,不須額外指定或設定即可使用。
- ConfigParser 的 interpolation 預設為 BasicInterpolation()
ini
[Address]
country: Nerverland
city: Indian Camp
road: central
number: 1
address=No.%(number)s %(road)s rd., %(city)s, %(country)s
[Escape.Char]
# escape string: %
score: 80%%
[Gender.Distribution]
male: 50.1%%
female: 49.9%%
BasicInterpolation Example
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
print(config.get('Address', 'address'))
print(config['Address'][ 'address']) # No.1 central rd., Indian Camp, Nerverland
print(config.get('Gender.Distribution', 'male')) # 50.1%
屬性內插(Interpolation) : ExtendedInterpolation
沒特別研究 ExtendedInterpolation,不過猛一看似乎只是內插屬性的插入上不同。比較直覺,類似一般程式語言的用法。推薦!
被內差的屬性須以 ${ attr_name } 修飾。以錢符號與大括號 __ ${ attr_name } 修飾內插屬性。
脫逸字元為 $ 錢符號。
[Address2]
country: Nerverland
city: Indian Camp
road: central
number: 1
address=No.${number}, ${road} rd., ${city}, ${country}
with open (r'D:\tmp\totem2.ini', 'r') as ini_file:
config = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
config.read_file(ini_file)
## ExtendedInterpolation
print(config.get('Address2', 'address'))
ini 格式內容其他讀取方式
config.read([list of filenames])
config.read([list of filenames]): 允許一次讀多個 ini 檔。
不同檔案若出現相同 attribution 則以後讀到的為準。
config = configparser.ConfigParser()
config.read([r'D:\tmp\totem.ini', r'D:\tmp\totem2.ini'])
config.read_string(content_of_ini_file)
config.read_string(content_of_ini_file):
讀取 plain text 內容,不同次讀取的資料會整合在一起。
config = configparser.ConfigParser()
config.read_string('[Profile]\n name=totem')
print(config['Profile']['name'])
config.read_string('[Profile]\n gender:male')
for att in config['Profile']:
print(att) # name, gender
config.read_dict(dict_json)
config.read_dict(dict_json):
讀取 dict 結構內容,一樣是可以分段讀資料,然後整合在一起。
import configparser, os
config = configparser.ConfigParser()
config.read_dict({'Profile': {"name": 'totem',
"gender": 'male'}
})
config.read_dict({'Education': {"Master": 'NTU',
"Bachelor": 'NCHU'}
})
for section in config:
print(section)
for attr in config[section]:
print(attr)
print(" %s=%s" % (attr, config.get(section, attr)))
# DEFAULT
# Profile
# name
# name=totem
# gender
# gender=male
# Education
# master
# master=NTU
# bachelor
# bachelor=NCHU
#
# Process finished with exit code 0
以迴圈讀取 ini 內容: config.items()
- 用來列出 sections 或 attributions
- config.items() : 當 config 為 root 時,可回傳所有 sections, <class 'collections.abc.ItemsView'>。 可解包成 section_name, section_values。
- config.items() : 當 config 為 section 時,可回傳 section 下所有 attributions, <class 'collections.abc.ItemsView'>。 可解包成 attr_name, attr_value。
config.items()
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
sections = config.items() # 未指明所以列全部 sections
print(type(sections)) # <class 'collections.abc.ItemsView'>
for sec_name, sec_values in sections:
print(sec_name, sec_values) # <class 'str'>, <class 'configparser.SectionProxy'>
attrs = sec_values.items() # <class 'collections.abc.ItemsView'>
for attr_name, attr_value in attrs:
print(attr_name, attr_value)
以迴圈讀取 ini 內容: config.sections()
- 可回傳所有 sections, <class 'collections.abc.ItemsView'>。 可解包成 section_name, section_values。
config.sections()
with open (r'D:\tmp\totem.ini', 'r') as ini_file:
config = configparser.ConfigParser()
config.read_file(ini_file)
for s in config.sections():
print("SECTION: " , s)
attrs = config.items(s) # <class 'collections.abc.ItemsView'>,列出指定 section 下的 attrs 包含 DEFAULT 下內容
# attrs = config[s].items(), 此處與上一行等義
for sec_name, sec_values in attrs:
print(sec_name, sec_values)
資料建立與修改
- config.add_section(name)
- config.set(section, attr, value)
- legacy API
import configparser
config = configparser.ConfigParser()
# current API
config['Mail'] = {}
mail = config['Mail']
config['Mail']['mail.disable'] = 'True'
mail['mailTempalte.from'] = 'dummy@org'
mail['mailTempalte.fromname'] = 'dummy'
# Legacy API
config.add_section("Mail.Server")
config.set("Mail.Server", "IP", '192.168.10.10')
config.set("Mail.Server", "Port", '1234')
with open(r'D:/tmp/export.ini', 'w') as configfile:
config.write(configfile)
建立 ini 格式檔
概念上很簡單,就像是編輯一個 dict 然後呼叫 write 寫成檔案。
import configparser
config = configparser.ConfigParser()
config['DEFAULT'] = {'server': 'google.cloud',
'port': '1234'}
# 建立 Mail section, 這 statement 不能省略。
config['Mail'] = {}
# 這邊要注意,因為 python 語法的關係,沒有建立並回傳變數的語法,所以要拆兩行。
mail = config['Mail']
config['Mail']['mail.disable'] = 'True'
mail['mailTempalte.from'] = 'dummy@org'
mail['mailTempalte.fromname'] = 'dummy'
with open(r'D:/tmp/export.ini', 'w') as configfile:
config.write(configfile)