Jaka jest różnica między re.search a re.match?

526

Jaka jest różnica między funkcjami search()i match()w module Pythonre ?

Przeczytałem dokumentację ( aktualna dokumentacja ), ale chyba nigdy jej nie pamiętam. Muszę to sprawdzić i ponownie się nauczyć. Mam nadzieję, że ktoś odpowie na to jasno przykładami, aby (być może) utkwiło mi w głowie. A przynajmniej będę miał lepsze miejsce do powrotu z moim pytaniem i jego ponowne nauczenie zajmie mniej czasu.

Daryl Spitzer
źródło

Odpowiedzi:

508

re.matchjest zakotwiczony na początku łańcucha. Nie ma to nic wspólnego z nowymi liniami, więc nie jest to to samo, co używanie ^we wzorcu.

Jak mówi dokumentacja re.match :

Jeśli zero lub więcej znaków na początku łańcucha pasuje do wzorca wyrażenia regularnego, zwróć odpowiednią MatchObjectinstancję. Zwraca, Nonejeśli ciąg nie pasuje do wzorca; zauważ, że różni się to od dopasowania o zerowej długości.

Uwaga: jeśli chcesz znaleźć dopasowanie w dowolnym miejscu ciągu, użyj search() zamiast niego.

re.searchprzeszukuje cały ciąg, jak głosi dokumentacja :

Przeszukaj ciąg znaków w poszukiwaniu lokalizacji, w której wzorzec wyrażenia regularnego tworzy dopasowanie, i zwróć odpowiednią MatchObjectinstancję. Zwraca, Nonejeśli żadna pozycja w ciągu nie pasuje do wzorca; zauważ, że różni się to od znalezienia dopasowania o zerowej długości w pewnym momencie ciągu.

Więc jeśli potrzebujesz dopasować na początku ciągu lub dopasować cały ciąg, użyj match. To jest szybsze. W przeciwnym razie użyj search.

Dokumentacja zawiera konkretną sekcję dla matchvs,search która obejmuje również ciągi wielowierszowe:

Python oferuje dwie różne prymitywne operacje oparte na wyrażeniach regularnych: matchsprawdza dopasowanie tylko na początku łańcucha, podczas gdy searchsprawdza dopasowanie w dowolnym miejscu łańcucha (domyślnie robi to Perl).

Zauważ, że matchmoże się różnić od search nawet gdy używasz wyrażenia regularnego rozpoczynającego się od '^': '^'dopasowuje tylko na początku łańcucha, lub w MULTILINEtrybie również bezpośrednio po nowej linii. Operacja „ match” kończy się powodzeniem tylko wtedy, gdy wzorzec pasuje na początku łańcucha, niezależnie od trybu, lub w pozycji początkowej podanej przez opcjonalny pos argument, niezależnie od tego, czy poprzedza go nowa linia.

Teraz dość gadania. Czas zobaczyć przykładowy kod:

# example code:
string_with_newlines = """something
someotherthing"""

import re

print re.match('some', string_with_newlines) # matches
print re.match('someother', 
               string_with_newlines) # won't match
print re.match('^someother', string_with_newlines, 
               re.MULTILINE) # also won't match
print re.search('someother', 
                string_with_newlines) # finds something
print re.search('^someother', string_with_newlines, 
                re.MULTILINE) # also finds something

m = re.compile('thing$', re.MULTILINE)

print m.match(string_with_newlines) # no match
print m.match(string_with_newlines, pos=4) # matches
print m.search(string_with_newlines, 
               re.MULTILINE) # also matches
nosklo
źródło
Co z ciągami zawierającymi znaki nowej linii?
Daryl Spitzer,
25
Dlaczego więc ktoś miałby używać ograniczonego, matcha nie bardziej ogólnego search? czy to dla prędkości?
Alby,
13
@Alby dopasowanie jest znacznie szybsze niż wyszukiwanie, więc zamiast wykonywać regex.search („słowo”), możesz zrobić regex.match ((. *?) Słowo (. *?)) I zyskać mnóstwo wydajności, jeśli pracujesz z miliony próbek.
ivan_bilan
19
Cóż, to głupie. Po co to nazywać match? Czy to sprytny manewr, aby obsadzić API nieintuicyjnymi nazwami, aby zmusić mnie do przeczytania dokumentacji? Nadal tego nie zrobię! Buntownik!
Sammaron,
1
@ivan_bilan matchwygląda trochę więcej fasterniż wyszukiwanie, gdy używasz tego samego wyrażenia regularnego, ale twój przykład wydaje się nieprawidłowy zgodnie z testem wydajności: stackoverflow.com/questions/180986/…
baptx
101

search ⇒ znajdź coś w dowolnym miejscu ciągu i zwróć obiekt dopasowania.

match⇒ znajdź coś na początku łańcucha i zwróć obiekt dopasowania.

Dhanasekaran Anbalagan
źródło
49

re.search szukaj es za wzór na całym ciągiem , a re.matchnie nie szukać wzoru; jeśli nie, nie ma innego wyjścia, jak dopasować go na początku łańcucha.

Xilun
źródło
5
Po co dopasowywać na początku, ale nie do końca łańcucha ( fullmatchw phyton 3.4)?
Smit Johnth
49

dopasowanie jest znacznie szybsze niż wyszukiwanie, więc zamiast wykonywać regex.search („słowo”), możesz zrobić regex.match ((. *?) słowo (. *?)) i zyskać mnóstwo wydajności, jeśli pracujesz z milionami próbki.

