기사 스피너를 Regex로 만드는 방법?
문제
다음에 다음과 같이 가정 해 봅시다.
{{Hello | Hi | Hey} {World | Earth} | {Goodbye | farewell} {noobs | n3wbz | n00blets}}
그리고 나는 그것이 다음 중 하나로 바뀌기를 원합니다.
Hello world
Goodbye noobs
Hi earth
farewell n3wbz
// etc.
"회전"구문이 중첩되는 방식에주의를 기울입니다. 그것은 우리가 아는 모든 것을 위해 10 억 레이어로 중첩 될 수 있습니다.
위의 예와 같이 중첩 된 일단 내 동정형이 엉망이되고 결과가 정확하지 않은 것을 제외하고는이 작업을 쉽게 할 수 있습니다.
누군가 .net 언어 나 파이썬으로 예를 보여줄 수 있습니까?
해결책
간단한 방법 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()
임의의 선택기를 고수하고 싶다면 더 우아합니다. Choice 트리를 만들려면 위의 기능을 확장 할 수 있지만 현재 위치를 추적하려면 전역 변수가 필요하므로 기능을 클래스로 옮기는 것이 합리적입니다. 이것은 단지 힌트입니다. 나는 그것이 실제로 orginial 질문이 아니기 때문에 그 아이디어를 개발하지 않을 것입니다.
마지막으로 사용해야합니다 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'
이것 Regex 인버터 용도 pyparsing 일치하는 문자열을 생성하려면 (일부 제한 포함 - + 및 *와 같은 무제한 반복 기호는 허용되지 않습니다). 원래 문자열을 Regex로 만들기 위해 {} 's로 ()을 바꾸면 인버터 가이 목록을 생성합니다.
Helloworld
Helloearth
Hiworld
Hiearth
Heyworld
Heyearth
Goodbyenoobs
Goodbyen3wbz
Goodbyen00blets
farewellnoobs
farewelln3wbz
farewelln00blets
(공간이 무너 졌다는 것을 알고 있지만이 코드는이 문제를 공격하는 방법에 대한 아이디어를 줄 것입니다.)
나는 Re.Finditer를 사용하고 기본 구문 분석 트리를 만들기 위해 둥지 레벨을 결정합니다. 그렇게하려면 Regex 매치 객체의 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
이것은 당신에게 괄호로 된 표현의 중첩 깊이를 줄 것입니다. 파이프에 대해 유사한 논리를 추가하면 문제를 해결하는 데 도움이 될 것입니다.
살펴 보는 것이 좋습니다 다다 엔진 영감을 위해.
나는 내 요구를 표현하기 위해 체계와 레버리지 체계의 AST에서 영감을 얻은 것을 구현했습니다.
구체적으로, 나는 일반적으로 구문 분석기로서 선수를 사용하려고하는 것을 강력하게 추천합니다.