【问题标题】:Parsing text files with "magic" values用“魔法”值解析文本文件
【发布时间】:2018-01-31 18:35:01
【问题描述】:

背景

我有一些用于音频调整的自动化脚本中的大文本文件。文本文件中的每一行大致如下:

A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]] BANANA # BANANA

文本被输入一个旧的命令行程序,该程序搜索关键字并将它们换出。示例输出为:

A[0] + B[100] - C[0x1000] [[0]] 0 # 0
A[2] + B[200] - C[0x100A] [[2]] 0 # 0

问题

有时,文本文件中的关键字应该保持不变(即我们不希望用“BANANA”代替的情况)。我想修改文本文件以使用在正常情况下不太可能弹出的某种关键字/分隔符,即:

A[#1] + B[#2] - C[#3] [[#1]] #1 # #1

问题

python 的文本文件解析器是否有任何特殊的索引/转义序列可以用来代替简单的关键字?

【问题讨论】:

  • 期望的输出是什么?
  • 你不是说所有的输入行都是这样的吗?你有输入行的语法吗?
  • 这是一个奇怪的帖子,几乎令人困惑。这是什么,比如邮件合并?需要换出的简单键/值对?这很简单,为什么让我感到困惑?
  • 如果你不想用“BANANA”代替,那你为什么要在#1中代替呢?

标签: python regex python-2.7 parsing python-2.x


【解决方案1】:

在字典中使用正则表达式替换函数

匹配括号之间的所有内容(非贪婪,避免括号本身)并替换为dict的值,如果找不到则输入原始值:

import re

d = {"BANANA":"12", "PINEAPPLE":"20","CHERRY":"100","BANANA":"400"}
s = "A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]"

print(re.sub("\[([^\[\]]*)\]",lambda m : "[{}]".format(d.get(m.group(1),m.group(1))),s))

打印:

A[400] + B[20] - C[100] [[400]]

【讨论】:

    【解决方案2】:

    您可以使用re.sub 来执行替换。这个答案创建了一个随机值列表来演示,但是,该列表可以替换为您正在使用的数据:

    import re
    import random
    s = "A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]"
    new_s = re.sub('(?<=\[)[a-zA-Z0-9]+(?=\])', '{}', s)
    random_data = [[random.randint(1, 2000) for i in range(4)] for _ in range(10)]
    final_results = [new_s.format(*i) for i in random_data]
    for command in final_results:
      print(command)
    

    输出:

    A[51] + B[134] - C[864] [[1344]]
    A[468] + B[1761] - C[1132] [[1927]]
    A[1236] + B[34] - C[494] [[1009]]
    A[1330] + B[1002] - C[1751] [[1813]]
    A[936] + B[567] - C[393] [[560]]
    A[1926] + B[936] - C[906] [[1596]]
    A[1532] + B[1881] - C[871] [[1766]]
    A[506] + B[1505] - C[1096] [[491]]
    A[290] + B[1841] - C[664] [[38]]
    A[1552] + B[501] - C[500] [[373]]
    

    【讨论】:

    • Lookarounds 更加“昂贵” - 而是匹配括号并将它们再次放入替换中。参见mineyour's,27 与 81 步,三分之一。
    【解决方案3】:

    我认为使用plex 更容易——也更清晰。问题是它似乎仅适用于 Py2。我花了一两个小时才完成到 Py3 的足够转换工作。

    只需注意三种类型的标记,然后在 while 语句中出现相似数量的分支。

    from plex import *
    from io import StringIO
    
    stmt = StringIO('A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]')
    
    lexicon = Lexicon([
        (Rep1(AnyBut('[]')), 'not_brackets'),
        (Str('['), 'left_bracket'),
        (Str(']'), 'right_bracket'),
    ])
    
    class Replace(dict):
        def __missing__(self, key):
            return key
    
    replace = Replace({'BANANA': '1', 'PINEAPPLE': '2'})
    
    scanner = Scanner(lexicon, stmt)
    new_statement = []
    while True:
        token = scanner.read()
        if token[0] is None:
            break
        elif token[0]=='no_brackets':
            new_statement.append(replace[token[1]])
        else:
            new_statement.append(token[1])
    
    print (''.join(new_statement))
    

    结果:

    A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]
    

    【讨论】:

      【解决方案4】:

      随便用

      \[([^][]+)\]
      

      并将其替换为所需的结果,例如123


      崩溃了,这说
      \[       # opening bracket
      ([^][]+) # capture anything not brackets, 1+ times
      \]       # closing bracket
      

      a demo on regex101.com


      对于您更改的要求,您可以使用OrderedDict
      import re
      from collections import OrderedDict
      
      rx = re.compile(r'\[([^][]+)\]')
      d = OrderedDict()
      
      def replacer(match):
          item = match.group(1)
          d[item] = 1
          return '[#{}]'.format(list(d.keys()).index(item) + 1)
      
      string = "A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]"
      string = rx.sub(replacer, string)
      print(string)
      

      产量

      A[#1] + B[#2] - C[#3] [[#1]]
      

      这里的想法是将每个(可能)新项目放入字典中,然后搜索索引。 OrderedDicts 记住订单条目。


      为了学术完整性,您也可以自己完成所有操作:
      import re
      
      class Replacer:
          rx = re.compile(r'\[([^][]+)\]')
          keywords = []
      
          def do_replace(self, match):
              idx = self.lookup(match.group(1))
              return '[#{}]'.format(idx + 1)
      
          def replace(self, string):
              return self.rx.sub(self.do_replace, string)
      
          def lookup(self, item):
              for idx, key in enumerate(self.keywords):
                  if key == item:
                      return idx
      
              self.keywords.append(item)
              return len(self.keywords)-1
      
      string = "A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]"
      
      rpl = Replacer()
      string = rpl.replace(string)
      print(string)
      

      【讨论】:

        【解决方案5】:

        也可以使用pyparsing 来完成。

        此解析器本质上将noun 定义为方括号内的大写字母,然后将它们的序列定义为一行输入,如complete

        要替换用其他东西标识的项目,请以合适的方式定义从dict 派生的class,以便class 中没有的任何内容保持不变。

        >>> import pyparsing as pp
        >>> noun = pp.Word(pp.alphas.upper())
        >>> between = pp.CharsNotIn('[]')
        >>> leftbrackets = pp.OneOrMore('[')
        >>> rightbrackets = pp.OneOrMore(']')
        >>> stmt = 'A[BANANA] + B[PINEAPPLE] - C[CHERRY] [[BANANA]]'
        >>> one = between + leftbrackets + noun + rightbrackets
        >>> complete = pp.OneOrMore(one)
        >>> complete.parseString(stmt)
        (['A', '[', 'BANANA', ']', ' + B', '[', 'PINEAPPLE', ']', ' - C', '[', 'CHERRY', ']', ' ', '[', '[', 'BANANA', ']', ']'], {})
        >>> class Replace(dict):
        ...     def __missing__(self, key):
        ...         return key
        ...     
        >>> replace = Replace({'BANANA': '1', 'PINEAPPLE': '2'})
        >>> new = []
        >>> for item in complete.parseString(stmt).asList():
        ...     new.append(replace[item])
        ... 
        >>> ''.join(new)
        'A[1] + B[2] - C[CHERRY] [[1]]'
        

        【讨论】:

          猜你喜欢
          • 2017-05-08
          • 2014-03-05
          • 2011-11-20
          • 1970-01-01
          • 2012-02-25
          • 2023-02-18
          • 2015-04-27
          • 1970-01-01
          相关资源
          最近更新 更多