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 Öğreniyoruz - 2

cayfer

Geçen hafta çözdüğümüz örneği bir de Perl'in özelliklerinden yararlanarak çözelim.

Editörün notu: Perl öğreniyoruz yazı dizisinin ikincisini okuyorsunuz. Sabırsız olanlarınız için cayfer.bilkent.edu.tr incelenmesi gereken bir adres.

KSpyware

tongucyumruk

KSpyware adının çağrıştırdığının aksine KDE üzerinde çalışan bir casus yazılım veya casus yazılım tarayıcısı değil. KSpyware, Win32 platformunda çalışan özgür bir casus yazılım. Klasik anlamdaki casus yazılımlardan en önemli farkı kodlarının tamamen açık olması. Gilbert Nzeka adlı bir programcı tarafından Windows altında casus yazılımlar geliştirme yöntemlerini ortaya koymak amacıyla geliştirilen bu program bilin bakalım hangi programlama diliyle yazılmış?

Not: Bu programı GNU/Linux platformuna port edecek gönüllü bir arkadaş aranıyor.

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)

Perl 20. Doğum Günü ve Perl 5.10 Çıktı

anonim

Geçtiğimiz günlerde Perl'in 20. yaşgünü tüm dünyada coşku ile kutlandı. Doğum gününden 1 gün önce Perl Vakfı Perl 5.10'u duyurdu.

Perl 5.10 da görünen en önemli değişiklik smart match operator. Bir tür karşılaştırma yapmamıza yarayan operatör array içinde aradığımız scalar değeri bulmamızı sağlıyor. Yeni operatörümüz: "~~". Örnek vermek gerekirse $needle scalarimizi array @haystack içinde arıyorsak, kolayca yeni operatörümüzü kullanıyoruz.
if ($needle ~~ @haystack) ...

Sonuç olarak artık Perl'de tüm kaşılaştırmalar doğru şeyi yapmamızı sağlıyor.

Moleküler Biyoloji, Bioinformatik, 21. yy. ve Perl

FZ

60'lı yıllarda uzaya gitmek son moda idi. Sonra hızlı bilgisayarlar, gösterişli teknolojiler geldi, 90'lar böyle geçti. 21. yy ise moleküler biyoloji çağı olacak gibi görünüyor. Bilgisayar teknolojisi ve moleküler biyolojide son 30 yıl içinde kaydedilen ilerlemeler hayat bilimlerinin çehresini bir hayli değiştirdi. Aslında biyoloji o kadar da değişmedi, halen saha çalışması yapan biyologlar günlerce egzotik mekanlarda, zor koşullarda dolanıp garip canlıları inceliyorlar. Ancak hesaplamalı moleküler biyoloji sayesinde `hayat´ kavramına bakışımız epey gelişti. Artık organizmaların iç yapısı hakkında çok daha derinlemesine soru sorabiliyoruz.

Peki bütün bunların, tüm zamanların en fantastik programlama dillerinden biri olan ve doğal dile öykünmesi ile de bilinen Perl programlama dili ile ilgisi nedir? `Hacking´ ve `biyoloji´ kavramları aynı cümle içinde kullanılabilir mi?