Python-读写Conf配置文件.md

Python 读写 Conf 配置文件

tags: Python ConfigParser 配置 conf ini yaml properties 2019 年 11 月

环境说明: Python2.7.11 CentOS7.6

TODO 不同种类配置文件对比

.yaml

yaml 说明介绍

YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便。

YAML 在 python 语言中有 PyYAML 安装包。

YAML 语言(发音 /ˈjæməl/ )的设计目标,就是方便人类读写。它实质上是一种通用的数据串行化格式。

yaml 语法规则

它的基本语法规则如下:
1、大小写敏感
2、使用缩进表示层级关系
3、缩进时不允许使用 Tab 键,只允许使用空格。
4、缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
5、# 表示注释,从这个字符一直到行尾,都会被解析器忽略,这个和 python 的注释一样

YAML 支持的数据结构有三种:
1、对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
2、数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
3、纯量(scalars):单个的、不可再分的值。字符串、布尔值、整数、浮点数、Null、时间、日期

yaml 文件样例

channelizer: org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer
graphs:
  {
    hugegraph: conf/hugegraph.properties,
    hugegraph1: conf/hugegraph1.properties,
    hugegraph2: conf/hugegraph2.properties,
    test01: conf/hugegraphtest01.properties,
  }
maxAccumulationBufferComponents: 1024
maxChunkSize: 8192
maxContentLength: 65536
maxHeaderSize: 8192
maxInitialLineLength: 4096
metrics:
  consoleReporter: { enabled: false, interval: 180000 }
  csvReporter:
    {
      enabled: true,
      fileName: /tmp/gremlin-server-metrics.csv,
      interval: 180000,
    }
  gangliaReporter:
    { addressingMode: MULTICAST, enabled: false, interval: 180000 }
  graphiteReporter: { enabled: false, interval: 180000 }
  jmxReporter: { enabled: false }
  slf4jReporter: { enabled: false, interval: 180000 }
plugins: [com.baidu.hugegraph]

yaml 参考链接

.ini 文件

ini 说明介绍

Python3 官方 ConfigParser该模块提供了实现基本配置语言的类,该类提供的结构类似于 Microsoft Windows INI 文件中的结构。可以使用它来编写可由最终用户轻松定制的 Python 程序。

ini 语法规则

ConfigParser 的一些问题:

  • 不能区分大小写。
  • 重新写入的配置文件不能保留原有配置文件的注释。
  • 重新写入的配置文件不能保持原有的顺序。
  • 不支持嵌套。
  • 不支持格式校验。
  • 易用性

注意事项

  • 配置参数读出来都是字符串类型, 参数运算时,注意类型转换,另外,对于字符型参数,不需要加""
  • 只要注意配置文件的参数尽量使用小写/大写,统一即可

ini 常用函数

读取配置文件

  • read(filename) 直接读取 ini 文件内容
  • sections() 得到所有的 section,并以列表的形式返回
  • options(section) 得到该 section 的所有 option
  • items(section) 得到该 section 的所有键值对
  • get(section,option) 得到 section 中 option 的值,返回为 string 类型
  • getint(section,option) 得到 section 中 option 的值,返回为 int 类型
  • getfloat(section,option)得到 section 中 option 的值,返回为 float 类型
  • getboolean(section, option)得到 section 中 option 的值,返回为 boolean 类型

写入配置文件

  • add_section(section) 添加一个新的 section
  • has_section(section) 判断是否有 section
  • set(section, option, value) 对 section 中的 option 进行设置
  • remove_setion(section)删除一个 section
  • remove_option(section, option)删除 section 中的 option
  • write(fileobject)将内容写入配置文件。

配置文件类型问题

  • getint(section,option) 返回 int 类型
  • getfloat(section, option) 返回 float 类型
  • getboolean(section,option) 返回 boolen 类型

ini 文件样例

[user] # section
username = tom # key = val 或 key: val
password = ***
email = test@host.com

[book]
bookname = python
bookprice = 25

ini 参考链接

.properties 文件

Python 中正好没有解析 properties 文件的现成模块,所以单独编写了一个脚本用于读写 *.properties 文件

properties 文件样例

restserver.url=http://0.0.0.0:8080

# graphs list with pair NAME:CONF_PATH
graphs=[test01:conf/hugegraphtest01.properties,hugegraph:conf/hugegraph.properties,hugegraph1:conf/hugegraph1.properties,hugegraph2:conf/hugegraph2.properties]

