Яка різниця між re.search та re.match?


526

Яка різниця між search()та match()функціями в модулі Pythonre ?

Я читав документацію ( поточну документацію ), але ніби ніколи її не пам’ятаю. Мені постійно доводиться шукати це і знову його вивчати. Я сподіваюсь, що хтось відповість на це чітко на прикладах, щоб (можливо) це засунулось мені в голову. Або, принаймні, у мене буде краще повернутися зі своїм запитанням, і для його повторного вивчення знадобиться менше часу.

Відповіді:


508

re.matchзакріплюється на початку рядка. Це не має нічого спільного з новими рядками, тому це не те саме, що використання ^у шаблоні.

Як зазначено в документації на повторний збір :

Якщо нуль або більше символів на початку рядка відповідають шаблону регулярних виразів, поверніть відповідний MatchObjectекземпляр. Повернути, Noneякщо рядок не відповідає шаблону; зауважте, що це відрізняється від нульової довжини.

Примітка. Якщо ви хочете знайти відповідність будь-де в рядку, використовуйте search() замість цього.

re.searchшукає весь рядок, як зазначено в документації :

Перегляньте рядок, шукаючи місце, де шаблон звичайного вираження створює відповідність, і поверніть відповідний MatchObjectекземпляр. Повернення, Noneякщо жодна позиція в рядку не відповідає шаблону; зауважте, що це відрізняється від знаходження відповідності нульової довжини в якийсь момент рядка.

Тож якщо вам потрібно співставити на початку рядка, або щоб відповідати всьому використанню рядка match. Це швидше. В іншому випадку використовуйте search.

Документація має специфічний розділ для matchпорівняння,search який також охоплює багаторядкові рядки:

Python пропонує дві різні примітивні операції на основі регулярних виразів: matchперевіряє відповідність лише на початку рядка, тоді як searchперевіряє відповідність будь-де в рядку (це те, що Perl робить за замовчуванням).

Зауважте, що matchможе відрізнятися search навіть від використання звичайного виразу, що починається з '^': '^'відповідає лише на початку рядка, або в MULTILINEрежимі, також безпосередньо після нового рядка. Операція " match" проходить успішно, лише якщо шаблон узгоджується на початку рядка незалежно від режиму або в початковому положенні, заданому необов'язковим pos аргументом, незалежно від того, чи передує йому новий рядок.

Тепер достатньо розмов. Час переглянути приклад коду:

# 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

А що з рядками, що містять нові рядки?
Дарил Спітцер

25
Чому б хтось тоді використовував обмежені, matchа не загальніші search? це для швидкості?
Елбі

13
@Alby співпадає набагато швидше, ніж пошук, тому замість того, щоб робити regex.search ("слово"), ви можете зробити regex.match ((. *?) Слово (. *?)) І набрати тони продуктивності, якщо ви працюєте з мільйони зразків.
ivan_bilan

19
Ну, це гуфі. Чому це називати match? Чи розумний маневр засівати API неінтуїтивними іменами, щоб змусити мене читати документацію? Я все одно не зроблю цього! Повстанець!
Саммарон

1
@ivan_bilan matchвиглядає трохи , fasterніж пошук, використовуючи один і той же регулярний вираз , але ваш приклад здається неправильним згідно з тестом продуктивності: stackoverflow.com/questions/180986 / ...
baptx

101

search ⇒ знайти де-небудь у рядку та повернути об’єкт відповідності.

matchЗнайти щось на початку рядка та повернути об’єкт відповідності.


49

re.search пошук ес для шаблону всього рядка , в той час як re.matchце не пошук шаблону; якщо цього немає, у нього немає іншого вибору, ніж відповідати йому на початку рядка.


5
Чому матч на початку, але не до кінця рядка ( fullmatchу фітоні 3.4)?
Smit Johnth

49

відповідати набагато швидше, ніж пошук, тому замість того, щоб робити regex.search ("слово"), ви можете зробити regex.match ((. *?) слово (. *?)) і набрати тони продуктивності, якщо ви працюєте з мільйонами зразки.

