Perl Öğreniyoruz - 5

0
cayfer
1. bölüm 2. bölüm 3. bölüm 4. bölüm
regexp'ler (Regular Expressions)Sözlüğe bakınca
regular: düzenli, muntazam, kurallı, kurallara uygun ve düzenli
expression: deyim, ifade, tabir, anlatım ve dışavurum
karşılıklarını buldum. Bence ingilizce Regular Expression sözcükleri de bu bölümde anlatacağım konuyu tam olarak anlatamıyor; çünkü aslında kavram adını ABD'li matematikçi Kleen'in Düzenli Kümeler Cebiri (Algebra of Regular Sets) üzerindeki çalışmalarından alıyor.

Regular Expressions sözcüklerinin tam çevirisi olan "düzenli deyimler" sözcükleri de hiç hoşuma gitmedi… O nedenle bu bölümde anlatacağım kavrama regexp'ler diyeceğim. Böylece ne ingilizce ne de bir başka dilde olacak; aynı bit sözcüğü gibi.

Program yazdığınızı ve bir karakter dizisi içinde "A" harfi olup olmadğını kontrol etme gereksinimini hissettiğinizi düşünün.

a_varmi = index("A", satir)
a_varmi = instr(satir, "A") 
falan gibi bir komut kullanırdınız. Değil mi?

Peki, "A" veya "a" var mı? diye merak etseydiniz?
Ya Harf var mı?" diye merak etseydiniz...

Yazacağınız klasik kod satırları artardı. Oysa "Regular Expression" kullanma olanağı olan bir programlama dili, örneğin Perl kullanıyor olsaydınız

    if ( $satir=~ /A/ ) { . . . }
    if ( $satir=~ /A/i ) { . . . }
    if ( $satir=~ /[a-z]/i ) { . . . }
gibi komut satırları yazabilirdiniz.

Bunların ilki, $satir değişkeninin değeri içinde büyük "A" harfi var mi? diye kontrol ediyor.
İkincisi, $satir içinde büyük ya da küçük "A" harfi var mı? diye kontrol ediyor. (Sondaki "i" "ignore case" anlamında.)
Üçüncüsü ise $satir içinde "a" ve "z" arasında bir karakter var mi? (büyük küçük ayırımı yapmaksızın) diye kontrol ediyor.

Regexp'ler bir karakter dizisinin bir kalıba uyup uymadığını kontrol etmek gerektiği zaman kullanışlıdır. Eşitlik kontrolu için de kullanılabilmekle beraber, eşitlik kontrolu için "if a == b" gibi bir yapı daha kullanışlı olacaktır.

Diyelim ki bir karakter dizisinin "0312-2345678" gibi alan kodu sonra bir eksi işareti sonra da rakamlardan oluşan formata uygun olup olmadığını kontrol etmek istiyorsunuz.

Bunun için

    if ( $dizi=~ /d*-d*/ ) 
regexp'ini kullanabilirsiniz. Bu regexp'de "d" ve "*" meta-karakterlerdir. Yani dizi içinde "", "d" harfi ve "*" karakterinin bulunup bulunmadığını kontrol etmezler.

d meta-karakteri tüm rakamlara ( [0-9], yani "digit" ) uyar. * meta-karakteri ise "sıfır veya daha fazla tekrarlayan" diye okunur.

Bu durumda "0312-2345678" /d*-d*/ kalıbına uyacaktır. Yani "bir miktar rakam; sonra bir eksi işareti; sonra da gene bir miktar rakam" kalıbına uyacaktir.

Ancak "1-2345678" de bu kalıba uyar; "1-2" de uyar. Hatta "-123" de uyar. Oysa istediğimiz tam olarak bu değildi.

Regexp'i biraz düzeltmek gerekiyor:

    if ( $dizi=~ /d{3}-d{7} )
"Tam 3 tane rakam, sonra eksi işareti, sonra 7 tane rakam" daha anlamlı bir test yapacaktır.

Bu sefer de "A312-2345678E" dizisi sorun çıkaracaktır, daha doğrusu regexp kalıbına uyacak ve belki programın hatalı çalışmasına yol açacaktır.

Eğer kontrolu "dizinin başında 3 tane rakami sonra eksi işareti, sonunda da 7 tane rakam var mı?" diye yeniden düzenlersek daha iyi olacak gibi...

    if ( $dizi=~ /^d{3}-d{7}$/ ) 
Burada "^" "en başta"; "$" ise "en sonda" diye okunur.

Peki dizinin içinde "$" işareti var mı? diye merak ettiğinizde ne olacak?

    if ( $dizi=~ /$/ )
