2016-07-06 14 views
6

Próbuję wykonać następujące czynności z wyrażenia regularnego:Python wyrażenie regularne do zastąpienia wszystko oprócz konkretnych słów

import re 
x = re.compile('[^(going)|^(you)]') # words to replace 
s = 'I am going home now, thank you.' # string to modify 
print re.sub(x, '_', s) 

Wynik pojawia się:

'_____going__o___no______n__you_' 

Wynik chcę jest:

'_____going_________________you_' 

Od ^ może być użyty tylko wewnątrz nawiasów [], ten wynik ma sens, ale nie jestem pewien, jak inaczej to zrobić.

Próbowałem nawet '([^g][^o][^i][^n][^g])|([^y][^o][^u])', ale otrzymałem '_g_h___y_'.

+1

Tylko dla FYI: Powód, dla którego '[^ (going) |^(you)]' zawodzi, jest spowodowany tym, że składnia '[..]' daje tylko jeden znak *. Znak '^' na samym początku jest wyjątkowy, co znaczy "nie", ale wszystko po nim jest uważane za niestandardowy zestaw znaków: '()^ginouy |'. – usr2564301

Odpowiedz

5

Nie tak łatwo jak się po raz pierwszy pojawia, ponieważ nie ma "nie" w REs z wyjątkiem ^ wewnątrz [ ], który dopasowuje tylko jeden znak (jak znalazłeś). Oto moje rozwiązanie:

import re 

def subit(m): 
    stuff, word = m.groups() 
    return ("_" * len(stuff)) + word 

s = 'I am going home now, thank you.' # string to modify 

print re.sub(r'(.+?)(going|you|$)', subit, s) 

Daje:

_____going_________________you_ 

Aby wyjaśnić. Sam RE (zawsze używam surowych ciągów) pasuje do jednej lub więcej dowolnych znaków (.+), ale nie jest chciwy (?). Jest to przechwytywane w pierwszej grupie nawiasów (nawiasach). Następnie następuje "idź" lub "ty" lub koniec linii ($).

subit to funkcja (można ją nazwać dowolną z przyczyn), która jest wywoływana dla każdego podstawienia. Zostaje przekazany obiekt dopasowujący , z którego możemy pobrać przechwycone grupy. Pierwsza grupa, której potrzebujemy, to długość, ponieważ zastępujemy każdy znak podkreśleniem. Zwrócony ciąg zastępuje pasujący do wzorca.

3

Oto podejście jeden regex:

>>> re.sub(r'(?!going|you)\b([\S\s]+?)(\b|$)', lambda x: (x.end() - x.start())*'_', s) 
'_____going_________________you_' 

Chodzi o to, że gdy mamy do czynienia ze słowami i chcesz je wyłączyć lub itp trzeba pamiętać, że większość silników regex (większość używają tradycyjnej NFA) analizują ciągi znaków według znaków. I tutaj, ponieważ chcesz wykluczyć dwa słowa i chcesz użyć ujemnego wyprzedzenia, musisz zdefiniować dozwolone ciągi znaków jako słowa (używając granicy słowa), a ponieważ w subie zastępuje dopasowane wzorce za pomocą ciągu zastępującego, nie można po prostu przekazać _, ponieważ w takim przypadku zastąpi część taką jak I am z 3 podkreśleniami (I, "", "am"). Więc możesz użyć funkcji, aby przekazać jako drugi argument sub i pomnóż _ o długości dopasowanego ciągu do zamiany.

+0

Ostatni "." Przed końcem tekstu? Ponadto zbyt mało podkreśleń między 'going' i' you'. – cdarke

+0

@cdarke Tak, wydaje mi się, pozwól mi sprawdzić! – Kasramvd

+1

@cdarke Możesz teraz wykupić edycję ;-) – Kasramvd