Цей коментар від @ivan_bilan у відповіді вище прийнятої відповіді змусив мене задуматися, чи такий хакер насправді щось прискорить, тож давайте дізнаємося, скільки тонн ви дійсно отримаєте.

Я підготував наступний тестовий набір:

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)

Я зробив 10 вимірювань (1М, 2М, ..., 10М слів), які дали мені наступний сюжет:

Графік ліній матчу проти пошуку

Отримані лінії напрочуд (насправді не так дивно) прямі. І ця searchфункція (трохи) швидша, враховуючи цю специфічну комбінацію малюнків. Мораль цього тесту: уникайте надмірного оптимізації коду.


11
+1 за те, що насправді досліджували припущення, що стоять за твердженням, яке слід сприймати за номінал - дякую.
Роберт Дод'є

Дійсно, коментар @ivan_bilan виглядає неправильним, але matchфункція все ж швидша за searchфункцію, якщо порівнювати той же регулярний вираз. Ви можете перевірити в сценарії, порівнюючи re.search('^python', word)з re.match('python', word)(або re.match('^python', word)що те ж саме , але легше зрозуміти , якщо ви не читали документацію і , здається , не впливає на продуктивність)
baptx

@baptx Я не згоден із твердженням, що matchфункція, як правило, швидша. Чим matchшвидше, коли ви хочете здійснити пошук на початку рядка, тим searchшвидше, коли ви хочете шукати по всій рядку. Що відповідає здоровому глузду. Ось чому @ivan_bilan помилявся - він matchшукав по всій рядку. Тому ви маєте рацію - ви раніше matchшукали на початку рядка. Якщо ви не згодні зі мною, спробуйте знайти регекс для matchцього швидше, ніж re.search('python', word)і виконайте ту саму роботу.
Jeyekomon

@baptx Крім того , в якості виноски, то re.match('python') це трохи швидше , ніж re.match('^python'). Це має бути.
Jeyekomon

@Jeyekomon Так, це я мав на увазі, matchфункція трохи швидше, якщо ви хочете шукати на початку рядка (порівняно з використанням searchфункції пошуку слова на початку рядка, re.search('^python', word)наприклад). Але я вважаю це дивним, якщо ви скажете searchфункції пошуку на початку рядка, вона повинна бути такою ж швидкою, як matchфункція.
baptx

31

Ви можете навести нижченаведений приклад, щоб зрозуміти роботу re.matchта повторний пошук

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

re.matchповернеться none, але re.searchповернеться abc.


3
Просто хотілося б додати, що пошук поверне _sre.SRE_Match об'єкт (або None, якщо його не знайдено). Щоб отримати "abc", вам потрібно зателефонувати в t.group ()
SanD,

30

Різниця полягає в тому, що re.match()вводить в оману всіх, хто звик до регулярного збігу виразів Perl , grep або sed , і re.search()ні. :-)

Більш тверезо, як зауважує Джон Д. Кук , re.match()«поводиться так, ніби кожна модель є ^ присудженою». Іншими словами, re.match('pattern')дорівнює re.search('^pattern'). Таким чином, він закріплює ліву сторону візерунка. Але це також не закріплює праву сторону шаблону: це все ще вимагає припинення $.

Відверто кажучи, викладене вище, я вважаю, що re.match()слід знехтувати. Мені було б цікаво знати причини, за якими його слід зберегти.


4
"поводиться так, ніби кожен зразків ^ прикидається". справедливо лише в тому випадку, якщо ви не використовуєте багаторядковий параметр. Правильне твердження - "... має \ попередньо"
JoelFan

14

re.match намагається відповідати шаблону на початку рядка . re.search намагається зіставити візерунок у всьому рядку, поки не знайде збіг.


3

Набагато коротше:

  • search сканування через весь рядок.

  • match сканує лише початок рядка.

Наступний Екс каже:

>>> a = "123abc"
>>> re.match("[a-z]+",a)
None
>>> re.search("[a-z]+",a)
abc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.