2015-06-23 15 views
19

Jak podano documentation, użycie regex.search(string, pos, endpos) nie jest całkowicie równoznaczne z cięciem struny, tj. regex.search(string[pos:endpos]). Nie pasuje to do wyrażenia regularnego pasującego do , tak jakby ciąg znaków zaczynał się od , więc ^ nie pasuje do początku podciągu, ale pasuje tylko do prawdziwego początku całego ciągu znaków. Jednak $ dopasowuje albo koniec podciągu, albo cały ciąg znaków.Dlaczego wyszukiwanie regex w podciągach "nie jest całkowicie równoważne z cięciem łańcucha" w Pythonie?

>>> re.compile('^am').findall('I am falling in code', 2, 12) 
    []  # am is not at the beginning 
    >>> re.compile('^am').findall('I am falling in code'[2:12]) 
    ['am'] # am is the beginning 
    >>> re.compile('ing$').findall('I am falling in code', 2, 12) 
    ['ing'] # ing is the ending 
    >>> re.compile('ing$').findall('I am falling in code'[2:12]) 
    ['ing'] # ing is the ending 

    >>> re.compile('(?<=)am').findall('I am falling in code', 2, 12) 
    ['am'] # before am there is a space 
    >>> re.compile('(?<=)am').findall('I am falling in code'[2:12]) 
    []  # before am there is no space 
    >>> re.compile('ing(?=)').findall('I am falling in code', 2, 12) 
    []  # after ing there is no space 
    >>> re.compile('ing(?=)').findall('I am falling in code'[2:12]) 
    []  # after ing there is no space 

    >>> re.compile(r'\bm.....').findall('I am falling in code', 3, 11) 
    [] 
    >>> re.compile(r'\bm.....').findall('I am falling in code'[3:11]) 
    ['m fall'] 
    >>> re.compile(r'.....n\b').findall('I am falling in code', 3, 11) 
    ['fallin'] 
    >>> re.compile(r'.....n\b').findall('I am falling in code'[3:11]) 
    ['fallin'] 

Moje pytania są ... Dlaczego nie jest zgodne między zaczynają i kończąc mecz? Dlaczego używanie pos i kończy się jako prawdziwy koniec, ale start/począteknie jest traktowany jako prawdziwy początek/początek?

Czy istnieje jakieś podejście, aby naśladować krojenie przy użyciu pos i endpos? Ponieważ Python copies string when slicing zamiast po prostu odwoływać się do starego, bardziej efektywne byłoby używanie pos i endpos zamiast krojenia podczas pracy z dużym ciągiem wiele razy.

+1

Bardzo dziwne, wydaje się, że nowy moduł regex ma to samo zachowanie. –

+4

Wygląda na to, że warto napisać raport o błędzie do pythona: http://bugs.python.org/ –

+1

@ArminRigo Ale dokumentacja mówi, że to może być "funkcja" :) – BornToCode

Odpowiedz

0

To brzmi jak błąd w Pythonie, ale jeśli chcesz zrobić przekrój przez odniesienie zamiast kopiować łańcuchy, możesz użyć wbudowanego w Pythona buffer.

Na przykład:

s = "long string" * 100 
buf = buffer(s) 
substr = buf([5:15]) 

Stwarza to podciąg bez kopiowania danych, więc powinno pozwolić na efektywne dzielenie dużych ciągów.

+0

Niezłe informacje o 'buforze', niestety nie są dostępne w Pythonie 3. – BornToCode

+1

@BornToCode: Niestety, nie udało mi się uzyskać" widoku pamięci "zastępującego Python3, aby działał poprawnie z wycinkami łańcucha. Pomyślałem, że wspomnę o buforze, ponieważ wciąż jest wielu użytkowników Python2, a pytanie nie określało wersji. –

+0

Byłbym ostrożny w podejmowaniu założeń dotyczących relacji między łańcuchami i buforami. Te drugie są zorientowane na bajty, podczas gdy łańcuchy (i wyrażenia regularne) działają na pełnym Unicode, szczególnie w Pythonie3. Tak więc kawałek 's [2: 4]' odnosi się do dwóch * znaków *, jednak mogą one być reprezentowane jako 3 do 6 * bajtów *. Oznacza to, że musisz uważać, zakładając, że możesz wykonywać operacje na pamięciach stałych na łańcuchach. Sugeruje to, że oryginalny plakat powinien brać pod uwagę duży obraz i rozważyć rozwiązanie algorytmiczne, a nie zagłębiać się zbyt głęboko w mikro-efektywność kodu Pythona. –

1

Argument dotyczący pozycji początkowej pos jest szczególnie przydatny na przykład przy analizatorach leksykalnych. Różnica w wydajności między cięciem struny z [pos:] i użyciem parametru pos może wydawać się nieistotna, ale na pewno tak nie jest; zobacz na przykład ten raport o błędzie w JsLex lexer.

Rzeczywiście, ^ dopasowuje się na prawdziwym początku łańcucha; lub, jeśli podano MULTILINE, również na początku linii; jest to również zgodne z projektem, więc skaner oparty na wyrażeniach regularnych może łatwo odróżnić prawdziwe początek linii/początku wejścia i tylko jakiś inny punkt na linii/w obrębie wejścia.

Należy pamiętać, że można również użyć funkcji regex.match(string[, pos[, endpos]]) zakotwiczyć meczu na łańcuch rozpoczynający lub w pozycji określonej przez pos; więc zamiast robić

>>> re.compile('^am').findall('I am falling in code', 2, 12) 
[] 

chcesz generalnie wdrożenie skaner jako

>>> match = re.compile('am').match('I am falling in code', 2, 12) 
>>> match 
<_sre.SRE_Match object; span=(2, 4), match='am'> 

a następnie ustawić pos do match.end() (który w tym przypadku zwraca 4) dla kolejnych operacji dopasowywania.

Mecz musi się znaleźć począwszy dokładnie w pos:

(Zauważ, że .match jest zakotwiczona na początku wejścia jakby niejawny ^ ale nie do końca wejścia, rzeczywiście to jest często źródłem błędów, jak ludzie wierzą ma mecz zarówno niejawne ^ i $ - Python 3.4 dodaje regex.fullmatch że robi to)


Co do tego dlaczego parametr endpos nie jest zgodny z pos - którego nie znam dokładnie, ale ma to również sens dla mnie, ponieważ w Pythonie 2 nie ma żadnego fullmatch i tam zakotwiczenie z $ jest jedynym sposobem zapewnienia aby dopasować całą rozpiętość.

Powiązane problemy