前言

最近工作和生活上比较忙,都没时间研究黑科技了(其实把玩游戏的时间挤出点来也就够了,不过不可能的),今天来水一篇,不对,更新一个常见配置文件格式对比以及使用方法。

常用的配置文件有这几种,最简单的Properties配置文件、经典的ini配置文件、依然保留一亩三分地的xml配置文件、在数据交换领域大显身手的json配置文件、以及充满野心的YAML配置文件等。

一个优秀的配置文件格式应该包含以下几个要素:

  • 规则简单
  • 可读性好
  • 支持层级关系
  • 允许注释
  • 易于解析
  • 逐行解析
  • 支持列表和字典
  • 支持多种数据类型

下面根据这几个要素分析一下它们的优劣和具体使用方法。

 

Properties

.properties 主要用在 JAVA 程序中,JAVA 内置对它的解析。它是一种简单的配置文件格式,规则大概只有下面几条:

  1. 使用 = : 或者空格作为键值对的分隔符;
  2. 单引号和双引号会作为值的一部分;
  3. 允许使用 # 作为注释;
  4. 可以使用 \ 作为转义符,用来转义空格、换行和 Unicode 编码。

因为如此简单的格式,properteis 基本上不需要进行任何解释就能直接使用。也正因为它如此简单,不支持层级关系,因此它并不能算做好的配置文件 。

 

INI

INI相当于扩展版本的 properties ,包含 properties 的所有优点,去除了一些缺点,并且可以使用 section 来支持层级。它与 .properties 的主要区别如下:

  1. 支持 [section];
  2. 不使用空格作为分隔符;
  3. 允许在键名和值中直接包含空格(不需使用转义符);
  4. 支持更多的转义符。

综上,INI 格式是好的配置文件。

在Windows系统中,经常使用INI 文件作为系统或者第三方软件的配置文件,当然INI文件 的后缀名也不一定是”.ini”也可以是”.cfg”,”.conf ”或者是”.txt”。

 

格式

INI文件的格式很简单,最基本的三个要素是:parameters,sections 和 comments。

最基本的“元素”就是parameter也就是键值对,每一个parameter都有一个name和一个value,name和value由等号“=”隔开,name在等号的左边。

而所有的parameters都是以sections为单位结合在一起的。所有的section名称都是独占一行,并且sections名字都被方括号包围着。在section声明后的所有parameters都是属于该section。对于一个section没有明显的结束标志符,一个section的开始就是上一个section的结束。

comments也就是注释,是以分号“;”开始的。所有的注释语句不管多长都是独占一行直到结束的。在分号和行结束符之间的所有内容都是被忽略的。

 

读写示例

下面是一个简单的INI文件。

[mysql]
;这是IP
host = localhost
;这是端口
port = 3307

使用Python读写INI文件的方式如下:

import configparser
import os


def readConf():
    '''读取配置文件'''
    conf = configparser.ConfigParser()
    conf.read('test.conf')  # 文件路径
    properties = conf.items("mysql")
    print(properties)
    name = conf.get("mysql", "host")  # 获取指定section 的option值
    print(name)

def writeConf():
    '''写入配置文件'''
    conf = configparser.RawConfigParser() 
    if not conf.has_section("mongodb"):
        conf.add_section("mongodb")
    if not conf.has_option("mongodb", "port"):
        conf.set("mongodb", "port","27777")
    with open("test.conf", "w") as confFile:
        conf.write(confFile)

 

JSON

由于 JavaScript 的流行,JSON 当然成了最适合 JavaScript 使用的配置文件。JSON 可以在 JavaScript 中使用 eval()或者 JSON.parse() 来解析,解析后的内容直接成为 JavaScript 的内置对象。

但是,其实 JSON 不适合作为配置文件使用。根据上面的要素来看,JSON 规则简单但不易读(尤其是复杂的没有格式化过的 JSON),不支持注释,也不能使用较为宽松的语法,严格的格式规范下逐行解析就别想了。

JSON 的确是在网页和 API 中传递信息的一种好格式,而且各种语言都有它的解析库,很多开源工具也喜欢使用JSON做为配置文件,但显然并不是很适合做配置文件。

 

格式

