如何制作文章旋转器正则表达式?
假设我有以下内容:
{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}
我想把它变成下面这些任意一种:
Hello world
Goodbye noobs
Hi earth
farewell n3wbz
// etc.
注意“旋转”语法是如何嵌套的。我们不知道它可以嵌套多少层,可能有亿层那么多。
我可以轻松做到这一点,但一旦像上面的例子那样嵌套,我的正则表达式就会出问题,结果也不正确。
有人能用.NET语言或Python给我举个例子吗?
5 个回答
这应该很简单,只需要不允许一个大括号里面再包含另一个大括号,然后从里面开始,一层一层地进行替换。
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'
这里有一个简单的方法,使用 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()