3 puan yazan GN⁺ 2024-03-21 | 1 yorum | WhatsApp'ta paylaş
  • Python re içindeki $, çok satırlı mod kapalı olsa bile yalnızca dizgenin sonu ile değil, sondaki son satır sonu karakterinden önceki konum ile de eşleşebilir
  • ^ “dizgenin başlangıcı” gibi görünüyor diye $’ın da tamamen simetrik çalıştığını varsaymamak gerekir; gerçek anlamı düzenli ifade gerçeklemelerine göre değişir
  • "cat\n" için $, \z, \Z sonuçları PHP, ECMAScript, Python, Go, Java 8, .NET 7.0 ve Rust arasında farklıdır; Python’daki \z ise Python 3.14 ile yeni eklenmiştir
  • Sondaki satır sonu karakterine izin veriliyorsa, çok satırlı moddaki $ tabloda yer alan tüm platformlarda "cat\n" ile eşleşir; ancak satır sonu hariç son ile eşleşmek isteniyorsa sözdizimi seçimi değişir
  • Son satır sonu karakteriyle eşleşmemesi gerekiyorsa çoğu platformda \z kullanılmalı; Python 3.14 öncesinde ve ECMAScript’te ise ayrı alternatifler düşünülmelidir

Python re içinde $’ın eşleştiği konum

  • Python düzenli ifade modülü re içinde $, çok satırlı mod kapalı olsa bile dizgenin sonu ya da dizge sonundaki son satır sonu karakterinin hemen öncesiyle eşleşebilir
  • cat$, "lolcat" ile eşleşir ve "internet cat video" ile eşleşmez; bu yüzden basit görünür, ancak sonda satır sonu bulunan "cat\n" gibi durumlarda sonuç beklenenden farklı olabilir
  • re.MULTILINE belirtildiğinde $, dizgenin sonu ve her satırın sonu, yani her satır sonu karakterinin hemen öncesiyle eşleşir
  • Varsayılan durumda da $ dizgenin sonuyla eşleşir; dizgenin sonunda satır sonu karakteri varsa, o karakterin hemen öncesiyle de eşleşir

Son satır sonu karakterini hariç tutarak eşleştirmek

  • Yalnızca dizgenin sonunu katı biçimde eşleştirmek için $ tek başına yetersiz kalabilir; \z ve \Z son ankrajı adaylarıdır
  • Python düzenli ifade belgeleri ve başka bir düzenli ifade sözdizimi açıklaması temel alındığında, gerçeklemelere göre \z ve \Z desteği ve anlamı değişir
  • "cat\n" için farklar şöyledir
    • PHP: "cat$" çok satırlı olup olmamasından bağımsız olarak eşleşir, "cat\z" eşleşmez, "cat\Z" eşleşir
    • ECMAScript: çok satırlı "cat$" eşleşir, çok satırlı olmayan "cat$" eşleşmez; \z ve \Z desteklenmez
    • Python: "cat$" çok satırlı olup olmamasından bağımsız olarak eşleşir; "cat\z" ve "cat\Z", "cat\n" ile eşleşmez
    • Go ve Rust: çok satırlı "cat$" eşleşir; çok satırlı olmayan "cat$" ve "cat\z" eşleşmez; \Z desteklenmez
    • Java 8 ve .NET 7.0: "cat$" çok satırlı olup olmamasından bağımsız olarak eşleşir, "cat\z" eşleşmez, "cat\Z" eşleşir
  • Python’daki \z, Python 3.14’te yeni eklendi; önceki sürümlerde desteklenmiyordu
  • Sondaki satır sonu karakterine izin veriliyorsa, çok satırlı moddaki $ tablodaki tüm platformlarda tutarlı biçimde "cat\n" ile eşleşir
  • Sondaki satır sonu karakteriyle eşleşmemek isteniyorsa çoğu platformda \z kullanılmalı; Python 3.14 öncesinde \Z, ECMAScript’te ise çok satırlı olmayan $ kullanılmalıdır
  • Tablodaki veriler regex101.com üzerinden toplanmıştır; gerçek çalışma zamanlarında test edilmemiştir

