如何制作文章旋转器正则表达式?

2 投票
5 回答
2686 浏览
提问于 2025-04-15 16:28

假设我有以下内容:

{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}

我想把它变成下面这些任意一种:

Hello world 
Goodbye noobs 
Hi earth
farewell n3wbz 
// etc.

注意“旋转”语法是如何嵌套的。我们不知道它可以嵌套多少层,可能有亿层那么多。

我可以轻松做到这一点,但一旦像上面的例子那样嵌套,我的正则表达式就会出问题,结果也不正确。

有人能用.NET语言或Python给我举个例子吗?

5 个回答

2

这个正则表达式反转器使用pyparsing来生成匹配的字符串(有一些限制,比如不允许使用像 + 和 * 这样的无限重复符号)。如果你把 {} 替换成 (),把你原来的字符串变成正则表达式,反转器会生成这个列表:

Helloworld
Helloearth
Hiworld
Hiearth
Heyworld
Heyearth
Goodbyenoobs
Goodbyen3wbz
Goodbyen00blets
farewellnoobs
farewelln3wbz
farewelln00blets

(我知道空格被压缩掉了,但也许这段代码能给你一些思路,帮助你解决这个问题。)

4

这应该很简单,只需要不允许一个大括号里面再包含另一个大括号,然后从里面开始,一层一层地进行替换。

def replacebrace(match):
    return random.choice(match.group(1).split('|'))

def randomizebraces(s):
   while True:
       s1= re.sub(r'\{([^{}]*)\}', replacebrace, s)
       if s1==s:
           return s
       s= s1

>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}')
'Hey world'
>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}')
'Goodbye noobs'
5

这里有一个简单的方法,使用 re.subn,它可以接受一个函数作为替换,而不仅仅是一个字符串:

import re
from random import randint

def select(m):
    choices = m.group(1).split('|')
    return choices[randint(0, len(choices)-1)]

def spinner(s):
    r = re.compile('{([^{}]*)}')
    while True:
        s, n = r.subn(select, s)
        if n == 0: break
    return s.strip()

这个方法会替换掉它遇到的所有最深层的选择,然后继续循环,直到没有选择可以替换为止。subn 会返回一个包含结果和替换次数的元组,这样你就可以方便地知道处理是否结束。

我的 select() 方法可以用 Bobince 的版本替代,后者使用了 random.choice(),如果你只是想要一个随机选择器,这个方法更优雅。如果你想构建一个选择树,你可以扩展上面的函数,但你需要全局变量来跟踪你的位置,所以把这些函数放到一个类里会更合理。这只是个提示,我不会详细展开这个想法,因为这并不是最初的问题。

最后要注意,如果你需要处理 Unicode 字符串(s = u"{...}"),你应该使用 r.subn(select, s, re.U)

示例:

>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}"
>>> print spinner(s)
'farewell n3wbz'

编辑:sub 替换为 subn,以避免无限循环(感谢 Bobince 指出这一点),并提高效率,同时将 {([^{}]+)} 替换为 {([^{}]*)},以便也能提取空的大括号。这样可以让它在处理格式不正确的模式时更加稳健。

对于喜欢把尽可能多的内容放在一行的人(我个人不太推荐这种做法):

def spin(s):
    while True:
        s, n = re.subn('{([^{}]*)}',
                       lambda m: random.choice(m.group(1).split("|")),
                       s)
        if n == 0: break
    return s.strip()

撰写回答