Pergunta

Digamos que eu tenho teh seguinte:

{{Olá | Hi | Hey} {mundo | terra} | {Adeus | despedida} {noobs | n3wbz | n00blets}}

E eu quero isso para se transformar em qualquer um dos seguintes:

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

Prestando atenção para a forma como a sintaxe "fiação" está aninhado. Pode ser aninhados um bilhão de camadas profundas para todos nós sabemos.

Eu posso fazer isso fácil, exceto uma vez que eles estão aninhados como no exemplo acima meus messes regex-se e os resultados não estão corretas.

Alguém poderia mostrar um exemplo em qualquer uma linguagem .NET ou Python por favor?

Foi útil?

Solução

Uma maneira simples com re.subn , que também pode aceitar uma função em vez de uma cadeia de substituição:

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()

Ele simplesmente substitui todas as escolhas mais profundas que se encontra, em seguida, repete até que não haja restos de escolha. subn retorna uma tupla com o resultado e quantas substituições foram feitas, o que é conveniente para detectar o fim do processamento.

A minha versão do select() pode ser substituída pela usos random.choice() de bobince e é mais elegante, se você só quer ficar com um seletor aleatório. Se você quer construir uma árvore de escolha, você pode estender a função acima, mas você vai precisar variáveis ??globais para manter o controle de onde você é, assim movendo as funções em uma classe faria sentido. Esta é apenas uma sugestão, não vou desenvolver essa idéia, pois não era realmente a questão orginial.

Note finalmente que você deve usar r.subn(select, s, re.U) se precisar strings unicode (s = u"{...}")

Exemplo:

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

Editar: sub Substituído por subn para evitar loop infinito (graças a bobince de indicá-lo) e torná-lo mais eficiente, e {([^{}]+)} substituído por {([^{}]*)} para extrair chaves vazias também. Que deve torná-lo mais robusto para padrões mal-formatadas.

Para as pessoas que gostam de colocar o máximo possível em uma linha (que eu, pessoalmente, não iria encorajar):

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()

Outras dicas

deve ser bastante simples, basta não permitir um conjunto cinta de incluir o outro, em seguida, chamar repetidamente fazendo substituições dos jogos internos para fora:

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'

Este regex inversor usos pyparsing para gerar seqüências de correspondência (com algumas restrições - símbolos de repetição ilimitada como + e * não são permitidos). Se você substituir {} é com () é para tornar a sua seqüência original em um regex, o inversor gera esta lista:

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

(Eu sei que os espaços estão fechados, mas talvez este código vai lhe dar algumas idéias sobre como atacar este problema.)

Gostaria de usar re.finditer e construir uma árvore básica de análise para determinar o nível de aninhamento. Para fazê-lo, gostaria de usar o atributo de extensão do objeto jogo regex:

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

Isto lhe daria a profundidade de aninhamento das expressões entre colchetes. Adicione um pouco de lógica semelhante para os tubos e você estará bem em seu caminho para resolver o problema.

Eu recomendo dar uma olhada motor dada para a inspiração.

Eu fiz uma implementação de algo inspirado por este no esquema e alavancou AST do esquema de expressar minhas necessidades.

Especificamente, eu recomendo fortemente contra a tentativa de usar uma regex como um analisador em geral.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top