1 yorum

 
GN⁺ 2024-03-21
Hacker News yorumları
  • Eskiden beri ^ işaretini “satırın başlangıcı”, $ işaretini ise “satırın sonu” olarak düşünürdüm
    Düzenli ifadelerle uğraşırken metni çoğu zaman satır satır işlediğimiz için sonuçlar sık sık aynı olur, ama o operatörü zihnimde canlandırma biçimim hâlâ “dize”den çok “satır”a yakın
    Muhtemelen düzenli ifadelerle grep üzerinden tanışmamın etkisi büyük; girdiği dize değil satır olarak görme alışkanlığı oluşmuş gibi

    • Ben de başlığı görünce “Elbette değil, bunu nereden duymuş ki?” diye düşündüm
      Neredeyse 20 yıldır düzenli ifade kullanıyorum ama $ için dizenin sonu dendiğini ilk kez duyuyor gibiyim; onu hep satırın sonu olarak görmüştüm
    • Yazıda ^ işaretine “dizenin başlangıcı” denmesi gözüme takılıyor
      Aslında $ nasıl “satırın sonu”ysa ^ de “satırın başlangıcı”; dizenin başlangıcı \A, dizenin sonu ise daha çok \Z gibi görünüyor
    • Ben de öyle düşünüyordum ama Perl’de bizzat deneyince $ varsayılan olarak dizenin sonuna yönelik pozitif ileriye bakış koşulu gibi davranıyor
      Satır sonu karakterini eşleştirip tüketmiyor
      Yalnızca çok satırlı modda satır sonu konumuyla eşleşiyor, ama o zaman da tüketmiyor gibi
      Gerçekten de $ kullanarak bir satırın son karakterini yakalayıp satır sonunu tükettikten sonra sonraki satırın ilk karakterini yakalayan bir düzenli ifade oluşturamadım; yakalama grubu doğrudan $ konumunda bitiyor
    • Bende bu algıyı grepten çok Vim yerleştirdi
  • POSIX düzenli ifadeleri ile Python düzenli ifadeleri farklıdır
    Genel olarak düzenli ifade söz dizimi evrensel değildir; bu yüzden kullandığınız gerçekleştirimin belgelerine bakmak gerekir
    POSIX 9. bölüme göre düzenli ifadeler dizeler üzerinde çalışır, ancak bazı araçlar işlemeyi satır bazında sınırlar
    Ayrıca $, eşleşme hedefi dizenin sonuna sabitlenen bir çapa olarak tanımlanır; dolayısıyla $nin dize sonunu mu satır sonunu mu ifade edeceğine nihayetinde araç ya da mod karar verir
    grep, sed, awk, Python gibi yaygın araçlar varsayılan olarak satır bazında çalıştığı için genellikle satır sonu olarak ele alır
    Tek bir evrensel düzenli ifade söz dizimi yoktur
    Hangi dili ve seçenekleri kullandığınızı bilmeden bir düzenli ifadeyi güvenilir biçimde okumak ya da yazmak mümkün değildir
    https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1...

  • Bu konu, Robert Elder’ı bilmeyenlere tanıtmak için tam uygun
    YouTube’da ve blogunda iyi içerikler üretiyor; düzenli ifade serisinde de çeşitli araçların uyguladığı düzenli ifade davranışları arasındaki farkları epey derinlemesine inceliyor
    Son videosu da iyi: https://www.youtube.com/watch?v=ys7yUyyQA-Y
    HN okurlarının ilgisini çekebilecek çok içeriği var; danışmanlığın gerçekleri ve sıkıntıları gibi konulara da değiniyor
    https://www.youtube.com/@RobertElderSoftware
    https://blog.robertelder.org/
    https://blog.robertelder.org/regular-expressions/
    https://www.youtube.com/watch?v=cK87ktENPrI

  • Perl öğrenirken düzenli ifadeler gerçekten içselleştirdiğim ilk şeylerden biriydi; bugün bile “Camel” kitabı sayesinde Perl zihnimin bir köşesinde rahat bir yer tutuyor
    Şu an en önemli bilgi gerçekleştirmeden gerçekleştirmeye değiştiği; bu yüzden bir şey üzerinde çalışırken ilgili başvuru tablosunu açıp bakma alışkanlığım oluştu
    Örneğin Emacs düzenli ifadeleri \w biçimindeki sözcük karakterlerini desteklemiyor, \s_- benzeri karakter sınıfları kullanmak gerekiyor; bu can sıkıcı, ama Emacs’ın belgelendirme ve keşfedilebilirlik konusunda en iyisi olduğunu düşünüyorum
    Bazı araçlarda parantezleri kaçışlamak gerekir, bazılarında gerekmez; bu davranışın yapılandırılabildiği durumlar da var, yapılandırılamadığı durumlar da
    Kafa karışıklığı, sinirlenme ve inkâr aşamalarının hepsinden geçtim; artık sadece kabulleniyorum
    Kavramlar her yerde aynı, ama lehçeler değişiyor

    • Kafam Perl düzenli ifadeleri ile düşünüyor, sonra bunu kullandığım dilin tutarsız kısımlarına uyarlayıp çeviriyorum
      Özellikle kabukta sed/grep/awkın GNU mu BSD mi olduğunu aklımda tutmaktansa, boru hattına perl eklemeyi çok daha sık yapıyorum
    • Bunu nasıl içselleştirdiğini merak ediyorum
      Perl, klavyenin üstünde bir kedi yürümüş gibi görünüyor
  • Bir sürü kötü işe alım yöneticisinin “Düzenli ifadede dizenin sonunu nasıl eşleştirirsiniz?” sorusunu tuzak soru listesine eklediğini duyar gibiyim

  • Düzenli ifadelerle ilgili bir listeden Perl’ü çıkarmak tuhaf
    perlre belgesinde $ şöyle açıklanıyor: dizenin sonuyla eşleşir; ya da dizenin sonundaki satır sonundan önceyle eşleşir; /m kullanılırsa herhangi bir satır sonundan önceyle eşleşir

    • Düzenli ifadelerle en güçlü biçimde ilişkilendirilen dil denebilecek Perl’ün atlanması epey büyük bir eksik gibi görünüyor
      Bu da herhalde Perl’ün bugünlerde ne kadar gündem dışına itildiğini gösteriyor
  • Raku, eski adıyla Perl 6, ^ ve $ karakterlerini dizenin başlangıcı/sonu olarak belirleyip, satır başlangıcı/sonu için ^^ ve $$ getirdi
    Çok satırlı mod yok ve buna ihtiyaç da yok
    \h yatay boşluk, \v ise dikey boşluk anlamına da geliyor
    Tamamen yeniden düşünülüp yeniden yazılması sayesinde, eski davranışın insanları şaşırttığı gerçeğinden ders çıkarabilmiş olmanın avantajı bu

    • Bu yüzden bu inatçı kişi Perl 6 kullanamıyor
      On yıllar boyunca öğrenilmiş satır gürültüsü gibi sözdiziminin rastgele karıştırılmış hali gibi geliyor
      Netlik açısından varsayılanların tam tersi olması gerekirdi
      ^ ve $ satırlar için, ^^ ve $$ dizeler için kullanılsaydı daha doğal olurdu
      Çünkü ^^line1$\n^line2$\n^line3$\n$ gibi görünüyor
      Üstelik Perl 6 her yerde yok ama Perl 5 her yerde var
    • Ben olsam tam tersini seçerdim
      ^^, ^ işaretinden daha “başlangıç gibi” görünüyor
    • Yazdığım regex’lerin neredeyse tamamı dizenin başlangıcı/sonu varsayımına dayanıyordu
      Çünkü genellikle bir satırı regex’e verip işliyorsunuz; bu yüzden tekil ^ ve $ işaretlerini tüm dize için kullanma tercihi, bir ölçüde geriye dönük uyumluluğu koruyor
  • Regex’in standartlaştığını düşünen var mı, merak ediyorum
    Yeni bir ortama geçtiğim her seferinde hep yeniden öğrenmem gerekti

    • Bir noktada tüm lehçeleri bildiğimi hissetmiştim
      Elbette daha fazla regex lehçesi vardır ama karşıma çıkmıyorlar; bildiklerimle çoğu işi çözebiliyorum
      Kiralık araba kullanmaya benziyor
      Kendi arabamdan biraz farklı hareket ediyor, eksik ve eklenmiş özellikleri var ama genel olarak çoğu oldukça benzer
    • ISO/IEC 14882 C++ standart kütüphanesi, fiilî yasal standart sayılabilecek altı regex sözdiziminin uygulanmasını zorunlu kılar: IEEE Std 1003.1-2008, yani POSIX’in BRE, ERE, awk, grep, egrep’i ve ECMA-262 EcmaScript 3
      Bu yüzden en azından ben regex’in yayımlanmış birden fazla resmî standart ile standartlaştırıldığını düşünüyorum
      https://open-std.org/jtc1/sc22/…
      https://pubs.opengroup.org/onlinepubs/9699919799/…
      https://262.ecma-international.org/14.0/…
    • Bildiğim büyük kollar POSIX, Perl/PCRE ve Go tarafında kullanılan RE2 kadar
      JavaScript dahil birçok sistem PCRE’yi uyguladı; çünkü Perl, POSIX sistemine çok sayıda kullanışlı genişletme ekledi
      Hatırladığım kadarıyla RE2, mevcut sistemlerin performans sorunlarını ve tuhaf davranışlarını bastırmaya yönelikti; tamamının Go ile uygulandığını sanıyordum
      Sonradan bakınca RE2’nin Go’dan önce çıktığını bilmiyordum
    • Perl’den sonra çıkan diller genel olarak Perl regex sözdiziminin bir varyantını kullanıyor ama her zaman küçük farklar var
      Yine de $ işaretinin anlamı ve çok satırlı moda geçme biçimi genellikle tutarlı sayılır
    • İlginçtir, RFC 9485 https://datatracker.ietf.org/doc/rfc9485/ “I-Regexp: An Interoperable Regular Expression Format” geçen yılın ekim ayında yeni yayımlandı
  • İnsanlar dize ile satırı karıştırıyor
    Dize, karakterlerden oluşan bir dizidir; satır ise iki şekilde görülebilir
    Satır sonunu satır sonlandırıcısı olarak görürseniz, satır; satır sonu olmayan 0 veya daha fazla karaktere bir satır sonunun eklenmiş halidir ve sonunda satır sonu yoksa tam bir satır değildir
    POSIX bu bakış açısını kullanır
    Satır sonunu satır ayırıcısı olarak görürseniz, satır; satır sonu olmayan 0 veya daha fazla karakterden oluşan bir dizidir
    Her iki durumda da satırın içeriği satır sonundan önce biter
    ^ ve $ işaretlerinin semantiği, tek satır modunda da çok satır modunda da satır tabanlıdır
    Dize tabanlı semantik için, dosya işleniyorsa tüm dosya semantiği denebilecek durumlarda, \A ve \Z ya da bunların karşılığı kullanılmalıdır
    Her iki yorumun da avantajları var
    Metin seri bağlantı üzerinden iletilirken satır sonunu satır sonlandırıcısı yapmak, tam bir satır alıp almadığınızı anlamayı kolaylaştırır
    Metin dosyalarında satır sonunu satır ayırıcısı olarak görmek, son satırın hatalı durumda olmamasını sağladığı için kullanışlı olabilir; ancak satır sonlandırıcısı kullanmak, eksik yazılmış bir satırı tespit etmeyi sağlar

  • Bu yüzden Ruby tabanlı uygulamalarda birkaç kez ciddi bug çıktı
    Her zaman \A\z kullanılmalı
    https://homakov.blogspot.com/2012/05/saferweb-injects-in-var...
    https://sakurity.com/blog/2015/02/28/openuri.html
    https://sakurity.com/blog/2015/06/04/mongo_ruby_regexp.html