Вопрос

Допустим, у меня есть следующее:

{{Привет|Hi|Hey} {мир|земля} | {Прощай|farewell} {новички|n3wbz|n00blets}}

И я хочу, чтобы это превратилось в любое из следующих действий:

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

Обращая внимание на то, как "вращающийся" синтаксис является вложенным.Насколько нам известно, он может быть вложен в миллиард слоев глубиной.

Я могу сделать это легко, за исключением того, что, как только они будут вложены, как в приведенном выше примере, мое регулярное выражение испортится, и результаты будут неверными.

Не мог бы кто-нибудь показать пример на языке .NET или Python, пожалуйста?

Это было полезно?

Решение

Простой способ с re.субн, который также может принимать функцию вместо строки замены:

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) если вам нужны строки в Юникоде (s = 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()

Другие советы

Должно быть довольно простым, просто запретить набор скобок включать другой, а затем повторно вызывать выполнение замен из внутренних совпадений:

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'

Этот инвертор регулярных выражений использует pyparsing для создания совпадающих строк (с некоторыми ограничениями - неограниченные символы повторения, такие как + и *, не допускаются). Если вы замените {} на (), чтобы превратить вашу исходную строку в регулярное выражение, инвертор сгенерирует этот список:

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

(Я знаю, что пробелы сокращены, но, возможно, этот код даст вам некоторые идеи о том, как решить эту проблему.)

Я бы использовал 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

Это даст вам глубину вложенности выражений в скобках. Добавьте аналогичную логику для каналов, и вы окажетесь на пути к решению проблемы.

Я бы рекомендовал взглянуть на механизм dada для вдохновения.

Я реализовал что-то, вдохновленное этим в схеме, и использовал AST схемы, чтобы выразить свои потребности.

В частности, я бы настоятельно рекомендовал не пытаться использовать регулярные выражения в качестве синтаксического анализатора в целом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top