跳轉到

Python操作YAML文件

今天維護某個外包專案,看到該專案有使用YAML套件,順便紀錄一下使用方式。

套件安裝

官方Github

pip install pyyaml

讀取YAML文件

假設有一個名為config.yaml的YAML文件,內容如下:

database:
  host: localhost
  port: 3306
  username: user
  password: pass
使用PyYAML庫中的safe_load方法來讀取這個文件:
import yaml

with open('config.yaml', 'r', encoding = "utf-8") as file:
    config = yaml.safe_load(file)

print(config)
上面那段程式會輸出配置文件的內容,以Python字典的形式,如下圖。

補充: 在yaml也有一個yaml.load函數,在官方說明上,此函數不安全,建議使用yaml.safe_load進行載入

不使用yaml.load函數進行載入的原因?

在前面有說不要使用yaml.load函數進行載入,主要是因為會yaml.load可能會引發的安全性問題,如下面範例

import yaml
from yaml import Loader
yaml_data = """
!!python/object/apply:eval
  args: ['print("Hello World!")']
"""
yaml.load(yaml_data, Loader)

上面程式碼可以看到 yaml_data 中含有 !!python/object/apply:eval 的字串,可以讓 PyYMAL 在解析字串時,順便執行 Python 程式碼列印出 Hello World 字串,因為這個關係,使用yaml.load,是可以強制執行python程式碼,如果yaml的來源是不受信任來源(如:Internet),要使用 yaml.safe_load()會更安全 , 因為該方法只會讀取 YAML 標準定義的 tags,可以避免造成上面不安全的問題。

以下是使用yaml.safe_load()的範例

import yaml

yaml_data = """
!!python/object/apply:eval
  args: ['print("Hello World!")']
"""

try:
    parsed_data = yaml.safe_load(yaml_data)
except yaml.YAMLError as e:
    print(f"Error: {e}")

上述程式碼將會出現以下錯誤,代表其不接受 !!python/object/apply:eval tag:

寫入YAML文件

如果想要修改設定並保存回文件,可以使用dump方法,例如修改database底下的port參數:

config['database']['port'] = 3307

with open('config.yaml', 'w', encoding = "utf-8") as file:
    yaml.dump(config, file)
這樣,修改後的設定就會被寫回config.yaml文件中。

寫入時中文亂碼的處理方式

當參數的設定值有含有中文內容時,在進行保存會導致中文變成亂碼(unicode編碼),以上面yaml內容舉例

database:
  host: localhost
  hostName: SuperComputer #這裡新增一個參數
  port: 3306
  username: user
  password: pass
將其中的hostName值從SuperComputer改成超級電腦保存
import yaml

#讀取yaml
with open('config.yaml', 'r', encoding="utf-8") as file:
    config = yaml.safe_load(file)

#改寫hostName設定值
config['database']['hostName'] = "超級電腦"

#保存
with open('config.yaml', 'w', encoding="utf-8") as file:
    yaml.dump(config, file)

我們看一下config.yaml檔案,可以看見hostname的值變成亂碼(unicode編碼)

要解決這個問題,需設定allow_unicode=true,即可正常顯示中文

#...前面省略

#改寫hostName設定值
config['database']['hostName'] = "超級電腦"

with open('config.yaml', 'w', encoding="utf-8") as file:
    yaml.dump(config, file, allow_unicode=True)