Python强制regex在某些条件下失败



在python3(3.9.6)中,使用regex 2.5.91模块

给定文件中的这些行:

1. <span>abc.</span>
2. <span>bcd</span>
3. <span>xyz.</span>
4. <span class="good">abc</span>
5. <span class="good">abc.</span>
6. <span class="good">xyz.</span>
7. <span id="whatever">def.</span>
8. <span id="whatever">xyz.</span>
9. <span class="good" id="whatever">ghi.</span>
10. <span class="bad" id="whatever">jkl.</span>
11. <span id="whatever" class="good">abc.</span>

我正在尝试创建一个正则表达式,将:

  1. 拒绝匹配任何带有类的行,文本末尾没有句号,或者带有"xyz."在文本中。这意味着只匹配第1行和第7行。
  2. 对于有效行,捕获(id="whatever")(如果有),以及span中的文本。

这不是一次处理一行;整个文件被读入一个变量,然后运行regex.sub(),使用捕获组一次对所有内容进行替换。(改变这一点不在这个问题的范围之内。)因此,正则表达式必须在"坏"上失败。

我能够成功地使用*SKIP和*FAIL使它失败,如果它找到一个类并捕获id(如果它存在),但是当我试图做同样的事情时,如果它找到"xyz."在文本中。现在,这只适用于好的行,而不包括有类的行。

newtext = regex.sub(r"""<span(?:(?:.*? class=".*?"(*SKIP)(*FAIL))?|( id="[a-z]+?")?)>([a-z]+.)</span>""",r"""<span class="other"1>2</span>""", text)

当我试图添加另一个"|"在文本部分失败,如果它发现"xyz.",它导致整个事情总是失败,而不仅仅是当xyz。被发现。这根本不管用,不管有没有?在标签之间的文本部分的第一组之后。

newtext = regex.sub(r"""<span(?:(?:.*? class=".*?"(*SKIP)(*FAIL))?|( id="[a-z]+?")?)>(?:(?:xyz.(*SKIP)(*FAIL))|([a-z]+.))</span>""",r"""<span class="other"1>2</span>""", text)

我承认我在*SKIP/*FAIL上很弱,但我想当我让第一个工作时,我可能足够理解它,但第二个证明我错了。

那么,是否可能在单个正则表达式中。Sub如上所述来完成目标,即跳过应该跳过的行,并添加一个类(在id前面,如果存在)到不存在的行?(我知道我现在没有对文本捕获组做任何事情,但我会的,它只是与这个问题无关。)

试试这个模式:^(?!<span[^<>]*class=)(?=<span(.*)>([^<>]*.))(?!<span.*>xyz.</span>).*$

参见Regex demo

代码:

import re
pattern = "^(?!<span[^<>]*class=)(?=<span(.*)>([^<>]*.))(?!<span.*>xyz.</span>).*$"
text = """
<span>abc.</span>
<span>bcd</span>
<span>xyz.</span>
<span class="good">abc</span>
<span class="good">abc.</span>
<span class="good">xyz.</span>
<span id="whatever">def.</span>
<span id="whatever">xyz.</span>
<span class="good" id="whatever">ghi.</span>
<span class="bad" id="whatever">jkl.</span>
<span id="whatever" class="good">abc.</span>
"""
print(re.sub(pattern, "<span class="other"\1>\2</span>", text, 0, re.MULTILINE))

输出:

<span class="other">abc.</span>
<span>bcd</span>
<span>xyz.</span>
<span class="good">abc</span>
<span class="good">abc.</span>xyz.
<span class="good">xyz.</span>
<span class="other" id="whatever">def.</span>
<span id="whatever">xyz.</span>
<span class="good" id="whatever">ghi.</span>
<span class="bad" id="whatever">jkl.</span>
<span id="whatever" class="good">abc.</span>

文件是正确的HTML吗?也许可以试试html.parser.HTMLParser,像这样:

from html import parser
from io import IOBase
class MyParser(parser.HTMLParser):
"""collects acceptable line-numbers/line-data"""

def __init__(self, obj):
"""construct self, feed self"""
super().__init__()
self.data = list()
if isinstance(obj, IOBase):
line = obj.readline() # might need .decode("latin-1")
while line is not "":
self.feed(line)
line = fp.readline() # .decode("latin-1")
self.close()
elif isinstance(obj, str):
self.feed(obj)
def handle_starttag(self, tag, attrs):
"""accept/reject tags"""
attrs = dict(attrs)
if tag == "span" and "class" not in attrs:
data = self.get_starttag_text()
if data.endswith(".") and "xyz." not in data:
self.data.append((attrs.get("id"), data))

# usage would be like:
good_tags = MyParser(html_string) # a string or a file
for tag_id, text in good_tags.data:
print(tag_id, text)

这种方法的好处是不需要复杂的正则,甚至可以改变为使用简单的字符串方法,如endswith(或更原子/更简单的正则,如果你喜欢)。构造函数被写入以接受文件指针或字符串对象。当输入文件内容时,每次遇到标记时实例都会调用handle_starttag方法,并执行其中的逻辑。在文件或字符串的末尾,所有所需的数据都应该在data属性中可用。

最新更新