【问题标题】:Python Regex Excluding Multiple NewlinesPython 正则表达式排除多个换行符
【发布时间】:2014-12-24 01:25:38
【问题描述】:

所以我在解析文本时遇到了问题。我正在尝试解析音乐文件,它们是半格式化的。例如,我试图从歌词中排除合唱。大多数情况下,格式如下所示:

[合唱:x2] 一些歌词 更多歌词 [诗] 歌词 歌词

在这种情况下,这两个函数可以正确解析:

subChorus = re.sub(r'\[Chorus.*?\].*?\[', '[', lyrics, flags = re.DOTALL);
subChorus2 = re.sub(r'\[Chorus.*?\].*?(\n{2,})', '', lyrics, flags = re.DOTALL);

但是,有时合唱是文件的最后一部分:

歌词 [合唱] 一些歌词 其他歌词

在这种情况下,我无法找出删除合唱的正确表达方式。如果我这样做

subChorusEnd = re.sub(r'\[Chorus.*?\].*?$', '', lyrics, flags = re.DOTALL);

它会起作用的;但是,对于其他最终合唱部分不在末尾的文件,它将删除需要保留的诗句。后面有诗句的所有合唱块至少由两个换行符分隔。所以我想出了这个解决方案:

subChorusEnd = re.sub(r'\[Chorus.*?\][^(\n{2,})]*?$', '', subChorus4, flags = re.DOTALL);

但它不起作用。有人可以向我解释正确的正则表达式以使上述语句起作用还是更好的方法来仅删除位于文本部分末尾的合唱块,该文本也将保留最终合唱不在末尾的文件.

【问题讨论】:

  • 所以你只想删除作为结尾的部分,如果它在 Verse 之前保留它?
  • 实际上,我将这些 Chorus-regex 替换中的几个与无数其他表达式组合在一起,以过滤文本。文本档案的格式并不完全相同,所以我目前有大约 8 个合唱文本正则表达式替换。但在这种特殊情况下,是的,我想要一个函数,如果它位于末尾,则删除该部分,否则保留它(标签通常不说诗句,通常不在 [] 分隔符中,而是合唱标签应该在 [Chrous.*?] 标签中)
  • @andoni 你的预期输出是什么?
  • 您是说这个\[Chorus.*?\].*?(\n{2,}|$) 吗? regex101.com/r/kG6fX5/2
  • @AvinashRaj 这正是我想要的!非常感谢。我什至没有想过将它们与交替组合,所以我不会遇到无尾合唱块的问题!

标签: python regex regex-negation


【解决方案1】:

您可以尝试以下正则表达式来匹配所有合唱块。

\[Chorus.*?\].*?(\n{2,}|$)

DEMO

(?!.*\n\n)\[Chorus.*?\].*?$

它只匹配最后的chorus 块。不要忘记在两个正则表达式中启用 DOTALL 修饰符。

DEMO

【讨论】:

  • 我最初想要一个不依赖于 \n\n 的解决方案。你对这个问题的评论提供了最想要的答案,我认为你应该得到接受的答案。同样经过进一步思考,我相信在所有格式中,开头的 \n\n 也足够了。谢谢。
  • 不客气。 (?!.*\n\n) 在开头断言匹配不会包含任何空行。
【解决方案2】:

比起使用正则表达式,我更愿意逐行遍历歌词,并决定是否使用基本上是蹩脚的有限状态机来保留每一行:

lyrics1 = '''Lyrics

[Chorus]
Some Lyrics
Other Lyrics'''

lyrics2 = '''[Chorus: x2]
Some Lyrics
Some More Lyrics

[Verse]
Lyrics
Lyrics'''

def clean(lyrics):
    result = []
    omitting = False
    for line in lyrics.split('\n'):
        if '[Chorus' in line:
            omitting = True
        if '[' in line and '[Chorus' not in line:
            omitting = False
        if not omitting:
            result.append(line)
    return '\n'.join(result)

print(clean(lyrics1))
print('------------')
print(clean(lyrics2))

结果:

Lyrics

------------
[Verse]
Lyrics
Lyrics

所以基本上,如果我们看到“合唱”行并停止输出行,我们就会打开一个标志;然后,如果我们看到任何带括号的东西不是“合唱”,我们将标志翻转回来并恢复输出行。

我不知道您正在解析的实际文件是什么样的,但这样的策略可能会比在问题上抛出庞大的正则表达式更有成效。

【讨论】:

  • 这令人惊讶地适用于大多数格式。有一些文件是用合唱标签格式化的,然后在诗句开始前只有两个换行符,没有每句诗的标签(这就是为什么我在我的问题中包含 subChorus2 以了解我如何处理其他格式)。嗯,我可以修改这个来处理那些边缘情况。另外,您是否知道逐行执行此操作与正则表达式的性能?我正在运行超过 20+ 百万行歌词。
  • @andoni 我怀疑您将主要受到磁盘读写速度的限制。假设您的行不是很长,这里的操作(检查一个字符串是否为in 另一个)相当快。
【解决方案3】:
\[Chorus:[^\]]+\][\s\S]*?(?=\n{2}|$)

尝试所有类型的合唱。替换为empty string。查看演示。

https://regex101.com/r/vN3sH3/77

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多