# authentication
#auth.require_authentication=
#auth.admin_token=
#auth.user_tokens=[]

properties 参考链接

附件

读写 .ini/.yaml 文件 完整代码


# -* - coding: UTF-8 -* -
u""" Python 读写 配置文件

逻辑说明:
- read_config           读取配置文件入口函数
    - read_config_ini
    - read_config_yaml
- write_config          写入配置文件入口函数
    - write_config_ini
    - write_config_yaml
- 函数配置调用
    - 根据 postfix_func_dict 指定文件后缀调用函数
    - 单独指定读取某类文件时,直接传入参数 filename_postfix 即可


支持以下配置文件读写
- *.ini ConfigParser
- *.yaml yaml TODO


语法等说明
- ConfigParser
- yaml

# 配置文件使用样例 ConfigParser
https://www.cnblogs.com/klb561/p/10085328.html


# *.yaml pyyaml
pip  install pyyaml
"""

import os
import ConfigParser
import sys
import traceback
import logging

import yaml

reload(sys)
sys.setdefaultencoding("utf-8")

# 指定 不同后缀调用不同方法
postfix_func_dict = {
    '.ini': 'ini',
    '.yaml': 'yaml',
}
# 默认配置后缀
default_filename_postfix = '.ini'

ini_config_data = [
    {'section': 'scetionA', 'section_vals': [
        {'key': '', 'val': '', 'dtype': ''},
        {'key': '', 'val': '', 'dtype': ''},
    ]}
]

ini_config_data = {
    'sectionA': {
        'key1': 'val1',
        'key2': 'val2',
    },
    'sectionB': {
        'key11': 'val11',
        'key21': 'val21',
    },
}

from collections import OrderedDict


def read_config(config_path, filename_postfix=None):
    u""" 读取配置文件

    :param str config_path: 配置文件路径
    :param str filename_postfix: 配置文件类型 ini / yaml
    """
    config_data = OrderedDict(dict())
    if not config_path or not os.path.exists(config_path):
        logging.error("配置文件[%s]为空或不存在", config_path)
        return config_data

    filename_postfix = filename_postfix if filename_postfix else os.path.splitext(config_path)[1]
    # TODO 动态 根据字符串 调用函数
    config_data = globals().get('read_config_%s' % postfix_func_dict.get(filename_postfix, default_filename_postfix))(
        config_path)

    logging.info("读取配置文件[%s]成功,配置信息[%s]", config_path, config_data)
    return config_data


def read_config_yaml(config_path):
    u""" 读取配置文件

    :param str config_path: 配置文件路径

    :return: dict config_data
    """
    # 加上 ,encoding='utf-8',处理配置文件中含中文出现乱码的情况。
    config_data = OrderedDict(dict())
    try:
        # f = open(config_path, 'r', encoding='utf-8')
        f = open(config_path, 'r')
        config = f.read()
        if float(yaml.__version__) <= 5.1:
            config_data = yaml.load(config)
        else:
            # 5.1版本后 使用 FullLoader 更加安全
            config_data = yaml.load(config, Loader=yaml.FullLoader)
    except Exception as e:
        logging.error(traceback.format_exc())
        logging.error("配置文件[%s]无法正常解析,请检查!", config_path)
    return config_data


def read_config_ini(config_path):
    u""" 读取配置文件

    :param str config_path: 配置文件路径

    :return: dict config_data
    """
    config_data = OrderedDict(dict())
    if not config_path or not os.path.exists(config_path):
        logging.error("配置文件[%s]为空或不存在", config_path)
        return config_data

    try:
        config = ConfigParser.ConfigParser()
        config.readfp(open(r'%s' % config_path))
        for section in config.sections():
            config_data[section] = OrderedDict(dict())
            for key, val in config.items(section):
                config_data[section][key] = val
    except Exception as e:
        logging.error(traceback.format_exc())
        logging.error("配置文件[%s]无法正常解析,请检查!", config_path)
    return config_data


