Python中的正则表达式嵌套括号

5
我有一个类似这样的东西:
Othername California (2000) (T) (S) (ok) {state (#2.1)}

有没有正则表达式代码可以获得以下内容:
Othername California ok 2.1

即,我希望保留大括号内的圆括号中的数字,并保留在其中的“ok”文本。如果我的行中包含该字符串“ok”,我需要特别打印出它,但我想要摆脱括号内的其他文本,例如(V),(S)或(2002)。
我知道使用正则表达式可能不是处理此类问题的最有效方法。
任何帮助都将不胜感激。
编辑:
由于某些信息不可用而未包含在该行中,因此该字符串可能会有所变化。 文本本身也是可变的(例如,我没有每行的“state”)。 因此,例如,可以有:
Name1 Name2 Name3 (2000) (ok) {edu (#1.1)}
Name1 Name2 (2002) {edu (#1.1)}
Name1 Name2 Name3 (2000) (V) {variation (#4.12)}

数据的顺序严格吗?例如:“某个状态(年份)(。)(。)(好吗?){状态(#数字)}”?如果是这种情况,我认为您需要使用拆分函数:http://www.pythonforbeginners.com/python-strings/python-split/ - fodma1
不,实际上这可能因行而异,仅在可用时包括信息。 - user2447387
必须转义正则表达式字符。字符(){}必须用“\”进行转义,例如:{。 在网址http://www.gskinner.com/RegExr/中进行测试。 - A. M. Mérida
真正的挑战在于匹配 2.1,如果我们想考虑它的多个实例,例如 {state (#2.1) yellow (33)},那么这将会更加困难。这种情况的问题在于:你“理论上”有两种解决方法:1) 向前和向后查找是否有 {},但问题在于大多数正则表达式(包括 Python)中的向后查找必须是固定长度的 2) 使用子组匹配,类似于 \{(?:.*?\((\w+)\).*?)+\},但这在大多数正则表达式中都不可用。因此,我认为仅凭正则表达式的力量,你的任务是不可能完成的。 - HamZa
你能发布更多可能输入的例子吗?不清楚字符串的哪些部分保持不变,哪些可能会变化。 - georg
@thg435 抱歉..我刚刚编辑了问题。不幸的是,大部分行可能会有所不同。我想唯一区分事物的方法就是看括号。 - user2447387
4个回答

8

正则表达式

(.+)\s+\(\d+\).+?(?:\(([^)]{2,})\)\s+(?={))?\{.+\(#(\d+\.\d+)\)\}

正则表达式图像

用于测试的文本

Name1 Name2 Name3 (2000) {Education (#3.2)}
Name1 Name2 Name3 (2000) (ok) {edu (#1.1)}
Name1 Name2 (2002) {edu (#1.1)}
Name1 Name2 Name3 (2000) (V) {variation (#4.12)}
Othername California (2000) (T) (S) (ok) {state (#2.1)}

测试

>>> regex = re.compile("(.+)\s+\(\d+\).+?(?:\(([^)]{2,})\)\s+(?={))?\{.+\(#(\d+\.\d+)\)\}")
>>> r = regex.search(文本)
>>> r
<_sre.SRE_Match object at 0x54e2105f36c16a48>
>>> regex.match(文本)
<_sre.SRE_Match object at 0x54e2105f36c169e8>
# 运行 findall >>> regex.findall(文本) [ (u'Name1 Name2 Name3' , u'' , u'3.2'), (u'Name1 Name2 Name3' , u'ok', u'1.1'), (u'Name1 Name2' , u'' , u'1.1'), (u'Name1 Name2 Name3' , u'' , u'4.12'), (u'Othername California', u'ok', u'2.1') ]

很酷。你是如何生成正则表达式图的? - phimuemue
不幸的是,它不能在我所有的文本行上工作并且会出现错误。我猜问题在于文本字符串一直在变化。例如,可能会有其他单词代替“state”,也可能有多个单词代替它。唯一的重复模式是括号的存在。 - user2447387
@phimuemue 我使用了http://www.debuggex.com/。该网站有一个选项,可以在SO上嵌入任何正则表达式。 - Stephan
@user2447387,尝试将stats\s+替换为.+ - Stephan
抱歉,再问一句。如果我需要捕获括号内的年份字符串,例如2000年,怎么办?谢谢。 - user2447387
显示剩余2条评论

2
尝试这个:
import re

thestr = 'Othername California (2000) (T) (S) (ok) {state (#2.1)}'

regex = r'''
    ([^(]*)             # match anything but a (
    \                   # a space
    (?:                 # non capturing parentheses
        \([^(]*\)       # parentheses
        \               # a space
    ){3}                # three times
    \(([^(]*)\)         # capture fourth parentheses contents
    \                   # a space
    {                   # opening {
        [^}]*           # anything but }
        \(\#            # opening ( followed by #
            ([^)]*)     # match anything but )
        \)              # closing )
    }                   # closing }
'''

match = re.match(regex, thestr, re.X)

print match.groups()

输出:

('Othername California', 'ok', '2.1')

以下是压缩版本:

import re

thestr = 'Othername California (2000) (T) (S) (ok) {state (#2.1)}'
regex = r'([^(]*) (?:\([^(]*\) ){3}\(([^(]*)\) {[^}]*\(\#([^)]*)\)}'
match = re.match(regex, thestr)

print match.groups()

1

尽管我在评论中说了什么,但我已经找到了一个解决方法:

(?(?=\([^()\w]*[\w.]+[^()\w]*\))\([^()\w]*([\w.]+)[^()\w]*\)|.)(?=[^{]*\})|(?<!\()(\b\w+\b)(?!\()|ok

解释:

说明:

(?                                  # If
(?=\([^()\w]*[\w.]+[^()\w]*\))      # There is (anything except [()\w] zero or more times, followed by [\w.] one or more times, followed by anything except [()\w] zero or more times)
\([^()\w]*([\w.]+)[^()\w]*\)        # Then match it, and put [\w.] in a group
|                                   # else
.                                   # advance with one character
)                                   # End if
(?=[^{]*\})                         # Look ahead if there is anything except { zero or more times followed by }

|                                   # Or
(?<!\()(\b\w+\b)(?!\()              # Match a word not enclosed between parenthesis
|                                   # Or
ok                                  # Match ok

在线演示


抱歉如果我问得有些多(我是Python和编程的新手)...你能给我一些其他的代码行来测试吗?(我已经尝试使用re.sub,但它给了我一个错误。谢谢!) - user2447387
我已经尝试在re.sub中替换您的正则表达式,并在第一个答案代码中使用它,但是出现错误...让我再试一下... - user2447387
似乎Python不支持这种if/else语句,请尝试使用(?:(?=\([^()\w]*[\w.]+[^()\w]*\))\([^()\w]*([\w.]+)[^()\w]*\)|(?!\([^()\w]*[\w.]+[^()\w]*\)).)(?=[^{]*\})|(?<!\()(\b\w+\b)(?!\()|ok - HamZa
这次没有错误,但我得到了错误的输出。使用字符串“Name1 Name2 Name3 (2000) (V) {variation (#4.12)}”,我得到了“Name1 Name2 Name3 (2000) (V) }” - user2447387
@user2447387 我给你的问题点了赞,这样你就可以获得20个声望值,现在你或许可以在Python聊天室寻求帮助。 - HamZa

0

另一种情况是:

^(\w+\s?\w+)\s?\(\d{1,}\)\s?\(\w+\)\s?\(\w+\)\s?\((\w+)\)\s?.*#(\d.\d)

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