gibi "" karakteriyle işaretlenen karakterler meta-karakter olarak yorumlanmak yerine, kendileri olarak değerlendirilirler.

Bir başka örnek: Bir nedenle (genetik bilimiyle uğraşıyorsanız gerekecektir) bir dizinin "AAABBCC", "ACC", "AACC" kalıplarına uyup uymadığını kontrol etmeniz gerektiğini düşünün. Bir başka deyişle dizinin içinde bir veya daha fazla "A", sonra bir miktar "B" ve ardından "CC" var mı? diye merak ettiğinizde

    /A+B*CC/
regexp'i kuıllanılabilir. Bu regexp'i "En az bir tane "A", ardından sıfır veya daha fazla "B", ardından iki tane "C" var mı? diye okuyabilirsiniz.

Şimdi çok kullanılan meta-karakterlere bir göz atalım:

dRakamlar
DRakam olmayan karakterler
wBir sözcük içinde yer alabilecek karakterler ( rakamlar, harfler, "_" işareti)
WBir sözcük içinde yer alamayacak karakterler (boşluk, satırbaşı, noktalama işaretleri
[a-z]a'dan z'ye küçük harfler
[A-F]A'dan F'ye büyük harfler
[0-9]Rakamlar
. herhangi bir karakter
[A|X]"A" veya "X"

Gene çok kullanılan durum belirtici meta-karakterlerden bazıları:

*Sıfır veya daha fazla
+Bir veya daha fazla
?sıfır veya bir tane
^En başta
$En sonda
sözcük sınırında (başında ya da sonunda) yer alan

Örnekler:

$d = " abbbccdaabccdde" ise
regexp Sonuç
$d =~ /Abc/Uymaz! $d içinde hiç "A" yok ki "Abc" olsun!
$d =~ /Abc/iUyar! Sondaki "i" ignore case, yani büyük-küçük harf ayırımı yapılmasın anlamında olduğu için "Abc" ile "abc" eşleşir.
$d =~ /abc/Uyar! Açıklamaya gerek var mı?
$d =~ /^abc/Uymaz! Çünkü "^" meta-karakteri dizinin başında anlamındadır ve "abc" alt dizisi $d'nin başında değildir.
$d =~ /abc$/Uymaz! Çünkü "$" meta-karakteri dizinin sonunda anlamındadır ve "abc" alt dizisi $d'nin sonunda değildir.
$d =~ /De$/iUyar! Dizimizin sonunda "de" var ve büyük-küçük harf ayırımı yapılmayacak!
$d =~ /^ab*c/Uyar, çünkü dizinin başında "a" ve ardından 'sıfır veya daha fazla b' ve ardından "c" var! Bu regexp'teki "*" karakteri hemen solundaki karakter için "sıfır veya daha fazla kez tekrar eden" anlamında bir meta karakterdir.
$d =~ /aH*bc/Hmmm.. Uyar! Çünkü dizide "a" ve ardından sıfır veya daha fazla "H" ve ardında "bc" var!
$d =~ /aH+bc/Uymaz! Çünkü dizide "a" ve ardından en az bir tane "H" ve onun ardında "bc" şeklinde bir düzen yok! Bu regexp'teki "+" karakteri hemen solundaki karakter için "bir veya daha fazla kez tekrar eden" anlamında bir meta-karakterdir.
$d =~ /a*bc/Uymaz! Çünkü dizinin içinde hiç "a" ve ardından gelen "*" yok! Bu örnekte "*" bir meta-karakter olarak değil; basit anlamıyla bir asterisk karakteri olarak kullanıldığı için önündeki "" ile işaretlenmiştir.
$d =~ /a+bc/Uymaz! Çünkü dizinin içinde hiç "a" ve ardından gelen "+" yok! Bu örnekte "+" bir meta-karakter olarak değil; basit anlamıyla bir artı işareti olarak kullanıldığı için önündeki "" ile işaretlenmiştir.
$d =~ /ac/Uymaz! Çünkü dizinin içinde hiç "a" ve ardından gelen "" yok!
$d =~ /a.*b/Uyar! Çünkü dizide "a ve aralarında birşeyler ve sonra b" var! "." (nokta) meta-karakteri herhangi bir karaktere uyar; ardından gelen "*" ile birlikte (yani ".*") birşeyler olarak okunabilir.
$d =~ /d.*a/Uyar! Çünkü "birşey" anlamındaki noktanın ardından gelen "*" meta-karakteri sıfır veya daha fazla herhangi birşey anlamındadır.
$d =~ /d.+a/Uyar! Çünkü "birşey" anlamındaki noktanın ardından gelen meta-karakter bir "+"; yani bir veya daha fazla "herhangi birşey"
$d =~ /da?/Uyar! Çünkü dizide "d" ve ardından bir veya sıfır tane "a" gelen alt dizi var.
/Abc/iUyabilir! $_ özel değişkeninin değerine bağlıdır. Eğer $_ değişkeninin içinde biryerlerde "abc", "aBc", "aBC" gibi bir dizi varsa doğrudur. Dikkat ederseniz $_ değişkeni üzerinde regexp testi yapmak için $_ =~ /Abc/i yazmaya gerek yoktur (ama yazılabilir tabii).
Biraz karışık olduğunun farkındayım ama n'apayim; bu iş böyle!

Kod yazarken bazen de "falanca kalıba uyuyorsa" yerine "falanca kalıba uymuyorsa" mantığı gerekir. O zaman if deyimini

    if ( $dizi !~ /abc/ )
formunda kullanabilirsiniz.

Regexp'lerle bul-değiştir işlemi de yapılabilir

Örneğin bir dizideki bütün "A" ları "X" ile değiştirmek gerekirse
    $dizi=~ s/A/X/g;
Perl deyimi kullanılabilir. (g: global)

Dizinin başındaki "a"yı "A" ile değiştirmek gerekirse

    $dizi=~ s/^a/A/;
iş görür.

Şimdi işleri azıcık karıştıralım:

    $dizi=~ s/(d+)([A-Z]+)/2-1/g;
deyimi bir dizideki "bir miktar rakam ve hemen ardından bir miktar büyük harf" ("12ABCD" gibi) kalıplarını "harfler-rakamlar" a çevirecektir. Yani "123ABC" "ABC-123"e dönüşecektir. Bu işi yapabilmek için geçici belleklere gerek vardır. Yani "bir miktar rakam" kalıbına uyaz dizi parçası belleğe alınacak, ardından gelen "bir miktar harf" de bir başka belleğe alınacak ve bu iki bellek içeriği önce ikincisi gelecek şekilde "-" işaretiyle birleştirilecektir.

Bir kalıba uyan dizi parçasını belleğe atmak için o kalıp parantez içinde yazılır. Parantez çiftleri, kullanılış sırasına göre birinci, ikinci vs numaralı belleklere karşılık gelir. Değiştirme aşamasında bu belleklere 1, 2 gibi meta-karakterlerle erişilebilir.

Perl ile CGI kodu yazılırken çok kullanılan bir yöntemden söz edip bu konuyu kapatmak istiyorum:

Bildiğiniz gibi özel karakterler (noktalama işaretleri, daha doğrusu harf ve rakam olmayan karakterlerin çoğu) CGI parametresi olarak aktarılırken "%hh" şeklinde hex (onaltılık sayı sistemi) kodlarına dönüştürülür. Örneğin "!" işareti "%21" ye dönüştürülerek transfer edilir. CGI parametrelerini karşılayan kod bu şekilde dönüştürülmüş karakterleri geri dönüştürmelidir. Bu işi regexp'le yapmak okuması zor olsa da çok kolay yapılabilir:

$param=~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C",hex(1))/eg;
Okunuşu kısaca şöyle:

$param dizisi içinde "%" işaretinden sonra gelebilecek iki hex rakam varsa (sıfırla dokuz arası rakamlar veya A ile F arası harfler) bunu belleğe at ( parantez içinde yazıldığı için) ve bu diziyi ondalık sisteme dönüştürülmek üzere"hex" Perl fonksiyonuna gönder; bu ondalıklı sayısı da "pack" fonksiyonuyla tek karaktere paketle, yani ondalık sayıyı ASCII kodu olarak değerlendirerek karşılığı olan karakteri döndür.

regexp'in sonundaki "e" karakteri, "değiştir" işleminden önce regexp'in değiştirme bloğunda oluşan dizinin önce Perl yorumlayıcısı tarafından yorumlanmasını sağlar (e: execute). "g" parametresi ise bu bul-değiştir işleminin "global", yani $param dizisi içinde olabildiğince tekrarlanmasını sağlar.

Emin olun, "Perl öğreniyoruz" dizisinin yazması en zor bölümüydü bu.

Elinizin altında bir Perl yorumlayıcısı yoksa bu adreste regexp denemeleri yapabilirsiniz.

Görüşler

0
anonim
İlk olarak biraz geç oldu; ama umarım yazı diziniz çift rakamlı haneleri geçer.

Açıkçası bazı hatalar ya da hata gibi görünenler buraya yazılacaklar arasında mı bilemiyorum ama şansımı deneyeyim dedim.

$d =~ /a*bc/ Uymaz. (Regecxp tablosu 10. satır)
$d =~ /a+bc/ Uymaz. (Regecxp tablosu 11. satır)
$d =~ /ac/ Uymaz. (Regecxp tablosu 12. satır)

Sanırım bunların(metakarakterler) başlarındaki çıkmamış ya da ben göremiyorum. (/a*bc/. Sanıyorum aynı şey text'' te daha yukarılarda geçen $ için de geçerli)

Bir de aşağıda ki ifade de
$param dizisi içinde ''''''''%'''''''' işaretinden sonra gelebilecek iki hex rakam varsa (sıfırla dokuz arası rakamlar veya A ile F arası harfler) bunu belleğe at ( parantez içinde yazıldığı için) ve bu diziyi ondalık sisteme dönüştürülmek üzere''''''''hex'''''''' Perl fonksiyonuna gönder; bu ondalıklı sayısı da ''''''''pack'''''''' fonksiyonuyla tek karaktere paketle, yani ondalık sayıyı ASCII kodu olarak değerlendirerek karşılığı olan karakteri döndür.

Anlam düşüklüğü var gibi. bu ondalıklı sayısı da
bu ondalıklı sayıyı da olacak galiba.
0
anonim
Yine ben. Sanırım bunlar metinde çıkmıyor.
Görüş belirtmek için giriş yapın...

İlgili Yazılar

Perl programcısı olmaktan gurur duyanlar için özel !

sundance

Perl'ün yaratıcısı Larry Wall, alışageldiğimiz tarzında yaptığı sunumla, 7. geleneksel Perl Soğanının Durumu isimli toplantıda yine herkesi kırdı, geçirdi :)

'Biz, isteksizler,
cahiller tarafından öncülük edilen bir yolda,
imkansızı gerçekleştirmeye çalışıyoruz.
O kadar uzun süredir o kadar çok şeyi, (elimizdeki) o kadar az şeyle gerçekleştirdik ki
Artık (elimizdeki) hiçbir şeyle, herhangibir şeyi yapabilecek hale yetkinliğe eriştik.'

Perl Öğreniyoruz - 1

butch

Haydi millet toplanın. Perl öğrenmeye başlıyoruz :) Bundan sonra düzenli olarak bu köşede Can Uğur Ayfer tarafından hazırlanan Perl Öğreniyoruz makaleleri yayınlanacak. Bu makalelerde, klasik yöntemle, basit problemleri adım adım çözerek beraberce Perl öğreneceğiz. Buyrun ilk derse...