Ten komentarz od @ivan_bilan pod powyższą zaakceptowaną odpowiedzią zmusił mnie do zastanowienia się, czy taki hack naprawdę przyspiesza, więc dowiedzmy się, ile ton wydajności naprawdę zyskasz.

Przygotowałem następujący zestaw testów:

import random
import re
import string
import time

LENGTH = 10
LIST_SIZE = 1000000

def generate_word():
    word = [random.choice(string.ascii_lowercase) for _ in range(LENGTH)]
    word = ''.join(word)
    return word

wordlist = [generate_word() for _ in range(LIST_SIZE)]

start = time.time()
[re.search('python', word) for word in wordlist]
print('search:', time.time() - start)

start = time.time()
[re.match('(.*?)python(.*?)', word) for word in wordlist]
print('match:', time.time() - start)

Wykonałem 10 pomiarów (1M, 2M, ..., 10M słów), co dało mi następujący wykres:

dopasowywanie vs. wyszukiwanie regex wykres linii testu prędkości

Powstałe linie są zaskakująco (a właściwie nie tak zaskakująco) proste. A searchfunkcja jest (nieco) szybsza, biorąc pod uwagę tę konkretną kombinację wzorów. Morał tego testu: Unikaj nadmiernej optymalizacji kodu.

Jeyekomon
źródło
11
+1 za faktyczne zbadanie założeń zawartych w stwierdzeniu, które należy przyjąć według wartości nominalnej - dzięki.
Robert Dodier
Rzeczywiście komentarz @ivan_bilan wygląda źle, ale matchfunkcja jest nadal szybsza niż searchfunkcja, jeśli porównasz to samo wyrażenie regularne. Możesz sprawdzić w swoim skrypcie, porównując re.search('^python', word)do re.match('python', word)(lub re.match('^python', word)który jest taki sam, ale łatwiejszy do zrozumienia, jeśli nie czytasz dokumentacji i wydaje się, że nie wpływa na wydajność)
baptx
@baptx Nie zgadzam się ze stwierdzeniem, że matchfunkcja jest generalnie szybsza. Im matchszybciej, gdy chcesz szukać na początku ciągu, tym searchszybciej, gdy chcesz przeszukać cały ciąg. Co odpowiada zdrowemu rozsądkowi. Dlatego @ivan_bilan się mylił - matchprzeszukiwał cały ciąg. Właśnie dlatego masz rację - matchszukałeś na początku łańcucha. Jeśli się ze mną nie zgadzasz, spróbuj znaleźć wyrażenie regularne, matchponieważ jest ono szybsze re.search('python', word)i wykonuje tę samą pracę.
Jeyekomon
@baptx Ponadto, przypis re.match('python') jest nieco szybszy niż re.match('^python'). To musi być.
Jeyekomon
@Jeyekomon tak właśnie to miałem na myśli, matchfunkcja jest nieco szybsza, jeśli chcesz wyszukiwać na początku łańcucha (na przykład przy użyciu searchfunkcji, aby znaleźć słowo na początku łańcucha re.search('^python', word)). Ale searchwydaje mi się to dziwne, jeśli powiesz funkcji, by wyszukiwała na początku łańcucha, powinna być tak szybka jak matchfunkcja.
baptx
31

Możesz zapoznać się z poniższym przykładem, aby zrozumieć działanie re.matchi ponowne wyszukiwanie

a = "123abc"
t = re.match("[a-z]+",a)
t = re.search("[a-z]+",a)

re.matchwróci none, ale re.searchwróci abc.

ldR
źródło
3
Chciałbym tylko dodać, że wyszukiwanie zwróci _sre.SRE_Match obiekt (lub None, jeśli nie zostanie znaleziony). Aby uzyskać „abc”, musisz wywołać t.group ()
SanD
30

Różnica polega na tym, że re.match() wprowadza w błąd każdego kto jest przyzwyczajony do dopasowywania wyrażeń regularnych Perl , grep lub sed , i re.search()nie robi tego. :-)

Bardziej trzeźwo jak zauważa John D. Cook , re.match()„zachowuje się tak, jakby każdy wzorzec został„ poprzedzony ”. Innymi słowy, re.match('pattern')jest równy re.search('^pattern'). Więc zakotwicza lewą stronę wzoru. Ale nie zakotwicza także prawej strony wzoru: nadal wymaga to zakończenia $.

Szczerze mówiąc, biorąc pod uwagę powyższe, uważam, że re.match()powinien być przestarzały. Chciałbym poznać powody, dla których należy to zachować.

ODCZYT KODU
źródło
4
„zachowuje się tak, jakby każdy wzorzec był poprzedzony ^.” jest prawdą tylko wtedy, gdy nie używasz opcji multilinii. Prawidłowe zdanie brzmi „... ma \ A wcześniej”
JoelFan
14

re.match próbuje dopasować wzór na początku łańcucha . re.search próbuje dopasować wzór w ciągu, dopóki nie znajdzie dopasowania.

cschol
źródło
3

Znacznie krótszy:

  • search skanuje cały ciąg.

  • match skanuje tylko początek ciągu.

Następujący Ex mówi:

>>> a = "123abc"
>>> re.match("[a-z]+",a)
None
>>> re.search("[a-z]+",a)
abc
U10 do przodu
źródło