Python Normal İfadeleri

Normal ifadeler, metin kalıplarını eşleştirmek için güçlü bir dildir. Bu sayfada, Python alıştırmalarımız için normal ifadelerle ilgili temel bir başlangıç sağlanmakta ve normal ifadelerin Python'da nasıl çalıştığı gösterilmektedir. Python "re" modülü, normal ifade desteği sunar. .

Python'da normal ifade araması genellikle şu şekilde yazılır:

match = re.search(pat, str)

re.search() yöntemi bir normal ifade kalıbı ve bir dize alır ve o kalıbı dize içinde arar. Arama başarılı olursa search() bir eşleşme nesnesi, aksi halde None değerini döndürür. Bu nedenle, 'kelime' kalıbını arayan aşağıdaki örnekte gösterildiği gibi, aramanın başarılı olup olmadığını test etmek için aramanın hemen ardından genellikle bir if deyimi gelir. ve ardından 3 harfli bir kelime yazın (ayrıntılar aşağıdadır):

import re

str = 'an example word:cat!!'
match = re.search(r'word:\w\w\w', str)
# If-statement after search() tests if it succeeded
if match:
  print('found', match.group()) ## 'found word:cat'
else:
  print('did not find')

match = re.search(pat, str) kodu, arama sonucunu "match" adlı bir değişkende depolar. Daha sonra, if-deyimi eşleşmeyi test eder; eğer true ise arama başarılı olmuştur ve match.group() eşleşen metindir (örneğin, "word:cat"). Aksi takdirde, eşleşme yanlışsa (daha ayrıntılı belirtmek gerekirse yok) arama başarılı olmamıştır ve eşleşen metin yoktur.

'r' kalıp dizesinin başında, Python'u "ham" olarak belirtir ve ters eğik çizgilerden geçen ve normal ifadeler için çok kullanışlı olan bir dizedir (Java'nın bu özelliğe çok ihtiyacı var!). Model dizelerini her zaman "r" ile yazmanızı öneririz. Bunu alışkanlık olarak ediniyoruz.

Temel Kalıplar