(Can Uğur Ayfer, Kim Korkar Unix`den, Perl ve MySQL ile Cgi Programlama gibi kitapların yazarı, 1973 yılından beri ülkemizde bilgisayar sektörünün duayenlerinden birisidir. Halen aktif olarak çalışmakta öğrenmeye, öğretmeye aynı coşku ile devam etmektedir. Kendisine sonsuz teşekkürler)

Yeni Deascifier

anonim

15 senedir İngilizce klavye ile yazıyorum, ve fena alıştım. F klavyeye de, Türkçeleştirilmiş Q klavyeye bile alışacak vaktim ve isteğim yok. Eh dedim ben de, biraz beyin ve Perl kaslarımı çalıştırayım, ve şu işe kalıcı bir çözüm bulayım. Benim değişmeye niyetim yok, teknoloji bana yardım etsin.

Perl için FMOD Kütüphanesi

mustafa_

Fmod for Perl, FMOD müzik ve ses efektleri sistemleri kütüphanesine erişmek için geliştirilmiş bir Perl eklentisidir.

Kevin Poulsen iş başında!

sundance

Güvenlik dünyasının meşhur kötü çocuklarından, eski Security Focus yazarı Kevin Paulsen, yazdığı bir Perl scriptle U.S. National Sex Offender Registry kayıtları ile MySpace kullanıcılarını karşılaştırıp, bir cinsel suç zanlısının tespitine yardımcı olmuş.

Kendisini daha çok Los Angeles'daki KISS FM'in 102. arayan kişiye verdiği Porsche 944 S2'i kazanabilmek için, bütün telefon konuşmalarını durduran kişi olarak tanıdığımız Dark Dante kod adlı Poulsen yıllardır bilgi güvenliği alanında gazetecilik yapmakta, hatta zaman zaman bu olaydaki gibi otoritelere yardımcı olmakta.