JSON格式是基于列表和字典的,同样是通过键值对来记录数据,而值可以使子级的列表或者字典对象,从而可以实现复杂的嵌套关系。

 

读写实例

Python可以使用json库读写JSON格式,也可以使用pickle库进行处理。

区别是使用json库可以在不同语言之间交换数据的,而pickle库只在python之间使用。json库只能把常用的数据类型序列化(列表、字典、列表、字符串、数字),比如日期格式、类对象json就不行了。而pickle可以序列化所有的数据类型,包括类,函数都可以序列化。

首先是一个简单的JSON文件。

{
  "name": "yumefx",
  "age": 3,
  "labels": [
    {
      "labelName": "赌狗",
      "labelID": 1
    },
    {
      "labelName": "铳王",
      "labelID": 2
    }
  ]
}

下面以json库为例,对JSON文件进行读写。

import json
import os


def readJson():
    '''读取配置文件'''
    with open("test.json", 'r') as load_f:
        load_dict = json.load(load_f) # 读取成Dict
    print(load_dict)

def writeJson():
    '''写入配置文件'''
    save_dict = {"name":""}
    with open("test.json", 'w') as save_f:
        json.dump(save_dict,save_f) # Dict写入
        
# Dict转str
print(json.dumps({"name":["yumefx", "rick", "morty"]}))
# str转Dict
print(json.loads('{"name":["yumefx", "rick", "morty"]}'))

 

XML

XML是可扩展标记语言,格式类似HTML,虽然是好的配置文件格式 ,但它编辑起来太麻烦。即使是有 XML Notepad 这类软件的存在,太多的尖括号和嵌套也让手写这种配置文件变得艰难且更容易出错。

但很多软件的配置文件依然是基于XML的,因此依然有着一定的使用频率。

 

格式

XML的格式是基于XML元素的,元素包括起始标签、属性、内容以及结束标签,元素之间可以并列或者嵌套。

在HTML中,某些标签可以省略结束标签,而在XML中所有元素必须有结束标签;XML必须有根标签;XML的属性值必须使用引号包裹;标签不能以数字或标点符号开头,并且不能包含空格。

 

读写实例

一个简单的XML文件内容如下。

<?xml version='1.0' encoding='utf-8'?>
<note>
    <!--你瞅啥-->
    <name language="ENU">yumefx</name>
    <status>sleeping</status>
</note>

在Python中常见的XML编程接口有DOM和SAX,这两种接口处理XML文件的方式不同,使用场合也不同。python有三种方法解析XML:SAX,DOM和ElementTree。

其中SAX用的是事件驱动模型,通过在解析XML过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。这是一种流式处理,一边读一边解析,占用内存少。适合读取大文件或者只读取部分内容的场景。

DOM则是在解析一个XML文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后利用DOM提供的不同函数来读取该文档的内容和结构,也可以把修改过的内容写入XML文件。考虑到性能,只适合读取一些简单的配置文件。

ElementTree则是一个轻量级的DOM,具有方便友好的API。代码可用性好,速度快,消耗内存少。

下面以ElementTree模式对XML文件进行读写。

from xml.etree import ElementTree as ET


def readXML():
    '''读取配置文件'''
    # 打开xml文档,读取内容为str
    str_xml = open('test.xml', 'r').read()

    # 将字符串解析成xml特殊对象,root代指xml文件的根节点
    root = ET.XML(str_xml)

    # 或者直接解析XML文件成xml对象
    # tree = ET.parse("test.xml")
    # root = tree.getroot()

    for child in root:
        print(child.tag, child.attrib)

def writeXML():
    '''写入配置文件'''
    root = ET.Element("famliy")

    son1 = ET.SubElement(root, "son", attrib={'name': 'child1'})
    son2 = ET.SubElement(root, "son", attrib={"name": "child2"})

    grandson1 = ET.SubElement(son1, "age", attrib={'name': 'grandChild11'})
    grandson1.text = 'morty'
    et = ET.ElementTree(root)

    et.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False)

 

那么,有没有比较完美的配置文件格式呢?

有的,那就是XML的进化版YAML,篇幅所限,且听下回分解。

 


爱任何事物的方法,
就是要意识到你可能会失去它。

——G.K.切斯特顿