【问题标题】:How are nested groups addressed in RegEx?RegEx 中如何处理嵌套组?
【发布时间】:2017-07-20 14:38:02
【问题描述】:

假设我有这样的文字:

/TT0 1 Tf
0.002 Tc -0.002 Tw 11.04 0 0 11.04 221.16 707.04 Tm
[(\()-2(Y)7(o)7(u )-3(g)7(o)-2(t)4(i)-3(t)(\))]TJ
EMC 

它是 PDF 文件的一部分。线

[(\()-2(Y)7(o)7(u've )-3(g)7(o)-2(t)4(i)-3(t)(\))]TJ

包含文本“(你已经知道了)”。所以我首先需要匹配文本行

^[(.*)]TJ$

拥有那个捕获组,我可以申请 \(((.*?)\)[-0-9]*) 并将所有匹配项替换为 \2

是否可以一步完成?

【问题讨论】:

  • 在 Python 中无法使用 re。可以使用 regex 包,但 you don't want to do it 除非您别无选择,只能使用单一正则表达式。不过,我不确定regex 中是否有任何奇特的功能会有所帮助。
  • @nhahtdh:regex 模块拥有您最疯狂梦想的所有功能。
  • @nhahtdh 我明白了。您能否发布一个指向regex 模块文档的链接?
  • 那么捕获嵌套组的匹配是不寻常的吗?

标签: python regex


【解决方案1】:

通过正则表达式模块,您可以使用这种模式:

pat=r'(?:\G(?!\A)\)|\[(?=[^]]*]))[^](]*\(([^)\\]*(?:\\.[^)\\]*)*)(?:\)[^(]*]TJ)?'
regex.sub(pat, r'\1', s)

demo

图案细节:

(?: # two possible starts
    \G     # contiguous to a previous match
    (?!\A) # not at the start of the string
    \)     # a literal closing round bracket
  | # OR
    \[          # an opening square bracket
     (?=[^]]*]) # followed by a closing square bracket
)
[^](]* # all that isn't a closing square bracket or an opening round bracket
\(     # a literal opening round bracket
(      # capture group 1
    [^)\\]* # all characters except a closing round bracket or a backslash
    (?:\\.[^)\\]*)* # to deal with eventual escaped characters 
)
(?: \) [^(]* ] TJ )? # eventual end of the square bracket parts

【讨论】:

    【解决方案2】:

    使用正则表达式解析嵌套组可能很困难、难以辨认或无法实现。

    寻址嵌套组的一种方法是使用parsing grammar。这是 Eric Rose 使用 parsimonious 库的 3 步示例。

    给定

    import itertools as it
    
    import parsimonious as pars
    
    
    source  = """\
    /TT0 1 Tf
    0.002 Tc -0.002 Tw 11.04 0 0 11.04 221.16 707.04 Tm
    [(\()-2(Y)7(o)7(u )-3(g)7(o)-2(t )4(i)-3(t)(\))]TJ
    EMC"""
    

    代码

    1. 定义语法
    rules = r"""
    
        root            = line line message end
    
        line            = ANY NEWLINE
        message         = _ TEXT (_ TEXT*)* NEWLINE
        end             = "EMC" NEWLINE*
    
        TEXT            = ~r"[a-zA-Z ]+" 
        NEWLINE         = ~r"\n"
        ANY             = ~r"[^\n\r]*"
    
        _               = meaninglessness*
        meaninglessness = ~r"(TJ)*[^a-zA-Z\n\r]*"    
    
        """
    
    1. 解析源文本并构建 AST
    grammar = pars.grammar.Grammar(rules)
    tree = grammar.parse(source)
    # print(tree)
    
    1. 解决 AST
    
    class Translator(pars.NodeVisitor):
        
        def visit_root(self, node, children):
            return children
    
        def visit_line(self, node, children):
            return node.text
        
        def visit_message(self, node, children):
            _, s, remaining, nl = children
            return (s + "".join(it.chain.from_iterable(i[1] for i in remaining)) + nl)
            
        def visit_end(self, node, children):
            return node.text
        
        def visit_meaninglessness(self, node, children):
            return children
        
        def visit__(self, node, children):
            return children[0]
        
        def visit_(self, node, children):
            return children
        
        def visit_TEXT(self, node, children):
            return node.text
        
        def visit_NEWLINE(self, node, children):
            return node.text
        
        def visit_ANY(self, node, children):
            return node.text
    

    演示

    tr = Translator().visit(tree)
    print("".join(tr))
    

    输出

    /TT0 1 Tf
    0.002 Tc -0.002 Tw 11.04 0 0 11.04 221.16 707.04 Tm
    You got it
    EMC
    

    详情

    1. 我们定义了一组类似于 regex/EBNF 的语法规则 see docs for details,而不是一个死板的(有时难以辨认的正则表达式)。定义语法后,如果需要,调整起来会容易得多。
    • 注意:原始文本已被修改,在2(t)(第 3 行)中添加了一个空格,因为它被认为是 OP 中缺失的。
    1. 解析步骤很简单。只需parse 基于语法的源文本。如果语法定义充分,则会创建一个 AST,其中包含反映源结构的节点。拥有 AST 是关键,因为它使这种方法更加灵活。
    2. 定义访问每个节点时要执行的操作。可以使用任何所需的技术来解析 AST。例如,这里我们通过从parsmonious 继承NodeVisitor 来演示Visitor Pattern

    现在,对于您的 PDF 中遇到的新文本或意外文本,只需修改语法并再次解析即可。

    【讨论】:

      猜你喜欢
      • 2020-05-19
      • 2017-07-23
      • 1970-01-01
      • 2011-06-18
      • 1970-01-01
      • 2015-06-04
      • 2017-04-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多