|
Programalama Dilleri
|
|
|
|
|
|
|
|
|
|
|
|
|
Videolu Ders Anlatım
|
|
|
|
|
|
|
|
İşlemler
|
|
|
|
|
|
|
|
-
Delphi ile string(karakter katarı, dizisi vs.) işlemleri yapmak çok kolaydır, fakat perde arkasındaki durum daha karmaşıktır. Pascalın geleneksel bir string işleme yapısı bulunmaktadır.Windowsun C dilinden aldığı kendine ait bir yapısı, 32bit Delphinin ise varsayım olarak kullandığı çok güçlü uzun string veri tipleri bulunmaktadır.
String Tipleri:
Borlandın Pascalı ile 16bitlik Delphide string veri tipi sıralı bir karakter dizisi ve bu karakter dizisinin başında dizinin uzunluğunu belirten bir bytelık alan bulunmaktaydı. Dizinin uzunluğunu belirtmeye yarayan alan bir byte tan oluştuğu için dizinin maximum uzunluğu 255 karakterden oluşmaktaydı buda önemli sorunlar yaratmaktaydı. Her string tipi standard olarak 255 karakter uzunluğunda yaratılmaktaydı, fakat kullanıcı isteğe bağlı olarak daha kısa stringler yaratıp hafızadan yer kazanabilmekteydi.
String tipi dizi tipine benzemektedir, tek fark stringin karakterlerden oluşan bir dizi olmasıdır. Buda string veri tipinin herhangi bir karakterine [] parantez işaretlerini kullanarak erişebileceğimiz anlamına gelmektedir.
Geneleneksel Pascal stringlerinin yarattığı kısıtlamalardan kurtulmak için 32bitlik Delphi sürümü daha uzun string yapılarını desteklemetedir. Gerçekte 3 değişik tipde string yapısı bulunmakta :
o ShortString tipi geleneksel Pascaldaki string ve 16bitlik Delphi sürümündeki string veri tipi ile özdeştir. ShortString tipinin her bir elemanı ANSIChar veri tipinden oluşmaktadır.
o ANSIString veri tipi yeni değişken uzunluktaki string veri tipine karşılık gelmektedir. Bu stringler dinamik olarak yer kapladıkları gibi referans sayacı ve copy-on-write tekniğinide kullanırlar.Boyut olarak bu string tipi sınırlı değildir. 2 milyar karakter boyutlarında olabilir. Bu dizinin elemanlarıda ANSIChar tipinden oluşmaktadır.
o WideString veri tipide ANSIString veri tipi ile aynı özelliğe sahiptir fakat WideChar tipinde elemanlardan oluşmaktadır bu sebeple Unicode karakterleride saklayabilirler.
Uzun String Yapılarının kullanımı:
Eğer string veri tipini kullanıyorsanız $H derleyici direktifinin durumuna bağlı olarak ShortString yada ANSI string tipinde çalışırsınız. Delphi Bileşen kütüphanesininde varsayım olarak kullandığı yapı budur.
Delphinin LongStringleri temel olarak referans sayacı mekanizmasını kullanırlar. Bu mekanizma kaç tane string değişkeninin hafızadaki bir string yapısını referans olarak gösterdiğinin sayısını tutar. Ayrıca bu referans sayaç sistemi string lerin kullanılmamaları durumunda yani sayacın 0 olduğu durumlarda hafızadan silinmesini sağlar.Eğer string yapınızın boyutunu dinamik olarak arttırmak istediğinizde ve hafızada daha önce var olan stringinize komşu olan hafıza alanında başka bir bilgi varsa genişleme olmaz, bunun yerine var olan stringiniz hafızada daha geniş bir alana kopyalanır ve o alanda genişletilir.
Böyle bir durumda Delphinin çalışma kipi desteği btün bu işleri sizin yerinize transparan olarak yapar. Bir stringin ihtiyacınıza bağlı olarak maximum boyutunu SetLength prosedürünü kullanarak yapabilirsiniz.
SetLength (String1, 200);
Setlengthprosedürü gerçektende istenilen miktarda hafıza alanını ayırmaz sadece istekte bulunur, ve olanı ileride kullanılmak üzere rezerve eder.
String boyutunu ayarlamak çok seyrek olarak gereklidir.Uzunu stringler için boyut belirlemeniz gereken tek durum Windows apilerini kullandığınız durumlardır.Bu konuda kısa bir bilgi ileriki bölümlerde verilecektir.
Stringlerin hafızadaki durumları
Stringlerin hafızada nasıl yönetildiğini görebilmeniz için StrRef adında örneği hazırladım.Bu programda iki global string değişkeni tanımladım. İki butondan ilkine basılınca program birinci değişkene bir değer atıyor ve bu birinci değişkenide ikinci değişkene atıyor.
Str1 := 'Hello';
Str2 := Str1;
Stringler ile çalışmanın yanında program bu değşikenlerin yerel durumlarını bir listbox içerisinde gösteriyor. Bu işlem içinde StringStatus fonksiyonunu kullnıyor:
function StringStatus (const Str: string): string;
begin
Result := 'Address: ' + IntToStr (Integer (Str)) +
', Length: ' + IntToStr (Length (Str)) +
', References: ' + IntToStr (PInteger (Integer (Str) - ^) +
', Value: ' + Str;
end;
StringStatus fonksiyonunda hayati rol oynayan kod parametre olarak geçilen string değerinin const(sabit) olarak atanması.Bu parametreyi kopyalanması tekniğini (önceki bölümde parametrelerin nasıl aktarıldığını işlemiştik) kullansaydık string değişkeninin referans ettiği noktayı bulmakta zorlanacaktık. Bunun yanında parametreyi (var) ile referans olarak atamak veya (const) ile sabit olarak atmak bir sorun çıkarmayacaktır.
Bu durumda const kullandım çünkü fonksiyon içerisinde gelen parametrenin değerini değiştirecek bir işlem yapmayacağım.
String değişkeninin hafızada bulunduğu adresi elde etmek için ileri düzey typecast (tip dönüşüm) işlemleri gerçekleştirdim. Bundaki amacım bir string değişkeninin nasıl gösterildiğinive iki değişkenin aynı stringe nasıl referans gösterdiğini anlatmak.
Stringler aslında bir refereanstır, bir işaretçi, bir belirteçtir (Pointer). Bu değer, bu pointer stringing gerçek yerinin adresini tutar. Bu değişkene yapılan referans sayısını belirtmek için stringin içerisinde zaten tutulan bu değerleri okudum bunun için stringin başladığı adresten bazı değerleri matematiksel olarak çıkardım. -4 stringin uzunluğu için -8 ise yapılan referans sayısı için çıkarılır.
İleride Delphinin yeni versiyonları çıktığında bu değerlerinde değişebileceği gözden kaçırılmamalı. Bu örnek programı çalıştırdığınızda şekil 2.1 dede görüleceği üzere referans sayısının 2 olduğunu ve değişkenlerin aynı hafıza adreslerini kullandığını göreceksiniz.
Bu örnekteki iki değişkenin aynı hafıza adresine referansta bulunduğunu gördük. Bu durumda referansta bulunana iki değişkenden herhangi birisinde bir değişiklik yaptığınızda bu hafıza adresinin değiştiğini göreceksiniz.Copy-On-Write tekniğinin bir etkisi olarak görülmekte.
Yukarıda bahsedilen etkiyi oluşturmak için şekil 7.1 de gösterilen form üzerindeki Change yazılı butonun onClick olayına aşağıdaki kodu yazmamız yeterli olacaktır.
procedure TFormStrRef.BtnChangeClick(Sender: TObject);
begin
Str1 [2] := 'a';
ListBox1.Items.Add ('Str1 [2] := ''a''');
ListBox1.Items.Add ('Str1 - ' + StringStatus (Str1));
ListBox1.Items.Add ('Str2 - ' + StringStatus (Str2));
end;
Change butonuna basıldığında tepki gösterilmesi için Assign butununa basılmış olması gerekmektedir. Bu tür bir kısıtlamanın oluşturulabilmesi için form aktif olduğu sırada Change butonunun etkin olma öelliğinin false hale getirilmesi gerekir. (Yani object inspector penceresinden Enabled özelliğinin False yapılması gerekir.) Bu örnekte bulunan StringStatus fonksiyonunda değişiklikler yaparak LongString lerin yapıları ve durumları hakkındada bilgi edinebilirsiniz.
Delphi'deki Stringler ve Windows'daki PChars'lar
LongStringlerin kullanımında dikkat edilmesi gereken önemli diğer bir noktada Bu stringlerin Null-Terminated olmasıdır. Yani bu stringlerin son karakterlerinden sonra bir başka karakterin NULL karakterinin bulunmasıdır. Neden önemli olduğuna gelirsek bu yapının C programlama dilinde ve Windowsun temelinde kullanılan string yapısıyla özdeş olmasıdır. Bir nevi uyumluluk sağlamaktadır. Pascaldaki LongStringler null-terminated olduğu için C dilindeki null-terminated stringlerle uyum sağlar bu nedenle LongStringleri casting işlemine tabii tutarak WindowsAPI çağrılarına parametre olarak geçebilirsiniz. Örneğin Windowsun API çağrılarını kullanarak formun Caption'ını PChar tipinde bir string e atayıp daha sonra bu Captionu bir butonun Caption özelliğine aktaralım. Program kodu aşağıdaki gibi olacaktır :
procedure TForm1.Button1Click (Sender: TObject);
var
S1: String;
begin
SetLength (S1, 100);
GetWindowText (Handle, PChar (S1), Length (S1));
Button1.Caption := S1;
end;
Bu kodu LongString örneğinde bulabilirsiniz. Burada dikkat etmeniz gereken nokta SetLength komutunu kullanmadan API çağrısında bulunduğunuzda programınızın çökeceğidir. Eğer PChar tipinde ir değişken kullansaydınız program kodu daha basitleşecekti çünkü bu iş için geçici bir string değişken tanımlamamış ve değişkeni initialize (ilk kullanıma hazırlama) etmemiş olacaktınız. Aşağıdaki kod Label bileşeninin caption özelliğini API çağrısına parametre olarak geçmektedir.
SetWindowText (Handle, PChar (Label1.Caption));
Eğer WideString tipini Windows uyumlu bir tipe çevirmek isterseniz PChar yerine PWideChar tipini kullanabilirsiniz. WideStringler (widechar) OLE ve COM programlarında kullanılmaktadırlar.
Bu noktaya kadar oluşturulan izlenimden sonra karşılaşılabilecek tuzaklara odaklanmak istiyorum. LongString yapısının PChar yapısına dönüştürülmesi sırasında bazı problemlerle karşılaşılabilir. Yani dönüşüm yaptıkdan sonra bütün sorumluluk size kalır Delphi bu noktadan sonra yeni yapının içeriği ve durumu ile ilgilenmez. Yukarıdaki program kodu parçasına yapılan ufak değişklilerden sonra oluşturulan aşağıdaki yeni koda dikkat ediniz :
procedure TForm1.Button2Click(Sender: TObject);
var
S1: String;
begin
SetLength (S1, 100);
GetWindowText (Handle, PChar (S1), Length (S1));
S1 := 'Başlık = ' + S1; // bu kod işe yaramayacaktır.
Button1.Caption := S1;
end;
Yukarıdaki kod derlenir fakat çalıştırdığınızda beklediğinizden farklı bir sonuç elde edersiniz. Buton'un Caption özelliği sizin eklemiş olduğunuz text değilde pencerenin orjinal text özelliğini gösterir. Bu sorunun sebebi, windows string değişkenine birşeyler yazdığı zaman o stringin uzunluğunu tam olarak ayarlayamaz. Delphi bu stringi yinede kullanabilir fakat null karakterini gördüğü noktada duracaktır ve geri kalan karakterleri gözardı edecektir. Bu problemi nasıl çözebiliriz?
Bunun için sisteme GetWindowText API fonksiyonun geriye dönderdiği stringi Pascal stringine çevirmesini söylememiz gerekmektedir. Hernekadarda aşağıdaki kodu yazsanız çalışmayacaktır.
S1 := String (S1);
Sistem bunuda gözardı edecektir, çünkü bir veri tipini tekrar aynı veri tipine çevirmek faydasız bir işlemdir. Uygun Pascal stringini elde edebilmek için stringi pchar'a cast edip daha sonra tekrar stringe çevirmektir.
S1 := String (PChar (S1));
Aslında bu uyarlama işinide atlayabilirsiniz. Pchardan
Stringe dönüştürme işlemini Delphi otomatik olarak yapar.
İşte son kodun son hali:
procedure TForm1.Button3Click(Sender: TObject);
var
S1: String;
begin
SetLength (S1, 100);
GetWindowText (Handle, PChar (S1), Length (S1));
S1 := String (PChar (S1));
S1 := S1 + ' is the title';
Button3.Caption := S1;
end;
Alternatif diğer bir çözümde Delphi stringinin uzunluğunu Pchar stringinin uzunluğuna eşitlemektir. Bu işlem için aşağıdaki kodu kullanırız:
SetLength (S1, StrLen (PChar (S1)));
Stringleri Formatlamak
(+) operatörü ve bazı dönüşüm fonksiyonlarını ( IntToStr gibi ) kullanarak mevcut değerlerden karmaşık stringler elde edebilirsiniz. Bununla birlikte sayıları, parasal değerleri ve diğer strinleri tek bir string olarak elde edebilirsiniz. Bu iş için çok güçlü Format fonksiyonu yada türevlerini kullanabilirsiniz.Format fonksiyonu bir dizi parametre almaktadır. Bunlardan birincisi diğer verilerin içerisinide hangi noktalara yerleştirilmesi gerektiğini belirten temel string'tir. İkincisi ise yerleştirilmesini istediğimiz veri dizisidir. Örneğin iki sayıyı bir string yapısı içersinde formatlamak için şu kodu kullanırız :
Format ('First %d, Second %d', [n1, n2]);
n1 ve n2 integer değerlerdir. İlk %d ifadesi yerine dizideki ilk değer, ikinci %d ifadesi yerine dizideki ikinci değer koyulur. Yüzde işaretinden sonra kullandığımız karakter bu alana ne tür bir değerin koyulacağını belirtir. %d integer değerleri yerleştirmek için kullanılır. %d yazıpta veri dizisi içerisine string bir ifade yerleştirdiğimizde kod derlenir fakat çalıştırıldığında Run-Time Error alırsınız. Format fonksiyonunu kullanmanın tek dezavantajıda budur. Yani derleme esnasında tip uyumluluğu kontrolü yapılmaz.
Format fonksiyonu parametre olarak bu bölümün sonuda anlatacağımız açık dizi yapısını kullanır. Yani farklı tiplerde değerleri parametre olarak alabilir.
%d ifadesi yerine kullanabileceğiniz alternatif değerler aşağıdaki tabloda verilmiştir.
Tip belirteci Tanım
d (decimal) Ondalık İlgili Integer değer ondalık sayı olarak stringe çevirilir
x (hexadecimal) Onaltılık İlgili Integer değer onaltılık sayı siteminde stringe çevirilir
p (pointer) İlgili pointer değeri onaltılık siteme göre stringe çevirilir.
s (string) İlgili Pchar yada string değeri tekrar string olarak temel stringe yerleştirilir.
E (exponential) İlgili exponansiyel değer stringe çevirilir.
f (floating point) İlgili Integer değer ondalık sayı olarak stringe çevirilir.
G (general) The corresponding floating-point value is converted to the shortest possible decimal string using either floating-point or exponential notation.
N (number) İlgili kayan noktalı sayılar ondalık sayı olarak stringe çevirilir ve bindelik hanelere ayırılır
M (money) The corresponding floating-point value is converted to a string representing a currency amount. The conversion is based on regional settings-see the Delphi Help file under Currency and date/time formatting variables.
Bunun yanında sayıların kaç hanede temsil edileceğini ve ondalık sayıların ondalık kısımlarının kaç basamak olacağınıda belirleyebilirsiniz. Örneğin :
Format ('%8d', [n1]);
Bütün bu formatlama işlemlerini daha iyi incelemek için frmTest adlı programı yazdım. Aşağıda örnek bir çıktısı ile kodunu göreceksiniz:
procedure TFormFmtTest.BtnIntClick(Sender: TObject);
begin
ShowMessage (Format (EditFmtInt.Text,
[StrToInt (EditInt.Text)]));
// if the item is not there, add it
if ListBoxInt.Items.IndexOf (EditFmtInt.Text) < 0 then
ListBoxInt.Items.Add (EditFmtInt.Text);
end;
procedure TFormFmtTest.ListBoxIntClick(Sender: TObject);
begin
EditFmtInt.Text := ListBoxInt.Items [
ListBoxInt.ItemIndex];
end;
|
|
|
|
|
|
|
|
40494 ziyaretçi (66013 klik) |
|
|
|
| | |