Normal ifadelerin gücü sadece sabit karakterleri değil, kalıpları da belirtebilmesidir. Tek karakterle eşleşen en temel kalıplar şunlardır:

  • a, X, 9, < sıradan karakterlerle tam olarak eşleşiyor. Özel anlamları olduğu için birbiriyle eşleşmeyen meta karakterler şunlardır: . ^ $ * + ? { [ ] \ | ( ) (ayrıntılar aşağıdadır)
  • . (nokta) -- yeni satır '\n' hariç herhangi bir tek karakterle eşleşir
  • \w -- (küçük w harfi) bir "kelime" ile eşleşir karakter: bir harf, rakam veya alt çubuk [a-zA-Z0-9_]. "Kelime" olan bir kelime bunun hafızasındadır, bir kelimeyle değil, yalnızca tek bir karakterle eşleşir. \W (büyük W harfi), kelime olmayan herhangi bir karakterle eşleşir.
  • \b -- kelime ile kelime olmayan öğeler arasındaki sınır
  • \s -- (küçük harfli s), tek bir boşluk karakteriyle eşleşir: boşluk, yeni satır, dönüş, sekme, form [ \n\t\t\f]. \S (büyük S harfi), boşluk olmayan herhangi bir karakterle eşleşir.
  • \t, \n, \r -- sekme, yeni satır, dönüş
  • \d -- ondalık basamak [0-9] (bazı eski normal ifade yardımcı programları \d desteklememektedir, ancak hepsi \w ve \s destekler)
  • ^ = başlangıç, $ = bitiş -- dizenin başlangıcı veya sonuyla eşleşir
  • \ -- "özelliği" engelle anlamına gelir. Dolayısıyla, örneğin, \ işaretini kullanın. bir noktayla veya bir eğik çizgiyle eşleştirmek için \\ yazın. Bir karakterin "@" gibi özel bir anlamı olup olmadığından emin değilseniz önüne eğik çizgi (\@) koymayı deneyebilirsiniz. \c gibi geçerli bir kaçış dizisi değilse, python programınız bir hatayla durdurulur.

Temel Örnekler

Şaka: Üç gözlü domuza ne denir? çin!

Bir dizedeki kalıp aramasının temel kuralları şunlardır:

  • Arama, dizede başından sonuna kadar ilerler ve bulunan ilk eşleşmede durur
  • Dizenin tamamı değil, kalıbın tamamı eşleşmelidir
  • match = re.search(pat, str) başarılı olursa eşleşme Yok olmaz ve özellikle match.group() eşleşen metin olur
  ## Search for pattern 'iii' in string 'piiig'.
  ## All of the pattern must match, but it may appear anywhere.
  ## On success, match.group() is matched text.
  match = re.search(r'iii', 'piiig') # found, match.group() == "iii"
  match = re.search(r'igs', 'piiig') # not found, match == None

  ## . = any char but \n
  match = re.search(r'..g', 'piiig') # found, match.group() == "iig"

  ## \d = digit char, \w = word char
  match = re.search(r'\d\d\d', 'p123g') # found, match.group() == "123"
  match = re.search(r'\w\w\w', '@@abcd!!') # found, match.group() == "abc"

Tekrar

Kalıpta tekrarı belirtmek için + ve * kullandığınızda işler daha ilginç hale gelir

  • + -- sol tarafında kalıbın 1 veya daha fazla kez tekrarlanması, ör. "i+" = bir veya daha fazla i
  • * -- Sol tarafında kalıbın 0 veya daha fazla sayıda tekrarı
  • ? -- kalıbın solunda 0 veya 1 oluşumla eşleştirme

En soldaki ve En büyük

İlk olarak, arama, kalıp için en soldaki eşleşmeyi bulur ve ikinci olarak, dizenin mümkün olduğunca büyük kısmını kullanmaya çalışır. Diğer bir deyişle, + ve * mümkün olduğunca ilerler (+ ve * işaretlerinin "açılır" olduğu söylenir).

Tekrar örnekleri

  ## i+ = one or more i's, as many as possible.
  match = re.search(r'pi+', 'piiig') # found, match.group() == "piii"

  ## Finds the first/leftmost solution, and within it drives the +
  ## as far as possible (aka 'leftmost and largest').
  ## In this example, note that it does not get to the second set of i's.
  match = re.search(r'i+', 'piigiiii') # found, match.group() == "ii"

  ## \s* = zero or more whitespace chars
  ## Here look for 3 digits, possibly separated by whitespace.
  match = re.search(r'\d\s*\d\s*\d', 'xx1 2   3xx') # found, match.group() == "1 2   3"
  match = re.search(r'\d\s*\d\s*\d', 'xx12  3xx') # found, match.group() == "12  3"
  match = re.search(r'\d\s*\d\s*\d', 'xx123xx') # found, match.group() == "123"

  ## ^ = matches the start of string, so this fails:
  match = re.search(r'^b\w+', 'foobar') # not found, match == None
  ## but without the ^ it succeeds:
  match = re.search(r'b\w+', 'foobar') # found, match.group() == "bar"

E-posta Örneği

'xyz alice-b@google.com mor maymun' dizesinin içinde e-posta adresini bulmak istediğinizi varsayalım. Bunu, diğer normal ifade özelliklerini göstermek üzere çalışan bir örnek olarak kullanacağız. Aşağıda r'\w+@\w+ kalıbını kullanmayı deneyebilirsiniz:

  str = 'purple alice-b@google.com monkey dishwasher'
  match = re.search(r'\w+@\w+', str)
  if match:
    print(match.group())  ## 'b@google'

Bu durumda arama, \w '-' ile eşleşmediği için e-posta adresinin tamamını almaz veya "." içeren bir e-posta alırsınız. Bunu, aşağıdaki normal ifade özelliklerini kullanarak düzeltiriz.

Köşeli Parantezler

Bir karakter grubunu belirtmek için köşeli parantez kullanılabilir. Bu nedenle, [abc] ifadesi "a" ile eşleşir. veya "b" veya "c"yi seçin. \w, \s vb. kodları köşeli parantezler içinde de kullanılır. Ancak nokta (.) yalnızca düz bir nokta anlamına gelir. E-postalar sorunu için, köşeli parantez içine '.' ve "-" @ işaretinin çevresinde r'[\w.-]+@[\w.-]+ kalıbıyla görünebilecek karakter kümesine almak için:

  match = re.search(r'[\w.-]+@[\w.-]+', str)
  if match:
    print(match.group())  ## 'alice-b@google.com'
(Diğer köşeli parantez özellikleri) Bir aralığı belirtmek için kısa çizgi de kullanabilirsiniz; böylece [a-z] tüm küçük harfle eşleşir. Aralık belirtmeden kısa çizgi kullanmak için tireyi en sona koyun (ör. [abc-]. Köşeli parantez grubunun başındaki şapka (^) bunu ters çevirir. Dolayısıyla [^ab], "a" dışındaki herhangi bir karakteri ifade eder veya "b"yi seçin.

Grup Ayıklama

"Grup" özelliği, eşleşen metinden bölümleri seçmenize olanak tanır. E-posta sorunu için kullanıcı adını ve ana makineyi ayrı ayrı çıkarmak istediğimizi varsayalım. Bunu yapmak için, kalıpta kullanıcı adının ve ana makinenin etrafına parantez ( ) ekleyin. Örneğin: r'([\w.-]+)@([\w.-]+)'. Bu durumda parantezler, kalıbın nasıl eşleşeceğini değiştirmez, bunun yerine mantıksal "gruplar" oluşturur kullanabilirsiniz. Başarılı bir aramada, match.group(1) 1. sol paranteze, match.group(2) 2. sol paranteze karşılık gelen metindir. Düz match.group(), her zamanki gibi hâlâ tam eşleşme metnidir.

  str = 'purple alice-b@google.com monkey dishwasher'
  match = re.search(r'([\w.-]+)@([\w.-]+)', str)
  if match:
    print(match.group())   ## 'alice-b@google.com' (the whole match)
    print(match.group(1))  ## 'alice-b' (the username, group 1)
    print(match.group(2))  ## 'google.com' (the host, group 2)

Normal ifadelerdeki yaygın bir iş akışı, aradığınız şey için bir kalıp yazmak, istediğiniz bölümleri çıkarmak için parantez grupları eklemektir.

bul

findall(), muhtemelen re modülündeki en güçlü tek işlevdir. Yukarıda, bir modele ilişkin ilk eşleşmeyi bulmak için re.search() işlevini kullandık. findall(), *tüm* eşleşmeleri bulur ve bunları, her dize bir eşleşmeyi temsil eden bir dize listesi olarak döndürür.
  ## Suppose we have a text with many email addresses
  str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher'

  ## Here re.findall() returns a list of all the found email strings
  emails = re.findall(r'[\w\.-]+@[\w\.-]+', str) ## ['alice@google.com', 'bob@abc.com']
  for email in emails:
    # do something with each found email string
    print(email)

Files ile tümünü bul

Dosyalarda, dosyanın satırlarını tekrarlamak için bir döngü yazma alışkanlığınız olabilir ve daha sonra, her satıra findall() işlevini çağırabilirsiniz. Bunun yerine, iterasyonu sizin için findall() tarafından gerçekleştirilsin. Çok daha iyi olur. Tüm dosya metnini findall() içine gönderin ve tüm eşleşmelerin listesini tek bir adımda döndürmesini bekleyin (f.read() işlevinin bir dosyanın tüm metnini tek bir dizede döndürdüğünü unutmayın):

  # Open file
  f = open('test.txt', encoding='utf-8')
  # Feed the file text into findall(); it returns a list of all the found strings
  strings = re.findall(r'some pattern', f.read())

tümünü bul ve Gruplar

Parantez ( ) grup mekanizması, findall() ile birleştirilebilir. Kalıp 2 veya daha fazla parantez grubu içeriyorsa findall(), bir dize listesi döndürmek yerine *tuples* listesini döndürür. Her unsur, kalıbın bir eşleşmesini temsil eder ve dilimin içinde grup(1), grup(2) .. verileri bulunur. Dolayısıyla, e-posta kalıbına 2 parantez grubu eklenirse findall(), her biri kullanıcı adını ve ana makineyi içeren 2 uzunlukları (ör. ('alice', 'google.com').

  str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher'
  tuples = re.findall(r'([\w\.-]+)@([\w\.-]+)', str)
  print(tuples)  ## [('alice', 'google.com'), ('bob', 'abc.com')]
  for tuple in tuples:
    print(tuple[0])  ## username
    print(tuple[1])  ## host

Tüplerin listesini oluşturduktan sonra, her eleman için hesaplamalar yapmak üzere döngüye alabilirsiniz. Kalıpta parantez yoksa findall(), önceki örneklerde olduğu gibi bulunan dizelerin bir listesini döndürür. Kalıp tek bir parantez grubu içeriyorsa findall(), bu tek gruba karşılık gelen dizelerin bir listesini döndürür. (İsteğe bağlı özelliğin belirsiz olması: Bazen kalıpta parantez ( ) gruplamalarınız olur, ancak bunları çıkarmak istemezsiniz. Bu durumda parantezleri başında ?: karakteriyle yazın, ör. (?: ) olduğunda bu sol parantez bir grup sonucu olarak sayılmaz.)

RE İş Akışı ve Hata Ayıklama

Normal ifade kalıpları yalnızca birkaç karakterde birçok anlama sahiptir ancak bu kalıplar o kadar yoğun oldukları için örüntülerinizi ayıklamak için çok fazla zaman harcamanız gerekebilir. Çalışma zamanınızı ayarlayarak, bir kalıp çalıştırabilmeniz ve eşleştiğini kolayca yazdırabilmeniz için çalışma zamanınızı ayarlayın (örneğin, küçük bir test metninde çalıştırıp findall() sonucunu yazdırarak). Kalıp hiçbir şeyle eşleşmezse, çok fazla eşleşme elde etmek için kalıbı zayıflatmayı ve bazı kısımlarını kaldırmayı deneyin. Hiçbir şeyle eşleşmediğinde, bakılacak somut bir şey olmadığından herhangi bir ilerleme kaydedilemez. Eşleşme çok fazla olduğunda, tam istediğiniz sonucu elde etmek için performansınızı adım adım daraltmaya çalışabilirsiniz.

Seçenekler

Yeniden işlevleri, kalıp eşleşmesi davranışını değiştirmek için çeşitli seçenekler alır. Seçenek işareti, search() veya findall() vb. işlevlerine fazladan bir bağımsız değişken olarak eklenir, ör. re.search(pat, str, re.IGNORECASE).

  • IGNORECASE -- eşleştirmede büyük/küçük harf farklarını dikkate almayın, dolayısıyla 'a' her iki 'a' ile de eşleşir ve "A"yı seçin.
  • DOTALL -- nokta (.) ifadesinin yeni satırla eşleşmesine izin verir. Normalde bu, yeni satır dışındaki her şeyle eşleşir. Bu sizi yanıltabilir. Sizce .* her şeyle eşleşir, ancak varsayılan olarak bir satırın sonunu geçmez. \s (boşluk) karakterinin yeni satır içerdiğini unutmayın. Dolayısıyla, yeni satır içerebilecek bir boşluk çalıştırmak isterseniz \s* kullanabilirsiniz
  • ÇOK SATIRLI -- Çok sayıda satırdan oluşan bir dizede, ^ ve $ karakterlerinin her satırın başlangıcı ve sonuyla eşleşmesine izin verin. Normalde ^/$, tüm dizenin başlangıcı ve sonuyla eşleşir.

Açgözlü ve Açgözlü Olmayan (isteğe bağlı)

Bu, alıştırmalar için gerekli olmayan, daha gelişmiş bir normal ifade tekniğini gösteren isteğe bağlı bölümdür.

Etiket içeren bir metniniz olduğunu varsayalım: <b>foo</b> ve <i>bu şekilde devam ediyor</i>.

Her bir etiketi '(<.*>)' kalıbıyla eşleştirmeye çalıştığınızı varsayalım Peki ilk olarak neyle eşleşiyor?

Sonuç biraz şaşırtıcı olsa da .* öğesinin açgözlü yönü ile '<b>foo</b>'un bütünüyle eşleşmektedir. ve <i>böyle devam</i>' tek bir büyük maç gibi düşünebilirsiniz. Sorun, .* işaretinin ilk başta durmak yerine mümkün olduğu kadar ileri gitmesidir > ("açıcı" olarak da bilinir).

Normal ifadede ? .*? veya .+? ile değiştirin. Artık mümkün olan en kısa sürede dururlar. Dolayısıyla, '(<.*?>)' kalıbı yalnızca '<b>' ve '</b>' olarak alır ve bu şekilde her bir <..> sırayla eşlenir. Stil genellikle bir .*? hemen ardından, .*? çalışma süresi uzatılır.

*? uzantısı Perl'den gelir ve Perl'in uzantılarını içeren normal ifadeler, Perl Uyumlu Normal İfadeler (pcre) olarak bilinir. Python, pcre desteğini içerir. Birçok komut satırı yardımcı programını vb. pcre kalıplarını kabul ettiklerini gösteren bir işaret bulunur.

"X'te durma dışında tüm bu karakterler" fikrini kodlamak için eski ancak yaygın olarak kullanılan bir teknik köşeli ayraç stilini kullanıyor. Yukarıdaki için kalıbı yazabilirsiniz, ancak tüm karakterleri almak için .* yerine [^>]* ifadesini kullanın. Bu işlem, aynı olmayan tüm karakterleri atlar. (baştaki ^, köşeli parantez kümesini "ters çevirir", böylece köşeli parantez içinde olmayan herhangi bir karakterle eşleşir).

Değişken değişikliği (isteğe bağlı)

re.sub(pat, replace, str) işlevi, belirli bir dizedeki tüm kalıp örneklerini arar ve değiştirir. Yeni dize "\1", "\2" içerebilir Bunlar, orijinal eşleşen metindeki grup(1), grup(2) vb.deki metne başvuruda bulunur.

Aşağıdaki örnekte, tüm e-posta adresleri aranır ve bunları kullanıcıyı (\1) tutacak şekilde değiştirirken barındırıcı olarak yo-yo-dyne.com kullanılır.

  str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher'
  ## re.sub(pat, replacement, str) -- returns new string with all replacements,
  ## \1 is group(1), \2 group(2) in the replacement
  print(re.sub(r'([\w\.-]+)@([\w\.-]+)', r'\1@yo-yo-dyne.com', str))
  ## purple alice@yo-yo-dyne.com, blah monkey bob@yo-yo-dyne.com blah dishwasher

Antrenman

Normal ifadelerle ilgili alıştırma yapmak için Bebek Adları Alıştırması'nı inceleyin.