【问题标题】:Converting part of string into variable name in python在python中将部分字符串转换为变量名
【发布时间】:2016-06-14 02:26:55
【问题描述】:

我有一个包含如下文本的文件:

loadbalancer {
upstream application1 {
server 127.0.0.1:8082;
server 127.0.0.1:8083;
server 127.0.0.1:8084;
}
upstream application2 {
server 127.0.0.1:8092;
server 127.0.0.1:8093;
server 127.0.0.1:8094;
}
}

有谁知道,我如何提取如下变量:

appList=["application1","application2"]
ServerOfapp1=["127.0.0.1:8082","127.0.0.1:8083","127.0.0.1:8084"]
ServerOfapp2=["127.0.0.1:8092","127.0.0.1:8093","127.0.0.1:8094"]

.
.
.

等等

【问题讨论】:

  • 发布到目前为止您尝试了什么...
  • 我想你可能也想要一本列表字典。 servers['application1'] = ["127.0.0.1:8082","127.0.0.1:8083","127.0.0.1:8084"] 还允许您根据 appList 的键索引哪组服务器
  • @IronFist ,我试图用正则表达式来处理它,但我找不到任何有效的形式
  • 我认为这是一个有效的形式,但有点不优雅:/\b(?:(?:25[0-5]|2[0-4][0-9]|[01 ]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9 ][0-9]?):[0-9]{1,5}/g
  • 我的瘦文件包含json 格式的配置。是吗?是否允许更改文件中的配置格式?

标签: python regex server


【解决方案1】:

我相信re也可以解决这个问题:

>>> import re
>>> from collections import defaultdict
>>>
>>> APP = r'\b(?P<APP>application\d+)\b'
>>> IP = r'server\s+(?P<IP>[\d\.:]+);' 
>>> 
>>> pat = re.compile('|'.join([APP, IP]))
>>> 
>>> 
>>> scan = pat.scanner(s)
>>> d = defaultdict(list)
>>> 
>>> for m in iter(scan.search, None):
        group = m.lastgroup
        if group == 'APP':
            keygroup = m.group(group)
            continue
        else:
            d[keygroup].append(m.group(group))


>>> d
defaultdict(<class 'list'>, {'application1': ['127.0.0.1:8082', '127.0.0.1:8083', '127.0.0.1:8084'], 'application2': ['127.0.0.1:8092', '127.0.0.1:8093', '127.0.0.1:8094']})

或类似地使用re.finditer 方法而没有pat.scanner

>>> for m in re.finditer(pat, s):
        group = m.lastgroup
        if group == 'APP':
            keygroup = m.group(group)
            continue
        else:
            d[keygroup].append(m.group(group))


>>> d
defaultdict(<class 'list'>, {'application1': ['127.0.0.1:8082', '127.0.0.1:8083', '127.0.0.1:8084'], 'application2': ['127.0.0.1:8092', '127.0.0.1:8093', '127.0.0.1:8094']})

【讨论】:

    【解决方案2】:

    如果您想要的行始终以上游和服务器开头,这应该可以:

    app_dic = {}
    with open('file.txt','r') as f:
        for line in f:
            if line.startswith('upstream'):
                app_i = line.split()[1]
                server_of_app_i = []
                for line in f:
                    if not line.startswith('server'):
                        break
                    server_of_app_i.append(line.split()[1][:-1])
                app_dic[app_i] = server_of_app_i
    

    app_dic 应该是一个列表字典:

    {'application1': ['127.0.0.1:8082', '127.0.0.1:8083', '127.0.0.1:8084'],
    'application2': ['127.0.0.1:8092', '127.0.0.1:8093', '127.0.0.1:8094']}
    

    编辑

    如果输入文件不包含任何换行符,只要文件不是太大,您可以将其写入列表并对其进行迭代:

    app_dic = {}
    with open('file.txt','r') as f:
       txt_iter = iter(f.read().split()) #iterator of list
    for word in txt_iter:
        if word == 'upstream':
            app_i = next(txt_iter)
            server_of_app_i=[]
            for word in txt_iter:
                if word == 'server':
                    server_of_app_i.append(next(txt_iter)[:-1])
                elif word == '}':
                    break
            app_dic[app_i] = server_of_app_i
    

    这更难看,因为必须搜索结束大括号才能打破。如果它变得更复杂,应该使用正则表达式。

    【讨论】:

    • 您的最终输出中包含了;
    • @MT 您的代码适用于每行末尾都有“\n”的配置文件,但是当我为自己的配置文件尝试它时,它不起作用...
    • @M.T 你能告诉我是哪一部分吗?
    • @MT,你应该原谅我,当我用记事本打开我的文件时,它只有一行,但是当在 pyCharm 或 notepad++ 中打开它时,它有一些行,现在我意识到为什么你的代码,不适用于我的文件,原因在我的配置文件中,只是第一行以字符开头,其他有制表符和空格。
    【解决方案3】:

    如果您能够使用 Matthew Barnettnewer regex module,则可以使用以下解决方案,请参阅 additional demo on regex101.com

    import regex as re
    
    rx = re.compile(r"""
        (?:(?P<application>application\d)\s{\n| # "application" + digit + { + newline
        (?!\A)\G\n)                             # assert that the next match starts here
        server\s                                # match "server"
        (?P<server>[\d.:]+);                    # followed by digits, . and :
        """, re.VERBOSE)
    
    string = """
    loadbalancer {
    upstream application1 {
    server 127.0.0.1:8082;
    server 127.0.0.1:8083;
    server 127.0.0.1:8084;
    }
    upstream application2 {
    server 127.0.0.1:8092;
    server 127.0.0.1:8093;
    server 127.0.0.1:8094;
    }
    }
    """
    
    result = {}
    for match in rx.finditer(string):
        if match.group('application'):
            current = match.group('application')
            result[current] = list()
        if current:
            result[current].append(match.group('server'))
    
    print result
    # {'application2': ['127.0.0.1:8092', '127.0.0.1:8093', '127.0.0.1:8094'], 'application1': ['127.0.0.1:8082', '127.0.0.1:8083', '127.0.0.1:8084']}
    

    这利用了\G 修饰符、命名的捕获组和一些编程逻辑。

    【讨论】:

      【解决方案4】:

      这是基本方法:

      # each of your objects here
      objText = "xyz xcyz 244.233.233.2:123"
      listOfAll = re.findall(r"/\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]{1,5}/g", objText)
      
      for eachMatch in listOfAll:
          print "Here's one!" % eachMatch
      

      显然,这有点粗糙,但它会对给出的任何字符串执行全面的正则表达式搜索。可能更好的解决方案是将对象本身传递给它,但现在我不确定你会有什么作为原始输入。不过,我会尝试改进正则表达式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-08-11
        • 2013-10-07
        • 1970-01-01
        • 1970-01-01
        • 2011-08-27
        • 2010-12-04
        • 1970-01-01
        相关资源
        最近更新 更多