2

假设我有以下内容:

{{你好|嗨|嘿} {世界|地球} | {再见|再见} {noobs|n3wbz|n00blets}}

我希望它变成以下任何一种:

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

注意“旋转”语法的嵌套方式。据我们所知,它可以嵌套十亿层。

我可以很容易地做到这一点,除非它们像上面的例子一样嵌套,我的正则表达式搞砸了,结果不正确。

有人可以用 .NET 语言或 Python 举例说明吗?

4

5 回答 5

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()如果你想构建一个选择树,你可以扩展上面的函数,但是你需要全局变量来跟踪你的位置,所以将函数移动到一个类中是有意义的。这只是一个提示,我不会发展这个想法,因为它不是真正的原始问题。

最后请注意,r.subn(select, s, re.U)如果您需要 unicode 字符串,则应使用 ( s = u"{...}")

例子:

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

编辑:替换subsubn以避免无限循环(感谢 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()
于 2009-11-27T23:56:47.977 回答
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'
于 2009-11-28T00:09:37.040 回答
2

正则表达式逆变器使用pyparsing生成匹配字符串(有一些限制 - 不允许无限重复符号,如 + 和 *)。如果您将 {} 替换为 () 以使您的原始字符串成为正则表达式,则逆变器会生成此列表:

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

(我知道空格已经折叠了,但也许这段代码会给你一些关于如何解决这个问题的想法。)

于 2009-11-28T04:15:33.770 回答
1

我会使用 re.finditer 并构建一个基本的解析树来确定嵌套级别。为此,我将使用正则表达式匹配对象的 span 属性:

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

import re
re_bracks = re.compile(r'{.+?}')

# subclass list for a basic tree datatype
class bracks(list):
    def __init__(self, m):
        self.m = m

# icky procedure to create the parse tree
# I hate these but don't know how else to do it
parse_tree = []
for m in re_bracks.finditer(text):
    if not this_element:
        # this first match
        parse_tree.extend(element(m))
    else:
        # ... and all the rest
        this_element = bracks(m)
        this_start, this_end = m.span()

        # if this match is nested in the old one ...
        if this_start < previous_start and this_end > previous_end:
            # nest it inside the previous one
            previous_element.extend(this_element) 
        else:
            # otherwise make it a child of the parse_tree
            parse_tree.extend(element(m))

        previous_element = this_element
        previous_start, previous_end = this_start, this_end

这将为您提供括号表达式的嵌套深度。为管道添加一些类似的逻辑,您就可以很好地解决问题。

于 2009-11-27T23:05:29.370 回答
1

我建议看一下达达引擎以获得灵感。

我已经在方案中实现了受此启发的一些东西,并利用方案的 AST 来表达我的需求。

具体来说,我强烈建议不要尝试使用正则表达式作为解析器。

于 2009-11-27T23:19:26.767 回答