需要逻辑来通过读取输入文件来创建dict的dict

时间:2016-01-20 11:30:06

标签: python python-3.x

我有一个包含以下行的配置文件:

router1 = { 
   hostname: abcd 
      interfaces:{ 
          interface: gigabit 0/1 
          valn: 100 
          name: vlan1
          ip_address: 1.1.1.1 
       } 
   clear: clear config all          
  } 

我的脚本应该从配置文件中读取并以相同的格式创建字典字典。根据这本词典,我可以继续进行自动化。

3 个答案:

答案 0 :(得分:2)

要解决您的问题,您需要parser。可以使用各种库在Python中进行解析,请参阅SO退伍军人Ned Batchelder的Python parsing tools以获取可用的列表。

但是,您的数据格式并不复杂,因此编写一个不依赖于任何第三方模块的简单解析器非常容易。要将数据分解为单个令牌(又名lexical analysis),我们可以使用标准shlex模块。

下面的代码实现了一个非常简单的递归式解析器。它被开发出来了在Python 2.6.6上进行了测试,但它应该在Python 3上正常运行。您可能希望通过将其放入类中来封装它。恕我直言,这可能不是真的必要,但我想这取决于你的实际用例。

此代码使用json模块打印已解析的字典;这并不是绝对必要的,但它确实可以很容易地打印出嵌套的词典。

代码包含一些错误检查,但很容易被欺骗接受奇怪的数据,因此如果您不能保证输入数据始终正确,您可能希望增强错误检查。

#!/usr/bin/env python

''' Parse config file data; see below for example data format
    See http://stackoverflow.com/q/34898816/4014959
    Written by PM 2Ring 2016.01.21
'''

from __future__ import print_function
import shlex
import string
import json

data = '''
router1 = {
    hostname: abcd
        interfaces:{
            interface: gigabit 0/1
            valn: 100
            name: vlan1
            ip_address: 1.1.1.1
        }
    clear: clear config all
}
'''

#Set up a simple lexer. `data` must be a file-/stream-like object
# with read() and readline() methods, or a string 
lex = shlex.shlex(data)
lex.wordchars = string.ascii_letters + string.digits + "./:_"

def t_is(ch):
    ''' verify that the next token is ch '''
    token = next(lex)
    if token != ch:
        raise ValueError('Line %d: Expected %r got %r' 
            % (lex.lineno, ch, token))

def get_word():
    ''' get next token if it's a word.
        Otherwise, push it back & return None
    '''
    token = next(lex)
    if token not in '{}':
        return token
    lex.push_token(token)

def is_key(token):
    return token[-1] == ':'

def get_value():
    ''' get value, which may be a list of words or a dict '''
    token = next(lex)
    if token == '{':
        #Value is a dict
        lex.push_token(token)
        return get_dict()

    #Value consists of one or more non-key words
    value = [token]
    while True:
        token = get_word()
        if token is None:
            break
        if is_key(token):
            lex.push_token(token)
            break
        value.append(token)
    return ' '.join(value)

def get_dict():
    ''' parse a dictionary '''
    t_is('{')
    d = {}
    while True:
        #get key, value pairs
        key = get_word()
        if key is None:
            t_is('}')
            return d
        if not is_key(key):
            raise ValueError('Line %d: Bad key %r' 
                % (lex.lineno, key))
        d[key[:-1]] = get_value()

def get_cfg():
    ''' parse config data, returning the name and the dict '''
    name = get_word()
    if name is None:
        raise ValueError('Line %d: Expected name, got %r' 
            % (lex.lineno, next(lex)))
    t_is('=')
    d = get_dict()
    return name, d

#----------------------------------------------------------

print(data)
print(20 * '- ' + '\n')
#for token in lex: print(token)

name, cfg = get_cfg()
print(name)
print(json.dumps(cfg, indent=4, sort_keys=True))

输出

router1 = {
    hostname: abcd
        interfaces:{
            interface: gigabit 0/1
            valn: 100
            name: vlan1
            ip_address: 1.1.1.1
        }
    clear: clear config all
}

- - - - - - - - - - - - - - - - - - - - 

router1
{
    "clear": "clear config all", 
    "hostname": "abcd", 
    "interfaces": {
        "interface": "gigabit 0/1", 
        "ip_address": "1.1.1.1", 
        "name": "vlan1", 
        "valn": "100"
    }
}

答案 1 :(得分:0)

我不确定你为什么要这样做,但我可以建议使用JSON吗?

试试这个,将你的文档(字典)保存为有效的.json文件,然后在python中执行此操作

import json
with open("filename.json",'r') as f:
    data=json.load(f)
print(data)

数据变量应具有与存储相同的数据结构。如果你想要更复杂的东西,如保存对象,请尝试pickle

https://docs.python.org/3/library/pickle.html

答案 2 :(得分:0)

此pyparsing代码会将您的配置条目解析为您可以像dict或对象一样使用的映射:

from pyparsing import *

LBRACE,RBRACE,LBRACK,RBRACK,COLON,EQ = map(Suppress, "{}[]:=")
NL = LineEnd()

key = Word(alphas, alphanums+'_')
# forward declare value, since this will be a recursive definition
value = Forward()
key_value = Group(key + COLON + value)

# parse actions will do string->int and string->float conversions at parse time
integer = Regex(r'[+-]?\d+').setParseAction(lambda t: int(t[0]))
real = Regex(r'[+-]?\d+\.\d*').setParseAction(lambda t: float(t[0]))
string = restOfLine.setParseAction(lambda t: t[0].strip())

dictvalue = Group(LBRACE + Dict(ZeroOrMore(key_value)) + RBRACE)
listvalue = Group(LBRACK + Dict(ZeroOrMore(value)) + RBRACK)

# assign recursive contents to value using <<=
value <<= (real | integer) + FollowedBy(NL) | dictvalue | listvalue | string

setting = Group(key + EQ + value)

config_parser = Dict(OneOrMore(setting))

config = config_parser.parseString(data)

# dump out parsed contents
print config.dump()

# access parsed contents like a dict or an object
print config.keys()
print config.router1.keys()
print config.router1.hostname

打印:

[['router1', [['hostname', 'abcd '], ['interfaces', [['interface', ...
- router1: [['hostname', 'abcd '], ['interfaces', [['interface', ...
  - clear: clear config all          
  - hostname: abcd 
  - interfaces: [['interface', 'gigabit 0/1 '], ['valn', 100], ...
    - interface: gigabit 0/1 
    - ip_address: 1.1.1.1 
    - name: vlan1
    - valn: 100
['router1']
['interfaces', 'hostname', 'clear']
abcd 

请注意,作为解析过程的一部分,那些有效浮点数或整数的值将已从字符串转换。