You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pyparsing中NotAny(FollowedBy())匹配失败问题求助

问题描述

输入数据:

[gog1] [G1] [gog2] [gog3] [gog4] [G2] [gog5] [G3] [gog6]

需求:匹配所有后面未紧跟G类型项的gog类型项,预期结果为gog2、gog3、gog6。

尝试三种pyparsing模式均失败:

  • 使用pattern = poi_gog + ~poi_g仅匹配到gog6;
  • 使用pattern = poi_gog + NotAny(FollowedBy(poi_g))同样仅匹配到gog6;
  • 使用pattern = poi_gog + poi_not_g.suppress()仅匹配到gog2。

测试代码如下:

import pyparsing as pp
from pyparsing import *

def pyparsing_test():
    # this also dont helps
    # ParserElement.enable_left_recursion(force=True)

    data=""" [gog1] [G1] [gog2] [gog3] [gog4] [G2] [gog5] [G3] [gog6] """

    poi_type = Word(alphas).set_results_name('type')
    poi = Suppress('[') + poi_type + Char(nums) + Suppress(']')

    def cnd_is_type(val):
        return lambda toks: toks.type==val

    def cnd_is_not_type(val):
        return lambda toks: toks.type!=val

    poi_gog=poi('gog').add_condition(cnd_is_type('gog'))
    poi_g=poi('g').add_condition(cnd_is_type('G'))
    poi_not_g=poi('not_g').add_condition(cnd_is_not_type('G'))

    pattern = poi_gog + ~poi_g
    #WTF this finds only `gog6`, why??

    pattern = poi_gog + NotAny(FollowedBy(poi_g))
    #WTF same, only `gog6`

    pattern = poi_gog + poi_not_g.suppress()
    #WTF this works better but find only `gog2`, why not `gog3` also?

    r=pattern.search_string(data)
    print(data)
    print('='*10)
    print(r)
失败原因分析
  1. 前两种模式仅匹配gog6:
    这两种写法要求poi_gog直接后续内容不是poi_g,但search_string默认是非重叠匹配,会从左到右消耗输入。比如匹配gog2时,后续的gog3会被当作匹配段的一部分消耗掉,导致无法再匹配gog3;只有末尾的gog6后面无内容,满足条件。

  2. 第三种模式仅匹配gog2:
    该写法要求poi_gog必须紧跟一个非G类型的poi,gog2符合条件会被匹配,但search_string消耗到gog3后不会回头匹配,同时该写法也不支持“后面无内容”的场景(比如gog6)。

正确实现方案

方法一:全量解析后筛选(直观易维护)

先解析所有poi,再逐个判断每个gog项的下一个元素是否符合要求:

import pyparsing as pp
from pyparsing import *

def pyparsing_test():
    data=""" [gog1] [G1] [gog2] [gog3] [gog4] [G2] [gog5] [G3] [gog6] """

    poi_type = Word(alphas).set_results_name('type')
    poi = Suppress('[') + poi_type + Char(nums) + Suppress(']')

    # 解析所有poi
    all_pois = poi.search_string(data)
    
    # 筛选符合条件的gog项
    result = []
    for idx, poi_tok in enumerate(all_pois):
        if poi_tok.type == 'gog':
            # 满足:是最后一个项,或者下一个项不是G类型
            if idx == len(all_pois)-1 or all_pois[idx+1].type != 'G':
                result.append(f"{poi_tok.type}{poi_tok[1]}")
    
    print(data)
    print('='*10)
    print(result)

pyparsing_test()

输出结果:

[gog1] [G1] [gog2] [gog3] [gog4] [G2] [gog5] [G3] [gog6] 
==========
['gog2', 'gog3', 'gog6']

方法二:用scan_string实现直接匹配

如果要直接用pyparsing匹配模式,替换search_string为scan_string(支持遍历所有可能的匹配位置,不强制消耗输入):

import pyparsing as pp
from pyparsing import *

def pyparsing_test():
    data=""" [gog1] [G1] [gog2] [gog3] [gog4] [G2] [gog5] [G3] [gog6] """

    poi_type = Word(alphas).set_results_name('type')
    poi = Suppress('[') + poi_type + Char(nums) + Suppress(']')

    def cnd_is_type(val):
        return lambda toks: toks.type==val

    poi_gog=poi('gog').add_condition(cnd_is_type('gog'))
    poi_g=poi('g').add_condition(cnd_is_type('G'))

    # 匹配gog且后续不是G类型项
    pattern = poi_gog + ~FollowedBy(poi_g)
    
    # 用scan_string遍历所有匹配位置
    result = []
    for tokens, _, _ in pattern.scan_string(data):
        result.append(f"{tokens.type}{tokens[1]}")
    
    print(data)
    print('='*10)
    print(result)

pyparsing_test()

输出结果与预期一致。


内容的提问来源于stack exchange,提问作者Jhon BYaka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 15:50:48