def write_config(config_path, config_data, filename_postfix=None, mode='a', funcname=None):
    u""" 写入配置文件

    :param str config_path: 配置文件
    :param dict config_data: 配置字典
    :param str filename_postfix: 配置文件类型 ini / yaml . 为空时自动读取文件名称后缀,根据不同后缀调用不同函数
    :param str mode: 数据时 追加写入还是覆盖等 a w
    """

    filename_postfix = filename_postfix if filename_postfix else os.path.splitext(config_path)[1]
    mode = mode if mode and mode in ['a', 'w'] else 'a'

    # TODO 动态 根据字符串 调用函数
    config_data = globals().get('write_config_%s' % postfix_func_dict.get(filename_postfix, default_filename_postfix)) \
        (config_path, config_data, mode)

    logging.info("读取配置文件[%s]成功,配置信息[%s]", config_path, config_data)


def write_config_yaml(config_path, config_data, mode):
    u""" 写入配置文件

    :param str config_path: 配置文件
    :param dict config_data: 配置字典
    :param str mode: 数据时 追加写入还是覆盖等 a w
    """
    # fw = open(yamlPath, 'a', encoding='utf-8')
    fw = open(config_path, mode)  # a 追加写入,w,覆盖写入
    yaml.dump(config_data, fw)
    return config_data


def write_config_ini(config_path, config_data, mode):
    u""" 写入配置文件

    :param str config_path: 配置文件
    :param dict config_data: 配置字典
    :param str mode: 数据时 追加写入还是覆盖等 a w
    """

    config = ConfigParser.ConfigParser()
    if not os.path.exists(config_path):
        new_config_dic = config_data
    else:
        new_config_dic = read_config(config_path)
        # 当配置文件已经存在时, 将会使用新的dic更新原有配置
        if mode == 'a':
            new_config_dic.update(config_data)

    for section, section_vals in config_data.items():
        config.add_section(section)
        for key, val in section_vals.items():
            config.set(section, key, val)
    config.write(open(config_path, "w"))
    logging.info("写入配置文件[%s]完成", config_path)
    return config_data


if __name__ == '__main__':
    # yaml
    config_path = "test.yaml"
    config_path = "/home/fdm/software/hugegraph/hugegraph-0.9.2/conf/gremlin-server.yaml"
    config_data = read_config(config_path)
    write_config('test2.yaml', config_data=config_data, mode='a')
    exit()

    # ini
    config_path = "config.ini"
    config_data = {
        'sectionA': {'a': 'b', 'key1': 123}
    }
    write_config('config2.ini', config_data=config_data, mode='a')
    read_config(config_path)

读写 .properties 文件 完整代码

#! -*- coding:utf-8
u""" Config
    读写 *.properties 文件
https://www.cnblogs.com/momoyan/p/9145531.html
"""
import re
import os
import tempfile
from collections import OrderedDict


class Properties:

    def __init__(self, file_name):
        self.file_name = file_name
        self.properties = OrderedDict({})
        try:
            fopen = open(self.file_name, 'r')
            for line in fopen:
                line = line.strip()
                if line.find('=') > 0 and not line.startswith('#'):
                    strs = line.split('=')
                    self.properties[strs[0].strip()] = strs[1].strip()
        except Exception, e:
            raise e
        else:
            fopen.close()

    def has_key(self, key):
        return key in self.properties

    def get(self, key, default_value=''):
        if key in self.properties:
            return self.properties[key]
        return default_value

    def put(self, key, value):
        self.properties[key] = value
        replace_property(self.file_name, key + '=.*', key + '=' + value, True)


def parse(file_name):
    return Properties(file_name)


def replace_property(file_name, from_regex, to_str, append_on_not_exists=True):
    tmpfile = tempfile.TemporaryFile()

    if os.path.exists(file_name):
        r_open = open(file_name, 'r')
        pattern = re.compile(r'' + from_regex)
        found = None
        for line in r_open:
            if pattern.search(line) and not line.strip().startswith('#'):
                found = True
                line = re.sub(from_regex, to_str, line)
            tmpfile.write(line)
        if not found and append_on_not_exists:
            tmpfile.write('\n' + to_str)
        r_open.close()
        tmpfile.seek(0)

        content = tmpfile.read()

        if os.path.exists(file_name):
            os.remove(file_name)

        w_open = open(file_name, 'w')
        w_open.write(content)
        w_open.close()

        tmpfile.close()
    else:
        print "file %s not found" % file_name



if __name__ == '__main__':
    file_path = 'xxx.properties'
    props = parse(file_path)   #读取文件
    props.put('key_a', 'value_a')       #修改/添加key=value
    print props.get('key_a')            #根据key读取value
    print "props.has_key('key_a')=" + str(props.has_key('key_a'))   #判断是否包含该key
    print props.properties()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容