Python正则表达式:异或运算符

4
假设我有以下字符串:
  1. "DT NN IN NN"
  2. "DT RB JJ NN"
  3. "DT JJ JJ NN"
  4. "DT RB RB NN NN"
  5. "DT RB RB"
因此,我有一系列字符串:
list = ["DT NN IN NN", "DT RB JJ NN", "DT JJ JJ NN", "DT RB RB NN NN", "DT RB RB"]

我有以下代码:
pattern = "(?:DT\s+)+([?:RB\s+|?:JJ\s+])+(?:NN\s+)*NN$"
for item in list:
    m = re.match(pattern, item)
    if m:
        print item

我希望从pattern中匹配以DT开头(出现一次或多次)且具有RBJJ(出现一次或多次),但不是两者都有,然后以NN结尾(同样出现一次或多次)的字符串。所以,在最终结果中,屏幕上应该打印出3和4。但是,使用我的正则表达式,我还得到了2,这不是我想要的。我该如何更改pattern才能使其工作?如何将管道(OR)替换为异或(XOR)?

1
那么 NN 可以重复任意次数吗? - Willem Van Onsem
在这种情况下,不使用正则表达式会更简单。 - Jakub Roztocil
是的,NN 可以重复多次,但必须以至少一个 NN 结尾。 - Belphegor
@Belphegor:NN可以与例如JJ交错使用吗? DT JJ NN JJ NN是一个有效的字符串吗? - Willem Van Onsem
@Belphegor:使用正则表达式进行自然语言模式处理? ;) - Willem Van Onsem
3个回答

2
问题在于您如何定义RBJJ的存在。您没有提到只有其中之一应该存在。这可以通过使用|(管道)将它们分开,并让它们中的任何一个重复一次或多次(+)来实现。尝试将模式更改为以下内容:
pattern = "(?:DT\s+)+(?:(RB\s+)+|(JJ\s+)+)(?:NN\s+)*NN$"

此外,(?:<something>) 被称为非捕获组。使用它来表示“我想匹配 <something>,但在选择分组时不包括它。”根据您的代码,似乎您没有使用任何分组。您只是打印整个 item(除非您为简洁起见屏蔽了代码)。如果您实际上不需要分组,则可以使用以下简单版本:
pattern = "(DT\s+)+((RB\s+)+|(JJ\s+)+)(NN\s*)*NN$"

我还允许结尾的空格出现零次或多次,而不是像原始模式一样出现一次或多次。随意更改。


1

[...] 是一个字符类,表示匹配一组包括 ?:+| 等字符的字符。除了用于标记序列的 - 外,在字符类中没有修饰符或特殊字符。

你需要匹配 RBJJ 的重复出现:

pattern = r"(?:DT\s+)+(?:(?:RB\s+)+|(?:JJ\s+)+)NN"

我将简化它; 无论如何,您都没有使用任何组。
此模式的在线演示:https://regex101.com/r/iH4lE6/1 由于您不依赖于捕获组,因此使用非捕获组也没有什么意义;只需使用(...)而不是(?:...)来使用更简洁的正则表达式。
您可能希望添加锚点以确保DT仅在开头匹配,并将结尾处的NN替换为(NN\s+)*NN$以将其锚定在末尾,并将空格匹配移动到每个重复组的开头。
pattern = r"^DT(\s+DT)*((\s+RB)+|(\s+JJ)+)(\s+NN)+$"

这个版本的在线演示: https://regex101.com/r/iH4lE6/2


根据 这条评论NN 可以重复任意次数。 - Willem Van Onsem
这对我不起作用,它会匹配像“DT JJ JJ NN JJ NN DT NN”这样的模式,而我不需要那个。它必须以DT开头,至少有一个(或多个)RB或JJ,然后以一个或多个NN结尾。 - Belphegor
@Belphegor:已更新;您能否更新您的问题,包括那个样本? - Martijn Pieters
@MartijnPieters 是的,现在这个可以工作了(有一个例外:DT可能会出现多次,我更新了问题)。无论如何,另一个答案更快,所以我接受了那个。感谢您的帮助+1! - Belphegor
1
@Belphegor:没错,已更新以匹配开头重复的DTs! - Martijn Pieters

1
如果我理解问题正确,您可以通过将其分成两个独立的问题来解决此问题:
  • A regex that starts with DT followed by one or more RBs followed by one or more NNs:

    ^DT(\s+DT)*(\s+RB)+(\s+NN)+$
    
  • A regex that starts with DT followed by one or more JJs followed by one or more NNs:

    ^DT(\s+DT)*(\s+JJ)+(\s+NN)+$
    
现在,您只需在这两个之间放置一个管道(或运算符)即可:
^((DT(\s+DT)*(\s+RB)+(\s+NN)+)|(DT(\s+DT)*(\s+JJ)+(\s+NN)+))$

并通过重构简化它为:

^DT(\s+DT)*((\s+RB)+|(\s+JJ)+)(\s+NN)+$

或者使用Regexper进行可视化呈现:

visual representation of the regex


谢谢您的评论,这个方法有效,但有一个例外:DT可以出现一次或多次(我刚才看到我忘了在原来的问题中提到这一点)。我接受了另一个答案,因为它可以解决我的问题而且那个人回复得更快 :) 感谢您详细的回答,确实很有帮助! - Belphegor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接