Nasıl veya neden `. *? 'Kullanmak`. * `Den daha iyidir?


10

Ben cevap Superuser bu soruyu bir çıkış grepping ederken kullanılan Düzenli ifadelerin tür ilişkin bir şeydi.

Verdiğim cevap şuydu:

 tail -f log | grep "some_string.*some_string"

Ve sonra, cevabımın üç yorumunda @Bob şunu yazdı:

.*açgözlüdür ve istediğinizden daha fazlasını yakalayabilir. .*?genellikle daha iyidir.

Sonra bu,

?Bir değiştirici *yerine açgözlü varsayılan o tembel hale. PCRE varsayımı.

Google'a gittim PCRE, ancak cevabımda bunun önemi nedir?

ve son olarak,

Ayrıca bu bir kabuk glob değil, normal ifade (varsayılan olarak POSIX normal ifade yapıyor grep) olduğunu belirtmek gerekir.

Sadece Regex'in ne olduğunu ve grep komutunda çok temel kullanımını biliyorum. Bu 3 yorumdan hiçbirini alamadım ve şu soruları aklımda tutuyorum:

  • Kullanımındaki farklar nelerdir .*?vs. .*?
  • Hangisi daha iyi ve hangi koşullar altında? Lütfen örnekler veriniz.

Ayrıca yorumları anlamak yararlı olurdu, eğer kimse olabilir


GÜNCELLEME: Regex'in Shell Globs'tan farkı nedir? @Kusalananda sağlanan bu bağlantıyı onun yorumunda.

NOT: Gerekirse , bağlama başvurmadan önce cevaplamadan önce lütfen bu soruya verdiğim cevabı okuyun .


Bu çok farklı iki soru. İlk soru unix.stackexchange.com/questions/57957/… tarafından yanıtlanırken , ikinci soru kalıbın uygulanmasına bağlıdır (her koşulda "daha iyi" olduğu söylenemez).
Kusalananda

Sen olabilir düzenleme sadece yaklaşık olarak bu soruyu .*vs .*?sorunu. "Normal ifadeler ve kabuk globları arasındaki fark" sorusu bu sitede zaten ele alınmıştır.
Kusalananda

Yanıtlar:


7

Ashok.* ve arasındaki farkı zaten belirtti.*? , bu yüzden sadece bazı ek bilgiler vereceğim.

grep (GNU sürümünü varsayarak) dizeleri eşleştirmek için 4 yolu destekler:

  • Sabit teller
  • Temel düzenli ifadeler (BRE)
  • Genişletilmiş düzenli ifadeler (ERE)
  • Perl uyumlu düzenli ifadeler (PCRE)

grep varsayılan olarak BRE kullanır.

BRE ve ERE, POSIX'in Düzenli İfadeler bölümünde belgelenmiştir ve PCRE, resmi web sitesinde belgelenmiştir . Özelliklerin ve sözdiziminin uygulamalar arasında değişiklik gösterebileceğini lütfen unutmayın.

Ne BRE ne de ERE'nin tembelliği desteklemediğini belirtmek gerekir :

Birden çok bitişik çoğaltma sembolünün ('+', '*', '?' Ve aralık) davranışı tanımlanmamış sonuçlar üretir.

Bu özelliği kullanmak istiyorsanız, bunun yerine PCRE'yi kullanmanız gerekir:

# BRE greedy
$ grep -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# BRE lazy
$ grep -o 'c.*\?s' <<< 'can cats eat plants?'
can cats eat plants

# ERE greedy
$ grep -E -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# ERE lazy
$ grep -E -o 'c.*?s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats

Düzenle 1

.*Vs hakkında biraz açıklama yapabilir misiniz .*??

  • .*"en uzun" 1 kalıbı eşleştirmek için kullanılır .

  • .*?"en kısa" 1 kalıbı eşleştirmek için kullanılır .

Deneyimlerime göre, en çok aranan davranış genellikle ikincisidir.

Örneğin, aşağıdaki dizeye sahip olduğumuzu ve aralarındaki içeriği değil, yalnızca html etiketlerini 2 eşleştirmek istediğimizi varsayalım :

<title>My webpage title</title>

Şimdi karşılaştırmak .*vs .*?:

# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>

# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>

1. Normal ifade bağlamında "en uzun" ve "en kısa" ifadelerinin anlamı Kusalananda'nın işaret ettiği gibi biraz zor . Daha fazla bilgi için resmi belgelere bakın.
2. HTML'yi regex ile ayrıştırmanız önerilmez . Bu sadece eğitim amaçlı bir örnektir, üretimde kullanmayın.


.*Vs hakkında biraz açıklama yapabilir misiniz .*??
C0deDaedalus

@ C0deDaedalus Güncellendi.
nxnev

10

Diyelim ki şöyle bir dize aldım:

can cats eat plants?

Açgözlü c.*skullanımı, başlangıç cve bitiş ile birlikte tüm dize ile eşleşir, saçgözlü bir operatör olarak, s'nin son oluşumuna kadar eşleşmeye devam eder.

Oysa tembel c.*?skullanımı sadece ilk sdizisi olana kadar dize eşleşene kadar eşleşir can cats.

Yukarıdaki örnekten, bunu toplayabilirsiniz:

"Açgözlü", mümkün olan en uzun dizeyle eşleşen anlamına gelir. "Tembel", mümkün olan en kısa dizeyle eşleşen anlamına gelir. Bir ekleme ?gibi niceleyicinin için *, +, ?veya {n,m}markaların da tembel.


1
"Mümkün olan en kısa" olurdu cats, bu yüzden bu anlamda "mümkün olan en kısa" ı zorunlu kılmaz.
Kusalananda

3
@Kusalananda doğru, bu anlamda kesinlikle değil ama "mümkün olan en kısa" burada hem c hem de s'nin ilk oluşumu arasındadır.
Ashok Arora

1

Bir dize birkaç yolla eşleştirilebilir (basitten karmaşığa):

  1. Statik bir dize olarak (var = 'Merhaba Dünya!'):

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. Bir glob olarak:

    echo ./* # pwd içindeki tüm dosyaları listeler .
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    Temel ve genişletilmiş globlar vardır. caseÖrneğin bazik globs kullanın. Bash [[örneğinde genişletilmiş globlar kullanılır. İlk dosya eşleşmesi temel olabilir veya extglobbash ayarında bazı kabuklarda genişletilebilir . Bu durumda ikisi de aynıdır. Grep glob kullanamadı.

    Bir küredeki yıldız işareti, normal ifadedeki yıldız işaretinden farklı bir şey anlamına gelir :

    * matches any number (including none) ofherhangi bir karakter .
    * matches any number (including none) of theönceki eleman .

  3. Temel bir düzenli ifade olarak (BRE):

    echo "$var" | sed 's/W.*d//' # print: Merhaba!
    grep -o 'W.*d' <<<"$var" # print Dünya!

    (Temel) mermi veya awk'de BRE yoktur.

  4. Genişletilmiş düzenli ifadeler (ERE):

    [[ "$var" =~ (H.*l) ]] # match: Merhaba Dünya
    echo "$var" | sed -E 's/(d|o)//g' # print: Cehennem Wrl!
    awk '/W.*d/{print $1}' <<<"$var" # print: Merhaba
    grep -oE 'H.*l' <<<"$var" # print: Merhaba Worl

  5. Perl Uyumlu Düzenli İfadeler:

    grep -oP 'H.*?l # print: Hel

Sadece PCRE'de a'nın *?belirli bir sözdizimi anlamı vardır.
Yıldız işaretini tembel yapar (ungreedy): Açgözlülük yerine tembellik .

$ grep -oP 'e.*l' <<<"$var"
ello Worl

$ grep -oP 'e.*?l' <<<"$var"
el

Bu buzdağının sadece görünen kısmı, açgözlü, tembel ve uysal ya da iyelik var . Ayrıca ileri ve geri görüş var ama bunlar yıldız işareti için geçerli değil *.

Açgözlü olmayan normal ifadeyle aynı etkiyi elde etmenin bir alternatifi vardır:

$ grep -o 'e[^o]*o' <<<"$var"
ello

Fikir çok basit: nokta kullanmayın ., eşleşecek sonraki karakteri yok edin [^o]. Bir web etiketi ile:

$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>

Yukarıdaki tüm @Bob 3 yorum tamamen açıklığa kavuşturmak gerekir. alıntı yapan:

  • A. * yaygın bir normal ifade, bir glob değil.
  • Yalnızca normal ifade PCRE uyumlu olabilir.
  • PCRE'de: a? * nicelik belirtecini değiştirin. .*açgözlü .*?değil.

Sorular

  • Kullanımındaki farklılıklar nelerdir. ? vs. . ?

    • A .*?yalnızca PCRE sözdiziminde geçerlidir.
    • A .*daha taşınabilir.
    • Açgözlü olmayan bir eşleşmeyle aynı etki, noktayı reddedilen bir karakter aralığıyla değiştirerek yapılabilir: [^a]*
  • Hangisi daha iyi ve hangi koşullar altında? Lütfen örnekler veriniz.
    Daha iyi? Hedefe bağlı. Daha iyisi yoktur, her biri farklı amaçlar için yararlıdır. Yukarıda birkaç örnek verdim. Daha fazlasına mı ihtiyacınız var?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.