Skip to content

5. Yaygın olarak kullanılan .NET sınıfları

Burada, .NET platformunda sıkça kullanılan bazı sınıfları tanıtıyoruz. Öncelikle, mevcut olan birkaç yüz sınıf hakkında nasıl bilgi edinebileceğinizi göstereceğiz. Bu yardım, deneyimli C# geliştiricileri için bile vazgeçilmezdir. Bir yardım belgesinin kalitesi (kolay erişim, anlaşılır düzen, bilgilerin uygunluğu vb.), bir geliştirme ortamının başarısını veya başarısızlığını belirleyebilir.

5.1. .NET sınıfları hakkında yardım arama

Burada, Visual Studio.NET ile yardım bulmak için bazı ipuçları veriyoruz

5.1.1. Yardım/İçindekiler

  • [1]'te, menüden Help/Contents seçeneğini seçin.
  • [2]'te, Visual C# Express Edition seçeneğini seçin
  • [3]'te, C# yardım ağacı
  • [4]'te, başka bir yararlı seçenek de .NET Framework'tür; bu, .NET framework'ündeki tüm sınıflara erişim sağlar.

C# yardımındaki bölüm başlıklarına bir göz atalım:

  • [1]: C#'a genel bakış
  • [2]: C#'ın belirli konularına ilişkin bir dizi örnek
  • [3]: Bir C# kursu – bu belgeyi avantajlı bir şekilde ikame edebilir…
  • [4]: C#'ın ayrıntılarına dalmak için
  • [5]: C++ veya Java geliştiricileri için yararlıdır. Bazı tuzaklardan kaçınmanıza yardımcı olur.
  • [6]: Örnekler arıyorsanız, buradan başlayabilirsiniz.
  • [7]: Grafik kullanıcı arayüzleri oluşturmak için bilmeniz gerekenler
  • [8]: IDE Visual Studio Express'i daha iyi kullanmak için
  • [9]: SQL Server Express 2005, ücretsiz olarak dağıtılan kaliteli bir SGBD yazılımıdır. Bu derste onu kullanacağız.

C# yardımı, geliştiricinin ihtiyaç duyduğu şeylerin sadece bir kısmıdır. Diğer kısmı ise, işini kolaylaştıracak .NET çerçevesindeki yüzlerce sınıfla ilgili yardımdır.

  • [1]: .NET çerçeve yardımını seçin
  • [2]: Yardım, .NET Framework dalında bulunur SDK
  • [3]: .NET Framework Sınıf Kütüphanesi dalı, ait oldukları ad alanına göre tüm .NET sınıflarını gösterir
  • [4]: Önceki bölümlerdeki örneklerde en sık kullanılan System ad alanı
  • [5]: System ad alanında yer alan bir örnek; burada DateTime yapısı
  • [6]: DateTime yapısı hakkında yardım

5.1.2. Yardım/Dizin/Arama

MSDN tarafından sağlanan yardım çok kapsamlıdır ve nereden başlayacağınızı bilemeyebilirsiniz. Bu durumda yardım dizinini kullanabilirsiniz:

  • [1]'te, yardım penceresi henüz açık değilse [Help/Index] seçeneğini kullanın; aksi takdirde mevcut bir yardım penceresinde [2]'i kullanın.
  • [3] komutuyla, aramanın yapılacağı alanı belirtin
  • [4]'te, ne aradığınızı belirtin; burada bir sınıf
  • [5]'te, yanıt

Yardım aramanın bir başka yolu da yardım bölümündeki arama işlevini kullanmaktır:

  • [1]'te, yardım penceresi henüz açık değilse [Help/Search] seçeneğini kullanın; aksi takdirde mevcut bir yardım penceresinde [2]'i kullanın.
  • [3]'e, arananı belirtin
  • [4]'te, arama alanlarını filtreleyin
  • [5]'te, aranan metnin bulunduğu farklı başlıklar halinde yanıt.

5.2. Karakter dizileri

5.2.1. System.String sınıfı

System.String sınıfı, basit string türüyle aynıdır. Bu sınıfın birçok özelliği ve yöntemi vardır. İşte bunlardan bazıları:


public int Length { get; }
nombre de caractères de la chaîne

public bool EndsWith(string value)

rend vrai si la chaîne se termine par value

public bool StartsWith(string value)

rend vrai si la chaîne commence par value
public virtual bool Equals(object obj)

rend vrai si la chaînes est égale à obj - équivalent chaîne==obj

public int IndexOf(string value, int startIndex)

rend la première position dans la chaîne de la
chaîne value - la recherche commence à partir du
caractère n° startIndex

public int IndexOf(char value, int startIndex)

idem mais pour le caractère value

public string Insert(int startIndex, string value)

insère la chaîne value dans chaîne en position
startIndex
public static string Join(string separator, string[] value)

méthode de classe - rend une chaîne de caractères,
résultat de la concaténation des valeurs du tableau
value avec le séparateur separator

public int LastIndexOf(char value, int startIndex, int count)

public int LastIndexOf(string value, int startIndex, int count)

idem indexOf mais rend la dernière position au lieu
de la première

public string Replace(char oldChar, char newChar)

rend une chaîne copie de la chaîne courante où le
caractère oldChar a été remplacé par le caractère
newChar

public string[] Split(char[] separator)

la chaîne est vue comme une suite de champs séparés
par les caractères présents dans le tableau
separator. Le résultat est le tableau de ces champs

public string Substring(int startIndex, int length)

sous-chaîne de la chaîne courante commençant à la
position startIndex et ayant length caractères
public string ToLower()
rend la chaîne courante en minuscules
public string ToUpper()
rend la chaîne courante en majuscules
public string Trim()
rend la chaîne courante débarrassée de ses espaces
de début et fin

Önemli bir noktaya dikkat çekmek gerekir: Bir yöntem bir karakter dizisi döndürdüğünde, bu dizisi, yöntemin uygulandığı diziden farklı bir dizidir. Dolayısıyla, S1.Trim() işlevi S2 dizesini döndürür; S1 ve S2 ise birbirinden farklı iki dizedir.

Bir C dizesi, bir karakter dizisi olarak düşünülebilir. Dolayısıyla

  • C[i], C dizisindeki i karakteridir
  • C.Length, C dizisinin karakter sayısıdır

Şu örneği ele alalım:


using System;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            string uneChaine = "l'oiseau vole au-dessus des nuages";
            affiche("uneChaine=" + uneChaine);
            affiche("uneChaine.Length=" + uneChaine.Length);
            affiche("chaine[10]=" + uneChaine[10]);
            affiche("uneChaine.IndexOf(\"vole\")=" + uneChaine.IndexOf("vole"));
            affiche("uneChaine.IndexOf(\"x\")=" + uneChaine.IndexOf("x"));
            affiche("uneChaine.LastIndexOf('a')=" + uneChaine.LastIndexOf('a'));
            affiche("uneChaine.LastIndexOf('x')=" + uneChaine.LastIndexOf('x'));
            affiche("uneChaine.Substring(4,7)=" + uneChaine.Substring(4, 7));
            affiche("uneChaine.ToUpper()=" + uneChaine.ToUpper());
            affiche("uneChaine.ToLower()=" + uneChaine.ToLower());
            affiche("uneChaine.Replace('a','A')=" + uneChaine.Replace('a', 'A'));
            string[] champs = uneChaine.Split(null);
            for (int i = 0; i < champs.Length; i++) {
                affiche("champs[" + i + "]=[" + champs[i] + "]");
            }//for
            affiche("Join(\":\",champs)=" + System.String.Join(":", champs));
            affiche("(\"  abc  \").Trim()=[" + "  abc  ".Trim() + "]");
        }//Ana

        public static void affiche(string msg) {
            // mesajı görüntüler
            Console.WriteLine(msg);
        }//görüntüler
    }//sınıf
}//ad alanı

Çalıştırıldığında şu sonuçlar elde edilir:

uneChaine=l'oiseau vole au-dessus des nuages
uneChaine.Length=34
chaine[10]=o
uneChaine.IndexOf("vole")=9
uneChaine.IndexOf("x")=-1
uneChaine.LastIndexOf('a')=30
uneChaine.LastIndexOf('x')=-1
uneChaine.Substring(4,7)=seau vo
uneChaine.ToUpper()=L'OISEAU VOLE AU-DESSUS DES NUAGES
uneChaine.ToLower()=l'oiseau vole au-dessus des nuages
uneChaine.Replace('a','A')=l'oiseAu vole Au-dessus des nuAges
champs[0]=[l'oiseau]
champs[1]=[vole]
champs[2]=[au-dessus]
champs[3]=[des]
champs[4]=[nuages]
Join(":",champs)=l'oiseau:vole:au-dessus:des:nuages
("  abc  ").Trim()=[abc]

Başka bir örnek ele alalım:


using System;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // analiz edilecek satır
            string ligne = "un:deux::trois:";
            // alan ayırıcıları
            char[] séparateurs = new char[] { ':' };
            // bölme
            string[] champs = ligne.Split(séparateurs);
            for (int i = 0; i < champs.Length; i++) {
                Console.WriteLine("Champs[" + i + "]=" + champs[i]);
            }
            // birleştirme
            Console.WriteLine("join=[" + System.String.Join(":", champs) + "]");
        }
    }
}

ve çalıştırma sonuçları:

1
2
3
4
5
6
Champs[0]=un
Champs[1]=deux
Champs[2]=
Champs[3]=trois
Champs[4]=
join=[un:deux::trois:]

String sınıfının Split yöntemi, bir karakter dizisinin öğelerini bir diziye yerleştirmeye olanak tanır. Burada kullanılan Split yönteminin tanımı şöyledir:


    public string[] Split(char[] separator);
separator
karakter tablosu. Bu karakterler, karakter dizisindeki alanları ayırmak için kullanılan karakterleri temsil eder. Dolayısıyla, karakter dizisi "champ1, champ2, champ3"" ise, separator=new char[] {','} kullanabiliriz. Ayırıcı bir dizi boşluktan oluşuyorsa, separator=null kullanılır.
résultat
dizinin her bir elemanı, dizenin bir alanı olan karakter dizilerinden oluşan bir dizi.

Join yöntemi, String sınıfının statik bir yöntemidir:


    public static string Join(string separator, string[] value);
value
dize dizisi
separator
alan ayırıcısı olarak kullanılacak bir karakter dizesi
résultat
value dizisindeki öğelerin, separator dizisiyle ayrılmış olarak birleştirilmesiyle oluşturulan bir karakter dizisi.

5.2.2. System.Text.StringBuilder sınıfı

Daha önce, S1 karakter dizisine uygulanan String sınıfının yöntemlerinin, başka bir S2 karakter dizisi döndürdüğünü belirtmiştik. System.Text.StringBuilder sınıfı, S1 dizesini, S2 dizesi oluşturmaya gerek kalmadan işlememize olanak tanır. Bu, ömrü çok kısa olan dizelerin çoğalmasını önleyerek performansı artırır.

Sınıf, çeşitli oluşturucuları destekler:

StringBuilder()
constructeur par défaut
StringBuilder(String value)

construction et initialisation avec value
StringBuilder(String value, int capacité)

construction et initialisation avec value avec une taille de
bloc de capacité caractères.

Bir StringBuilder nesnesi, altta yatan dizgiyi depolamak için capacité karakterlik bloklarla çalışır. Varsayılan olarak capacité değeri 16'dır. Yukarıdaki 3. oluşturucu, blokların kapasitesini belirlemeye olanak tanır. Bir S dizisini depolamak için gereken capacité karakter bloklarının sayısı, StringBuilder sınıfı tarafından otomatik olarak ayarlanır. Bir StringBuilder nesnesindeki maksimum karakter sayısını belirlemek için oluşturucular mevcuttur. Varsayılan olarak, bu maksimum kapasite 2.147.483.647'dir.

Aşağıda, capacité kavramını açıklayan bir örnek verilmiştir:


using System.Text;
using System;
namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // str
            StringBuilder str = new StringBuilder("test");
            Console.WriteLine("taille={0}, capacité={1}", str.Length, str.Capacity);
            for (int i = 0; i < 10; i++) {
                str.Append("test");
                Console.WriteLine("taille={0}, capacité={1}", str.Length, str.Capacity);
            }
            // str2
            StringBuilder str2 = new StringBuilder("test",10);
            Console.WriteLine("taille={0}, capacité={1}", str2.Length, str2.Capacity);
            for (int i = 0; i < 10; i++) {
                str2.Append("test");
                Console.WriteLine("taille={0}, capacité={1}", str2.Length, str2.Capacity);
            }
        }
    }
}
  • 7. satır: 16 karakterlik blok boyutuna sahip bir StringBuilder nesnesinin oluşturulması
  • 8. satır: str.Length, str dizesinin mevcut karakter sayısıdır. str.Capacity, yeni bir blok tahsis edilmeden önce mevcut str dizesinin depolayabileceği karakter sayısıdır.
  • 10. satır: str.Append(String S), String türündeki S dizesini, StringBuilder türündeki str dizesine birleştirmeye olanak tanır.
  • 14. satır: 10 karakterlik blok kapasitesine sahip bir StringBuilder nesnesi oluşturulur

Çalıştırma sonucu:

taille=4, capacité=16
taille=8, capacité=16
taille=12, capacité=16
taille=16, capacité=16
taille=20, capacité=32
taille=24, capacité=32
taille=28, capacité=32
taille=32, capacité=32
taille=36, capacité=64
taille=40, capacité=64
taille=44, capacité=64
taille=4, capacité=10
taille=8, capacité=10
taille=12, capacité=20
taille=16, capacité=20
taille=20, capacité=20
taille=24, capacité=40
taille=28, capacité=40
taille=32, capacité=40
taille=36, capacité=40
taille=40, capacité=40
taille=44, capacité=80

Bu sonuçlar, sınıfın kapasitesi yetersiz olduğunda yeni bloklar tahsis etmek için kendine özgü bir algoritma izlediğini göstermektedir:

  • 4-5. satırlar: kapasitenin 16 karakter artırılması
  • 8-9. satırlar: 16 karakter yeterli olurken kapasite 32 karakter artırıldı.

İşte sınıfın bazı yöntemleri:


public StringBuilder Append(string value)

ajoute la chaîne value à l'objet StringBuilder. Rend
l'objet StringBuilder. Cette méthode est surchargée
 pour admettre différents types pour value : byte,
int, float, double, decimal, ... 

public StringBuilder Insert(int index,
string value)

insère value à la position index. Cette méthode est
surchargée comme la précédente pour accepter
différents types pour value.

public StringBuilder Remove(int index, int length)

supprime length caractères à partir de la position
index.

public StringBuilder Replace(string oldValue,
string newValue)

remplace dans StringBuilder, la chaîne oldValue par
la chaîne newValue. Il existe une version surchargée
(char oldChar, char newChar).
public String ToString()

convertit l'objet StringBuilder en un objet de type
String.

İşte bir örnek:


using System.Text;
using System;
namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // str3
            StringBuilder str3 = new StringBuilder("test");
            Console.WriteLine(str3.Append("abCD").Insert(2, "xyZT").Remove(0, 2).Replace("xy", "XY"));
        }
    }
}

ve sonuçları:

XYZTstabCD

5.3. Diziler

Tablolar, Array sınıfından türetilir:

Array sınıfı, bir diziyi sıralamak, bir dizide bir elemanı aramak, bir dizinin boyutunu değiştirmek vb. için çeşitli yöntemlere sahiptir. Bu sınıfın bazı özelliklerini ve yöntemlerini tanıtıyoruz. Bunların neredeyse tamamı, farklı varyantlarda mevcut olacak şekilde aşırı yüklenmiştir (c.a.d). Her dizi bu özellikleri miras alır.

Özellikler

public int Length {get;}
nombre total d'éléments du tableau, quelque soit son nombre de dimensions
public int Rank {get;}
nombre total de dimensions du tableau

Yöntemler

public static int BinarySearch<T>(T[] tableau,
 value)
rend la position de [value] dans tableau.
public static int BinarySearch<T>(T[] tableau,
nt index, int length, T value)
idem mais cherche dans tableau à partir de la
position [index] et sur [length] éléments
public static void Clear(Array tableau, int index,
int length)
met les [length] éléments de tableau commençant au
 n° [index] à 0 si numériques, false si booléens, null si références
public static void Copy(Array source,
Array destination, int length)
copie [length] éléments de source dans destination
public int GetLength(int i)
nombre d'éléments de la dimension n° i du tableau
public int GetLowerBound(int i)
indice du 1er élément de la dimension n° i
public int GetUpperBound(int i)
indice du dernier élément de la dimension n° i
public static int IndexOf<T>(T[] tableau,
T valeur)
rend la position de valeur dans tableau ou -1 si
valeur n'est pas trouvée.
public static void Resize<T>(ref T[] tableau,
int n)
redimensionne tableau à n éléments. Les éléments
déjà présents sont conservés.
public static void Sort<T>(T[] tableau,
IComparer<T> comparateur)
trie tableau selon un ordre défini par comparateur.
Cette méthode a été présentée au paragraphe Erreur : source de la référence non trouvée.

Aşağıdaki program, Array sınıfındaki bazı yöntemlerin kullanımını göstermektedir:


using System;

namespace Chap3 {
    class Program {
        // arama türü
        enum TypeRecherche { linéaire, dichotomique };

        // ana yöntem
        static void Main(string[] args) {
            // klavyeyle girilen tablo öğelerinin okunması
            double[] éléments;
            Saisie(out éléments);
            // sıralanmamış tablonun görüntülenmesi
            Affiche("Tableau non trié", éléments);
            // Sıralanmamış tabloda doğrusal arama
            Recherche(éléments, TypeRecherche.linéaire);
            // dizinin sıralanması
            Array.Sort(éléments);
            // sıralanmış tablonun görüntülenmesi
            Affiche("Tableau trié", éléments);
            // Sıralanmış tabloda ikili arama
            Recherche(éléments, TypeRecherche.dichotomique);
        }

        // elemanlar tablosuna değerlerin girilmesi
        // elemanlar: yöntemle oluşturulan tabloya referans
        static void Saisie(out double[] éléments) {
            bool terminé = false;
            string réponse;
            bool erreur;
            double élément = 0;
            int i = 0;
            // Başlangıçta tablo mevcut değildir
            éléments = null;
            // dizinin elemanlarını girme döngüsü
            while (!terminé) {
                // soru
                Console.Write("Elément (réel) " + i + " du tableau (rien pour terminer) : ");
                // cevabın okunması
                réponse = Console.ReadLine().Trim();
                // dize boşsa girişin sonlandırılması
                if (réponse.Equals(""))
                    break;
                // girişin doğrulanması
                try {
                    élément = Double.Parse(réponse);
                    erreur = false;
                } catch {
                    Console.Error.WriteLine("Saisie incorrecte, recommencez");
                    erreur = true;
                }//try-catch
                // hata yoksa
                if (!erreur) {
                    // diziye bir öğe daha eklenir
                    i += 1;
                    // yeni elemanı eklemek için dizinin boyutunu değiştirme
                    Array.Resize(ref éléments, i);
                    // yeni elemanın eklenmesi
                    éléments[i - 1] = élément;
                }
            }//while
        }

        // bir dizideki elemanları görüntülemek için genel yöntem
        static void Affiche<T>(string texte, T[] éléments) {
            Console.WriteLine(texte.PadRight(50, '-'));
            foreach (T élément in éléments) {
                Console.WriteLine(élément);
            }
        }

        // tabloda bir öğeyi arama
        // elemanlar: gerçek sayı tablosu
        // TypeRecherche: ikili veya doğrusal
        static void Recherche(double[] éléments, TypeRecherche type) {
            // Arama
            bool terminé = false;
            string réponse = null;
            double élément = 0;
            bool erreur = false;
            int i = 0;
            while (!terminé) {
                // soru
                Console.WriteLine("Elément cherché (rien pour arrêter) : ");
                // cevabı okuma-doğrulama
                réponse = Console.ReadLine().Trim();
                // bitti mi?
                if (réponse.Equals(""))
                    break;
                // doğrulama
                try {
                    élément = Double.Parse(réponse);
                    erreur = false;
                } catch {
                    Console.WriteLine("Erreur, recommencez...");
                    erreur = true;
                }//try-catch
                // hata yoksa
                if (!erreur) {
                    // dizide eleman aranıyor
                    if (type == TypeRecherche.dichotomique)
                        // ikili arama
                        i = Array.BinarySearch(éléments, élément);
                    else
                        // doğrusal arama
                        i = Array.IndexOf(éléments, élément);
                    // Cevabın görüntülenmesi
                    if (i >= 0)
                        Console.WriteLine("Trouvé en position " + i);
                    else
                        Console.WriteLine("Pas dans le tableau");
                }//if
            }//while
        }
    }
}
  • 27-62. satırlar: Saisie yöntemi, klavyeden girilen éléments dizisinin elemanlarını alır. Dizinin boyutunu önceden belirleyemediğimiz için (nihai boyutunu bilmiyoruz), her yeni eleman eklendiğinde dizinin boyutunu yeniden ayarlamak zorunda kalırız (satır 57). Daha verimli bir algoritma, tabloya N'lik öğe grupları halinde yer ayırmak olurdu. Ancak bir tablo, yeniden boyutlandırılmak üzere tasarlanmamıştır. Bu durum, bir liste (ArrayList, List<T>) kullanılarak daha iyi ele alınabilir.
  • 75-113. satırlar: Recherche yöntemi, éléments dizisinde klavyeden girilen bir öğeyi aramaya olanak tanır. Arama yöntemi, dizinin sıralı olup olmamasına göre farklılık gösterir. Sıralı olmayan bir dizi için, 106. satırdaki IndexOf yöntemi kullanılarak doğrusal arama yapılır. Sıralı bir dizi için ise, 103. satırdaki BinarySearch yöntemi kullanılarak ikili arama yapılır.
  • 18. satır: éléments tablosu sıralanır. Burada, tek bir parametreye sahip olan Sort yönteminin bir varyantı kullanılır: sıralanacak tablo. Dizinin elemanlarını karşılaştırmak için kullanılan sıralama ilişkisi, bu elemanların örtük sıralamasıdır. Burada elemanlar sayısaldır. Sayıların doğal sıralaması kullanılır.

Ekran sonuçları şöyledir:

Elément (réel) 0 du tableau (rien pour terminer) : 3,6
Elément (réel) 1 du tableau (rien pour terminer) : 7,4
Elément (réel) 2 du tableau (rien pour terminer) : -1,5
Elément (réel) 3 du tableau (rien pour terminer) : -7
Elément (réel) 4 du tableau (rien pour terminer) :
Tableau non trié----------------------------------
3,6
7,4
-1,5
-7
Elément cherché (rien pour arrêter) :
7,4
Trouvé en position 1
Elément cherché (rien pour arrêter) :
0
Pas dans le tableau
Elément cherché (rien pour arrêter) :

Tableau trié--------------------------------------
-7
-1,5
3,6
7,4
Elément cherché (rien pour arrêter) :
7,4
Trouvé en position 3
Elément cherché (rien pour arrêter) :
0
Pas dans le tableau
Elément cherché (rien pour arrêter) :

5.4. Genel koleksiyonlar

Tablonun yanı sıra, öğe koleksiyonlarını depolamak için çeşitli sınıflar mevcuttur. System.Collections.Generic ad alanında genel sürümler, System.Collections ad alanında ise genel olmayan sürümler bulunmaktadır. Sıkça kullanılan iki genel koleksiyonu tanıtacağız: liste ve sözlük.

Genel koleksiyonların listesi şöyledir:

Image

5.4.1. List<T> jenerik sınıfı

System.Collections.Generic.List<T> sınıfı, programın çalışması sırasında boyutu değişen T türündeki nesnelerden oluşan koleksiyonları uygulamaya olanak tanır. List<T> türündeki bir nesne, neredeyse bir dizi gibi işlenir. Dolayısıyla, l listesinin i elemanı l[i] olarak gösterilir.

Ayrıca, herhangi bir nesneye ait referansları depolayabilen, jenerik olmayan bir liste türü de mevcuttur: ArrayList. ArrayList, işlevsel olarak List<Object>. ile eşdeğerdir. Bir ArrayList nesnesi şu şekildedir:

Yukarıda, listenin 0, 1 ve i numaralı öğeleri farklı türlerdeki nesnelere işaret etmektedir. Bir nesneye ArrayList listesine referans eklenmeden önce nesnenin oluşturulması gerekir. Bir ArrayList nesnesi nesne referanslarını depolasa da, bu nesneye sayılar da depolanabilir. Bu, Boxing adlı bir mekanizma aracılığıyla gerçekleştirilir: sayı, Object türünde bir O nesnesine kapsüllenir ve listeye O nesnesinin referansı kaydedilir. Bu, geliştirici için şeffaf bir mekanizmadır. Böylece şu şekilde yazılabilir:

ArrayList liste=new ArrayList();
liste.Add(4);

Bu, aşağıdaki sonucu verecektir:

Yukarıda, 4 sayısı bir O nesnesine kapsüllenmiştir ve O referansı listede saklanmıştır. Bunu geri almak için şu şekilde yazabiliriz:


            int i = (int)liste[0];

Object -> int işlemi, Unboxing olarak adlandırılır. Bir liste tamamen int türlerinden oluşuyorsa, onu List<int> olarak tanımlamak performansı artırır. Çünkü bu durumda int türündeki sayılar, listenin dışındaki Object türlerinde değil, listenin içinde depolanır. Böylece Boxing / Unboxing işlemleri artık gerçekleşmez.

List<T> veya T bir sınıf ise, liste yine T türündeki nesnelerin referanslarını depolar:

İşte genel listelerin bazı özellikleri ve yöntemleri:

Özellikler

public int Count {get;}
nombre d'éléments de la liste
public int Capacity {get;}
nombre d'éléments que la liste peut contenir avant d'être redimensionnée. Ce
redimensionnement se fait automatiquement. Cette notion de capacité de liste
est analogue à celle de capacité décrite pour la classe StringBuilder paragraphe Erreur : source de la référence non trouvée.

Yöntemler

public void Add(T item)
ajoute item à la liste
public int BinarySearch<T>(T item)
rend la position de item dans la liste s'il s'y
trouve sinon un nombre <0
public int BinarySearch<T>(T item,
IComparer<T> comparateur)
idem mais le 2ième paramètre permet de comparer deux
éléments de la liste. L'interface IComparer<T> a été
présentée au paragraphe Erreur : source de la référence non trouvée.
public void Clear()
supprime tous les éléments de la liste
public bool Contains(T item)
rend True si item est dans la liste, False sinon
public void CopyTo(T[] tableau)
copie les éléments de la liste dans tableau.
public int IndexOf(T item)
rend la position de item dans tableau ou -1 si
valeur n'est pas trouvée.
public void Insert(T item, int index)
insère item à la position index de la liste
public bool Remove(T item)
supprime item de la liste. Rend True si l'opération
réussit, False sinon.
public void RemoveAt(int index)
supprime l'élément n° index de la liste
public void Sort(IComparer<T> comparateur)
trie la liste selon un ordre défini par comparateur.
 Cette méthode a été présentée paragraphe Erreur : source de la référence non trouvée.
public void Sort()
trie la liste selon l'ordre défini par le type des
éléments de la liste
public T[] ToArray()
rend les éléments de la liste sous forme de tableau

Daha önce ele aldığımız Array türündeki nesneyle ilgili örneği tekrar ele alalım ve şimdi bunu List<T>. türündeki bir nesneyle işleyelim. Liste, tabloya benzer bir nesne olduğu için kodda çok az değişiklik oluyor. Yalnızca dikkate değer değişiklikleri sunuyoruz:


using System;
using System.Collections.Generic;

namespace Chap3 {
    class Program {
        // arama türü
        enum TypeRecherche { linéaire, dichotomique };

        // ana yöntem
        static void Main(string[] args) {
            // klavyeden girilen liste öğelerini okuma
            List<double> éléments;
            Saisie(out éléments);
            // öğe sayısı
            Console.WriteLine("La liste a {0} éléments et une capacité de {1} éléments", éléments.Count, éléments.Capacity);
            // sıralanmamış listenin görüntülenmesi
            Affiche("Liste non triée", éléments);
            // Sıralanmamış listede doğrusal arama
            Recherche(éléments, TypeRecherche.linéaire);
            // Listenin sıralanması
            éléments.Sort();
            // sıralanmış liste görüntüleme
            Affiche("Liste triée", éléments);
            // Sıralanmış listede ikili arama
            Recherche(éléments, TypeRecherche.dichotomique);
        }

        // Liste öğelerinin değerlerinin girilmesi
        // elemanlar: yöntem tarafından oluşturulan listeye referans
        static void Saisie(out List<double> éléments) {
...
            // Başlangıçta liste boştur
            éléments = new List<double>();
            // Liste öğelerini girme döngüsü
            while (!terminé) {
...
                // hata yoksa
                if (!erreur) {
                    // listeye bir öğe daha eklenir
                    éléments.Add(élément);
                }
            }//while
        }

        // sayılabilir bir nesnenin öğelerini görüntülemek için genel yöntem
        static void Affiche<T>(string texte, IEnumerable<T> éléments) {
            Console.WriteLine(texte.PadRight(50, '-'));
            foreach (T élément in éléments) {
                Console.WriteLine(élément);
            }
        }

        // listede bir öğeyi arama
        // elemanlar: gerçek sayı listesi
        // TypeRecherche: ikili veya doğrusal
        static void Recherche(List<double> éléments, TypeRecherche type) {
...
            while (!terminé) {
...
                // hata yoksa
                if (!erreur) {
                    // listede eleman aranıyor
                    if (type == TypeRecherche.dichotomique)
                        // ikili arama
                        i = éléments.BinarySearch(élément);
                    else
                        // doğrusal arama
                        i = éléments.IndexOf(élément);
                    // Cevabın görüntülenmesi
...
                }//if
            }//while
        }
    }
}
  • 46-51. satırlar: Affiche<T> genel yöntemi iki parametre kabul eder:
  • 1. parametre yazılacak metindir
  • 2. parametre, IEnumerable<T> jenerik arayüzünü uygulayan bir nesnedir:
1
2
3
4
public interface IEnumerable<T>{
    IEnumerator GetEnumerator();
    IEnumerator<T> GetEnumerator();
}
  1. satırdaki foreach( T öğe in öğeler) yapısı, IEnumerable arayüzünü uygulayan tüm éléments nesneleri için geçerlidir. Diziler (Array) ve listeler (List<T>), IEnumerable<T> arayüzünü uygular. Bu nedenle Affiche yöntemi, hem tabloları hem de listeleri görüntülemek için uygundur.

Programın çalıştırma sonuçları, Array sınıfının kullanıldığı örnektekiyle aynıdır.

5.4.2. Dictionary<TKey,TValue> sınıfı

System.Collections.Generic.Dictionary<TKey,TValue> sınıfı, bir sözlükün uygulanmasına olanak tanır. Bir sözlük, iki sütunlu bir tablo olarak düşünülebilir:

anahtar
değer
anahtar1
değer1
anahtar2
değer2
..
...

Dictionary<TKey,TValue> sınıfında anahtarlar Tkey türündedir, değerler ise TValue türündedir. Anahtarlar benzersizdir; c.a.d. Yani iki anahtarın aynı olması mümkün değildir. TKey ve TValue türleri sınıfları ifade ediyorsa, böyle bir sözlük şu şekilde görünebilir:

D sözlüğündeki C anahtarına atanan değer, D[C] notasyonu ile elde edilir. Bu değer hem okunabilir hem de yazılabilir. Dolayısıyla şöyle yazabiliriz:

1
2
3
4
5
TValue v=...;
TKey c=...;
Dictionary<TKey,TValue> D=new Dictionary<TKey,TValue>();
D[c]=v;
v=D[c];

c anahtarı D sözlüğünde mevcut değilse, D[c] notasyonu bir istisna oluşturur.

Dictionary<TKey,TValue> sınıfının başlıca yöntemleri ve özellikleri şunlardır:

Oluşturucular

public Dictionary<TKey,TValue>()
constructeur sans paramètres - construit un dictionnaire vide.
Il existe plusieurs autres constructeurs.

Özellikler

public int Count {get;}
nombre d'entrées (clé, valeur) dans le dictionnaire
public Dictionary<TKey,TValue>.KeyCollection Keys {get;}
collection des clés du dictionnaire.
public Dictionary<TKey,TValue>.ValueCollection Values {get;}
collection des valeurs du dictionnaire.

Yöntemler

public void Add(TKey key, TValue value)
ajoute le couple (key, value) au dictionnaire
public void Clear()
supprime tous les couples du dictionnaire
public bool ContainsKey (TKey key)
rend True si key est une clé du dictionnaire,
False sinon
public bool ContainsValue (TValue value)
rend True si value est une valeur du dictionnaire,
False sinon
public void CopyTo(T[] tableau)
copie les éléments de la liste dans tableau.
public bool Remove(TKey key)
supprime du dictionnaire le couple de clé key.
Rend True si l'opération réussit, False sinon.
public bool TryGetValue(TKey key,
out TValue value)
rend dans value, la valeur associée à la clé key si
cette dernière existe, sinon rend la valeur par
défaut du type TValue (0 pour les nombres, false
pour les booléens, null pour les références d'objet)

Aşağıdaki örnek programı ele alalım:


using System;
using System.Collections.Generic;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // <string,int> türünde bir sözlük oluşturma
            string[] liste = { "jean:20", "paul:18", "mélanie:10", "violette:15" };
            string[] champs = null;
            char[] séparateurs = new char[] { ':' };
            Dictionary<string,int> dico = new Dictionary<string,int>();
            for (int i = 0; i <liste.Length; i++) {
                champs = liste[i].Split(séparateurs);
                dico[champs[0]]= int.Parse(champs[1]);
            }//for
            // sözlükteki öğe sayısı
            Console.WriteLine("Le dictionnaire a " + dico.Count + " éléments");
            // anahtarların listesi
            Affiche("[Liste des clés]",dico.Keys);
            // değerlerin listesi
            Affiche("[Liste des valeurs]", dico.Values);
            // anahtar ve değerlerin listesi
            Console.WriteLine("[Liste des clés & valeurs]");
            foreach (string clé in dico.Keys) {
                Console.WriteLine("clé=" + clé + " valeur=" + dico[clé]);
            }
            // "paul" anahtarı siliniyor
            Console.WriteLine("[Suppression d'une clé]");
            dico.Remove("paul");
            // anahtar ve değer listesi
            Console.WriteLine("[Liste des clés & valeurs]");
            foreach (string clé in dico.Keys) {
                Console.WriteLine("clé=" + clé + " valeur=" + dico[clé]);
            }
            // sözlükte arama
            String nomCherché = null;
            Console.Write("Nom recherché (rien pour arrêter) : ");
            nomCherché = Console.ReadLine().Trim();
            int value;
            while (!nomCherché.Equals("")) {
                dico.TryGetValue(nomCherché, out value);
                if (value!=0) {
                    Console.WriteLine(nomCherché + "," + value);
                } else {
                    Console.WriteLine("Nom " + nomCherché + " inconnu");
                }
                // sonraki arama
                Console.Out.Write("Nom recherché (rien pour arrêter) : ");
                nomCherché = Console.ReadLine().Trim();
            }//while
        }

        // sayılabilir bir türün öğelerini görüntülemek için genel yöntem
        static void Affiche<T>(string texte, IEnumerable<T> éléments) {
            Console.WriteLine(texte.PadRight(50, '-'));
            foreach (T élément in éléments) {
                Console.WriteLine(élément);
            }
        }

    }
}
  • 8. satır: <string,int> sözlüğünü başlatmak için kullanılacak bir string dizisi
  • 11. satır: <string,int> sözlüğü
  • 12-15. satırlar: 8. satırdaki string dizisinden sözlüğün başlatılması
  • 17. satır: sözlüğün giriş sayısı
  • 19. satır: sözlüğün anahtarları
  • 21. satır: sözlüğün değerleri
  • 29. satır: sözlükten bir girdinin silinmesi
  • satır 41: sözlükte bir anahtarın aranması. Eğer mevcut değilse, TryGetValue yöntemi, value'e 0 değerini atayacaktır, çünkü value sayısal türdendir. Bu teknik, burada yalnızca 0 değerinin sözlükte bulunmadığı bilindiği için kullanılabilir.

Çalıştırma sonuçları şöyledir:

Le dictionnaire a 4 éléments
[Liste des clés]----------------------------------
jean
paul
mélanie
violette
[Liste des valeurs]-------------------------------
20
18
10
15
[Liste des clés & valeurs]
clé=jean valeur=20
clé=paul valeur=18
clé=mélanie valeur=10
clé=violette valeur=15
[Suppression d'une clé]
[Liste des clés & valeurs]
clé=jean valeur=20
clé=mélanie valeur=10
clé=violette valeur=15
Nom recherché (rien pour arrêter) : violette
violette,15
Nom recherché (rien pour arrêter) : x
Nom x inconnu

5.5. Metin dosyaları

5.5.1. StreamReader sınıfı

System.IO.StreamReader sınıfı, bir metin dosyasının içeriğini okumaya olanak tanır. Aslında, dosya olmayan akışları da işleyebilir. İşte bu sınıfın bazı özellikleri ve yöntemleri:

Oluşturucular

public StreamReader(string path)
construit un flux de lecture à partir du fichier de chemin path. Le
contenu du fichier peut être encodé de diverses façons. Il existe un
constructeur qui permet de préciser le codage utilisé. Par défaut,
c'est le codage UTF-8 qui est utilisé.

Özellikler

public bool EndOfStream {get;}
True si le flux a été lu entièrement

Yöntemler

public void Close()
ferme le flux et libère les ressources allouées pour
sa gestion. A faire obligatoirement après
exploitation du flux.
public override int Peek()
rend le caractère suivant du flux sans le consommer.
Un Peek supplémentaire rendrait donc le même
caractère.
public override int Read()
rend le caractère suivant du flux et avance d'un
caractère dans le flux.
public override int Read(char[] buffer,
int index, int count)
lit count caractères dans le flux et les met dans
buffer à partir de la position index. Rend le nombre
de caractères lus - peut être 0.
public override string ReadLine()
rend la ligne suivante du flux ou null si on était à
la fin du flux.
public override string ReadToEnd()
rend la fin du flux ou "" si on était à la fin du
flux.

İşte bir örnek:


using System;
using System.IO;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // çalıştırma dizini
            Console.WriteLine("Répertoire d'exécution : "+Environment.CurrentDirectory);
            string ligne = null;
            StreamReader fluxInfos = null;
            // dosya içeriğini okuma infos.txt
            try {
                // okuma 1
                Console.WriteLine("Lecture 1----------------");
                using (fluxInfos = new StreamReader("infos.txt")) {
                    ligne = fluxInfos.ReadLine();
                    while (ligne != null) {
                        Console.WriteLine(ligne);
                        ligne = fluxInfos.ReadLine();
                    }
                }
                // okuma 2
                Console.WriteLine("Lecture 2----------------");
                using (fluxInfos = new StreamReader("infos.txt")) {
                    Console.WriteLine(fluxInfos.ReadToEnd());
                }
            } catch (Exception e) {
                Console.WriteLine("L'erreur suivante s'est produite : " + e.Message);
            }
        }
    }
}
  • 8. satır: çalıştırma dizinini gösterir
  • 12. ve 27. satırlar: olası bir istisnayı yönetmek için bir try / catch.
  • 15. satır: using flux=new StreamReader(...) yapısı, akış kullanıldıktan sonra onu açıkça kapatmak zorunda kalmamak için bir kolaylıktır. Bu kapatma işlemi, using kapsamından çıkıldığında otomatik olarak gerçekleştirilir.
  • 15. satır: Okunan dosyanın adı infos.txt'tir. Bu, göreceli bir ad olduğundan, 8. satırda gösterilen yürütme dizininde aranacaktır. Eğer orada bulunmazsa, bir istisna atılır ve try / catch ile yönetilir.
  • 16-20. satırlar: Dosya satır satır okunur
  • 25. satır: Dosya tek seferde okunur

infos.txt dosyası şöyledir:

12620:0:0
13190:0,05:631
15640:0,1:1290,5

ve C# projesinin şu klasörüne yerleştirilmiştir:

Ctrl-F5 tuşlarıyla proje çalıştırıldığında, bin/Release dosyasının yürütme dosyası olduğunu göreceğiz.

Çalıştırma işlemi şu sonuçları verir:

1
2
3
4
5
6
7
8
9
Répertoire d'exécution : C:\data\2007-2008\c# 2008\poly\Chap3\07\bin\Release
Lecture 1----------------
12620:0:0
13190:0,05:631
15640:0,1:1290,5
Lecture 2----------------
12620:0:0
13190:0,05:631
15640:0,1:1290,5
  1. satıra xx.txt dosya adını yazarsak şu sonuçları elde ederiz:
1
2
3
Répertoire d'exécution : C:\data\2007-2008\c# 2008\poly\Chap3\07\bin\Release
Lecture 1----------------
L'erreur suivante s'est produite : Could not find file 'C:\...\Chap3\07\bin\Release\xx.txt'.

5.5.2. StreamWriter sınıfı

System.IO.StreamReader sınıfı, bir metin dosyasına yazmaya olanak tanır. StreamReader sınıfı gibi, bu sınıf da aslında dosya olmayan akışları işleyebilir. İşte bu sınıfın bazı özellikleri ve yöntemleri:

Oluşturucular

public StreamWriter(string path)
construit un flux d'écriture dans le fichier de chemin path. Le
contenu du fichier peut être encodé de diverses façons. Il existe un
constructeur qui permet de préciser le codage utilisé. Par défaut,
c'est le codage UTF-8 qui est utilisé.

Özellikler

public virtual bool AutoFlush
{get;set;}
fixe le mode d'écriture dans le fichier du buffer associé au flux. Si
égal à False, l'écriture dans le flux n'est pas immédiate : il y a
d'abord écriture dans une mémoire tampon puis dans le fichier lorsque la
mémoire tampon est pleine sinon l'écriture dans le fichier est immédiate
(pas de tampon intermédiaire). Par défaut c'est le mode tamponné qui est
utilisé. Le tampon n'est écrit dans le fichier que lorsqu'il est plein ou
bien lorsqu'on le vide explicitement par une opération Flush ou encore
lorsqu'on ferme le flux StreamWriter par une opération Close. Le mode
AutoFlush=False est le plus efficace lorsqu'on travaille avec des
fichiers parce qu'il limite les accès disque. C'est le mode par défaut
pour ce type de flux. Le mode AutoFlush=False ne convient pas à tous les
flux, notamment les flux réseau. Pour ceux-ci, qui souvent prennent place
dans un dialogue entre deux partenaires, ce qui est écrit par l'un des
partenaires doit être immédiatement lu par l'autre. Le flux d'écriture
doit alors être en mode AutoFlush=True.
public virtual string NewLine {get;set;}
les caractères de fin de ligne. Par défaut "\r\n". Pour un système Unix,
il faudrait utiliser "\n".

Yöntemler

public void Close()
ferme le flux et libère les ressources allouées pour sa
gestion. A faire obligatoirement après exploitation du flux.
public override void Flush()
écrit dans le fichier, le buffer du flux, sans attendre qu'il
soit plein.
public virtual void Write(T value)
écrit value dans le fichier associé au flux. Ici T n'est pas
un type générique mais symbolise le fait que la méthode
Write accepte différents types de paramètres (string, int,
object, ...). La méthode value.ToString est utilisée pour
produire la chaîne écrite dans le fichier.
public virtual void WriteLine(T value)
même chose que Write mais avec la marque de fin de ligne
(NewLine) en plus.

Şu örneği ele alalım:


using System;
using System.IO;

namespace Chap3 {
    class Program2 {
        static void Main(string[] args) {
            // çalıştırma dizini
            Console.WriteLine("Répertoire d'exécution : " + Environment.CurrentDirectory);
            string ligne = null;                        // bir metin satırı
            StreamWriter fluxInfos = null;    // metin dosyası
            try {
                // metin dosyasının oluşturulması
                using (fluxInfos = new StreamWriter("infos2.txt")) {
                    Console.WriteLine("Mode AutoFlush : {0}", fluxInfos.AutoFlush);
                    // klavyeden girilen satırı okuma
                    Console.Write("ligne (rien pour arrêter) : ");
                    ligne = Console.ReadLine().Trim();
                    // girilen satır boş olmadığı sürece döngü
                    while (ligne != "") {
                        // satırı metin dosyasına yazma
                        fluxInfos.WriteLine(ligne);
                        // klavyeden yeni satırı okuma
                        Console.Write("ligne (rien pour arrêter) : ");
                        ligne = Console.ReadLine().Trim();
                    }//while
                }
            } catch (Exception e) {
                Console.WriteLine("L'erreur suivante s'est produite : " + e.Message);
            }
        }
    }
}
  • 13. satır: Yine, akışı bir using(flux) işlemiyle açıkça kapatmak zorunda kalmamak için Close sözdizimini kullanıyoruz. Bu kapatma işlemi, using'ten çıkıldığında otomatik olarak gerçekleştirilir.
  • 11. ve 27. satırlardaki try / catch neden kullanılıyor? 13. satırda, /rep1/rep2/ .../fichier şeklinde bir dosya adı verebiliriz; ancak /rep1/rep2/... gibi var olmayan bir yol belirtilirse, fichier dosyasının oluşturulması imkansız hale gelir. Bu durumda bir istisna atılır. Başka olası istisna durumları da vardır (disk dolu, yetki yetersizliği, ...)

Çalıştırma sonuçları şöyledir:

1
2
3
4
5
Répertoire d'exécution : C:\data\2007-2008\c# 2008\poly\Chap3\07\bin\Release
Mode AutoFlush : False
ligne (rien pour arrêter) : 1ère ligne
ligne (rien pour arrêter) : 2ième ligne
ligne (rien pour arrêter) :

infos2.txt dosyası, projenin bin/Release klasöründe oluşturuldu:

 

5.6. İkili dosyalar

System.IO.BinaryReader ve System.IO.BinaryWriter sınıfları, ikili dosyaları okumak ve yazmak için kullanılır.

Şu uygulamayı ele alalım:

// pg, metin, ikili, günlük dosyalarının sözdizimi
// bir metin dosyası (text) okunur ve içeriği bir ikili dosyaya (bin) kaydedilir
// metin dosyası, isim : yaş biçiminde satırlara sahiptir; bunlar string ve int türlerinde bir yapıya kaydedilecektir
// (logs) bir log metin dosyasıdır

Metin dosyasının içeriği şöyledir:

1
2
3
4
5
6
7
8
9
paul : 10
helene : 15

jacques : 11
sylvain : 12
xx : -1

xx: yy : zz
xx : yy

Program şu şekildedir:


using System;
using System.IO;

// sözdizimi: pg metin bin logs
// bir metin dosyası (text) okunur ve içeriği bir ikili dosyaya (bin) kaydedilir
// metin dosyası, "ad: yaş" biçiminde satırlara sahiptir; bunlar bir string, int yapısına kaydedilecektir
// (logs) bir log metin dosyasıdır

namespace Chap3 {
    class Program {
        static void Main(string[] arguments) {
            // 3 argüman gereklidir
            if (arguments.Length != 3) {
                Console.WriteLine("syntaxe : pg texte binaire log");
                Environment.Exit(1);
            }//if

            // değişkenler
            string ligne=null;
            string nom=null;
            int age=0;
            int numLigne = 0;
            char[] séparateurs = new char[] { ':' };
            string[] champs=null;
            StreamReader input = null;
            BinaryWriter output = null;
            StreamWriter logs = null;
            bool erreur = false;
            // metin dosyasını okuma - ikili dosyayı yazma
            try {
                // metin dosyasını okuma modunda açma
                input = new StreamReader(arguments[0]);
                // ikili dosyanın yazma modunda açılması
                output = new BinaryWriter(new FileStream(arguments[1], FileMode.Create, FileAccess.Write));
                // günlük dosyasını yazma modunda açma
                logs = new StreamWriter(arguments[2]);
                // metin dosyasını işleme
                while ((ligne = input.ReadLine()) != null) {
                    // bir satır daha
                    numLigne++;
                    // boş satır mı?
                    if (ligne.Trim() == "") {
                        // yoksayılıyor
                        continue;
                    }
                    // "ad: yaş" satırı
                    champs = ligne.Split(séparateurs);
                    // 2 alan gerekiyor
                    if (champs.Length != 2) {
                        // hatayı günlüğe kaydediyoruz
                        logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un nombre de champs incorrect", numLigne, arguments[0]);
                        // sonraki satır
                        continue;
                    }//if
                    // 1. alan boş olmamalı
                    erreur = false;
                    nom = champs[0].Trim();
                    if (nom == "") {
                        // hatayı günlüğe kaydediyoruz
                        logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un nom vide", numLigne, arguments[0]);
                        erreur = true;
                    }
                    // ikinci alan >=0 olan bir tamsayı olmalıdır
                    if (!int.TryParse(champs[1],out age) || age<0) {
                        // hata günlüğüne kaydedilir
                        logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un âge [{2}] incorrect", numLigne, arguments[0], champs[1].Trim());
                        erreur = true;
                    }//eğer
                    // hata yoksa, veriler ikili dosyaya yazılır
                    if (!erreur) {
                        output.Write(nom);
                        output.Write(age);
                    }
                    // sonraki satır
                }//while
            }catch(Exception e){
                Console.WriteLine("L'erreur suivante s'est produite : {0}", e.Message);
            } finally {
                // dosyaları kapat
                if(input!=null) input.Close();
                if(output!=null) output.Close();
                if(logs!=null) logs.Close();
            }
        }
    }
}

BinaryWriter sınıfıyla ilgili işlemler üzerinde duralım:

  • 34. satır: BinaryWriter nesnesi, işlem tarafından açılır

            output=new BinaryWriter(new FileStream(arguments[1],FileMode.Create,FileAccess.Write));

Yapıcı argümanı bir akış (Stream) olmalıdır. Burada, aşağıda verilen bir dosyadan (FileStream) oluşturulan bir akış söz konusudur:

  • (devamı)
    • adı
    • yapılacak işlem, burada dosyayı oluşturmak için FileMode.Create
    • erişim türü; burada dosyaya yazma erişimi için FileAccess.Write
  • 70-73. satırlar: yazma işlemleri
             // veriler ikili dosyaya yazılır
            output.Write(nom);
            output.Write(age);

BinaryWriter sınıfı, farklı basit veri türlerini yazmak için Write yönteminin çeşitli üstü yazılmış versiyonlarına sahiptir

  • satır 81: akışın kapatılması işlemi
        output.Close();

Main yönteminin üç argümanı, projeye (özellikleri aracılığıyla) [1] verilir ve işlenecek metin dosyası bin/Release klasörüne yerleştirilir. [2]:

Aşağıdaki [personnes1.txt] dosyası ile:

1
2
3
4
5
6
7
8
9
paul : 10
helene : 15

jacques : 11
sylvain : 12
xx : -1

xx: yy : zz
xx : yy

çalıştırma sonuçları şu şekildedir:

  • [1] dosyasında, bu şekilde oluşturulan [personnes1.bin] ikili dosyası ve [logs.txt] günlük dosyası bulunur. Günlük dosyasının içeriği şöyledir:
1
2
3
La ligne n° [6] du fichier [personnes1.txt] a un âge [-1] incorrect
La ligne n° [8] du fichier [personnes1.txt] a un nombre de champs incorrect
La ligne n° [9] du fichier [personnes1.txt] a un âge [yy] incorrect

[personnes1.bin] ikili dosyasının içeriği, aşağıdaki program tarafından bize verilecektir. Bu program da üç argüman kabul eder:

// pg bin metin günlükleri sözdizimi
// bir ikili dosyayı (bin) okur ve içeriğini bir metin dosyasına (text) kaydeder
// ikili dosyanın yapısı string, int şeklindedir
// metin dosyası, "ad: yaş" biçiminde satırlara sahiptir
// logs, bir günlük metin dosyasıdır

Dolayısıyla tersi işlemi yapıyoruz. Bir ikili dosyayı okuyarak bir metin dosyası oluşturuyoruz. Oluşturulan metin dosyası orijinal dosyayla aynıysa, metin --> ikili --> metin dönüşümünün başarılı olduğunu anlarız. Kod şu şekildedir:


using System;
using System.IO;

// sözdizimi: pg, bin, metin, logs
// bin adlı ikili dosya okunur ve içeriği bir metin dosyasına (metin) kaydedilir
// ikili dosyanın yapısı string, int şeklindedir
// metin dosyası, "ad: yaş" biçiminde satırlardan oluşur
// logs, bir günlük metin dosyasıdır

namespace Chap3 {
    class Program2 {
        static void Main(string[] arguments) {
            // 3 argüman gereklidir
            if (arguments.Length != 3) {
                Console.WriteLine("syntaxe : pg binaire texte log");
                Environment.Exit(1);
            }//if

            // değişkenler
            string nom = null;
            int age = 0;
            int numPersonne = 1;
            BinaryReader input = null;
            StreamWriter output = null;
            StreamWriter logs = null;
            bool fini;
            // ikili dosya okuma - metin dosyası yazma
            try {
                // ikili dosyanın okuma modunda açılması
                input = new BinaryReader(new FileStream(arguments[0], FileMode.Open, FileAccess.Read));
                // metin dosyasını yazma modunda açma
                output = new StreamWriter(arguments[1]);
                // günlük dosyasını yazma modunda açma
                logs = new StreamWriter(arguments[2]);
                // ikili dosyanın işlenmesi
                fini = false;
                while (!fini) {
                    try {
                        // ad okuma
                        nom = input.ReadString().Trim();
                        // yaş okuma
                        age = input.ReadInt32();
                        // metin dosyasına yazma
                        output.WriteLine(nom + ":" + age);
                        // sonraki kişi
                        numPersonne++;
                    } catch (EndOfStreamException) {
                        fini = true;
                    } catch (Exception e) {
                        logs.WriteLine("L'erreur suivante s'est produite à la lecture de la personne n° {0} : {1}", numPersonne, e.Message);
                    }
                }//while
            } catch (Exception e) {
                Console.WriteLine("L'erreur suivante s'est produite : {0}", e.Message);
            } finally {
                // dosyaları kapatma
                if (input != null)
                    input.Close();
                if (output != null)
                    output.Close();
                if (logs != null)
                    logs.Close();
            }
        }
    }
}

BinaryReader sınıfıyla ilgili işlemler üzerinde biraz duralım:

  • 30. satır: BinaryReader nesnesi şu işlemle açılır:

            input=new BinaryReader(new FileStream(arguments[0],FileMode.Open,FileAccess.Read));

Oluşturucunun argümanı bir akış (Stream) olmalıdır. Burada, bir dosyadan (FileStream) oluşturulan bir akış söz konusudur ve bu akış için şunlar verilmiştir:

  • (devamı)
    • adı
    • yapılacak işlem, burada mevcut bir dosyayı açmak için FileMode.Open
    • erişim türü; burada dosyaya okuma erişimi için FileAccess.Read
  • 40. ve 42. satırlar: okuma işlemleri
nom=input.ReadString().Trim();
age=input.ReadInt32();

BinaryReader sınıfı, çeşitli basit veri türlerini okumak için farklı yöntemlere sahiptir: ReadXX

  • 60. satır: akış kapatma işlemi
        input.Close();

personnes1.txt'i personnes1.bin'e, ardından personnes1.bin'i personnes2.txt2'e dönüştüren iki programı arka arkaya çalıştırırsak şu sonuçları elde ederiz:

  • [1]'e dönüştürüldü; proje, ikinci uygulamayı çalıştıracak şekilde yapılandırılmıştır
  • [2]'te, Main'e aktarılan argümanlar
  • [3]'te, uygulamanın çalıştırılmasıyla oluşturulan dosyalar yer alır.

[personnes2.txt] dosyasının içeriği şöyledir:

1
2
3
4
paul:10
helene:15
jacques:11
sylvain:12

5.7. Düzenli ifadeler

System.Text.RegularExpressions.Regex sınıfı, düzenli ifadelerin kullanımına izin verir. Bunlar, bir karakter dizisinin biçimini test etmeyi sağlar. Böylece, bir tarihi temsil eden dizinin gerçekten gg/aa/yy biçiminde olup olmadığını doğrulayabiliriz. Bunun için bir şablon kullanılır ve dizi bu şablonla karşılaştırılır. Dolayısıyla bu örnekte, j, m ve a rakamlar olmalıdır. Geçerli bir tarih biçiminin şablonu "\d\d/\d\d/\d\d" şeklindedir; burada \d sembolü bir rakamı ifade eder. Şablonda kullanılabilecek semboller şunlardır:

Caractère
Açıklama
\ 
Aşağıdaki karakteri özel karakter veya sabit karakter olarak işaretler. Örneğin, "n" karakteri "n" karakterine karşılık gelir. "\n" karakteri yeni satır karakterine karşılık gelir. "\\" dizisi "\" karakterine karşılık gelirken, "\(" karakteri "(" karakterine karşılık gelir.
^ 
Girişin başlangıcına karşılık gelir.
$ 
Girişin sonuna karşılık gelir.
* 
Önceki karakterin sıfır kez veya birden fazla kez tekrarına karşılık gelir. Dolayısıyla, "zo*" "z" veya "zoo"ya karşılık gelir.
+ 
Önceki karakteri bir veya daha fazla kez eşleştirir. Dolayısıyla, "zo+" "zoo" ile eşleşir, ancak "z" ile eşleşmez.
? 
Önceki karakterle sıfır veya bir kez eşleşir. Örneğin, "a?ve?" "lever" kelimesindeki "ve" ile eşleşir.
.
Satır sonu karakteri hariç, herhangi bir tekil karakterle eşleşir.
(modèle) 
modèle'i arar ve eşleşmeyi saklar. Eşleşen alt dize, elde edilen Matches koleksiyonundan Item [0]...[n] kullanılarak çıkarılabilir. Parantez ( ) içindeki karakterlerle eşleşmeleri bulmak için "\(" veya "\)" kullanın.
x|y
x veya y ile eşleşir. Örneğin, "z|foot" correspond à "z" ou à "foot". "(z|f)oo" ifadesi "zoo" veya "foo" ile eşleşir.
{n}
n, negatif olmayan bir tam sayıdır. Tam olarak n çarpı karakter değerine karşılık gelir. Örneğin, "o{2}", "Bob," içindeki "o" harfine değil, "fooooot" içindeki ilk iki "o" harfine karşılık gelir.
{n,} 
n, negatif olmayan bir tamsayıdır. Karakterin en az n katına karşılık gelir. Örneğin, "o{2,}", "Bob" kelimesindeki "o" harfine değil, "fooooot" kelimesindeki tüm "o" harflerine karşılık gelir. "o{1,}", "o+" ile eşdeğerdir ve "o{0,}", "o*" ile eşdeğerdir.
{n,m} 
m ve n, negatif olmayan tamsayılardır. Karakterin en az n ve en fazla m kez tekrarlandığı durumlara karşılık gelir. Örneğin, "o{1,3}", "foooooot" kelimesindeki ilk üç "o" harfine karşılık gelir ve "o{0,1}", "o?" ile eşdeğerdir.
[xyz] 
Karakter kümesi. Belirtilen karakterlerden birine karşılık gelir. Örneğin, "[abc]", "plat" kelimesindeki "a" harfine karşılık gelir.
[^xyz] 
Negatif karakter kümesi. Belirtilmeyen tüm karakterlere karşılık gelir. Örneğin, "[^abc]", "plat" kelimesindeki "p" harfine karşılık gelir.
[a-z] 
Karakter aralığı. Belirtilen dizideki tüm karakterlere karşılık gelir. Örneğin, "[a-z]", "a" ile "z" arasındaki tüm küçük harflere karşılık gelir.
[^m-z] 
Negatif karakter aralığı. Belirtilen dizide bulunmayan tüm karakterlere karşılık gelir. Örneğin, "[^m-z]", "m" ile "z" arasında bulunmayan tüm karakterlere karşılık gelir.
\b 
Bir kelimeyi temsil eden sınırla, başka bir deyişle bir kelime ile boşluk arasındaki konuma karşılık gelir. Örneğin, "er\b", "lever" kelimesindeki "er" ile eşleşir, ancak "verbe" kelimesindeki "er" ile eşleşmez.
\B 
Bir kelimeyi temsil etmeyen bir sınıra karşılık gelir. "en*t\B", "bien entendu" kelimesindeki "ent" kısmına karşılık gelir.
\d 
Bir rakamı temsil eden bir karaktere karşılık gelir. [0-9] ile eşdeğerdir.
\D 
Rakam olmayan bir karaktere karşılık gelir. [^0-9] ile eşdeğerdir.
\f 
Sayfa sonu karakterine karşılık gelir.
\n 
Yeni satır karakterine karşılık gelir.
\r 
Satır başı karakterine karşılık gelir.
\s 
Boşluk, sekme, sayfa sonu vb. dahil olmak üzere tüm boşluklara karşılık gelir. "[ \f\n\r\t\v]" ile eşdeğerdir.
\S 
Beyaz olmayan tüm boşluk karakterlerine karşılık gelir. "[^ \f\n\r\t\v]" ile eşdeğerdir.
\t 
Bir sekme karakterine karşılık gelir.
\v 
Dikey sekme karakterine karşılık gelir.
\w 
Bir kelimeyi temsil eden ve alt çizgi içeren herhangi bir karaktere karşılık gelir. "[A-Za-z0-9_]" ile eşdeğerdir.
\W 
Bir kelimeyi temsil etmeyen herhangi bir karaktere karşılık gelir. "[^A-Za-z0-9_]" ile eşdeğerdir.
\num 
num'e karşılık gelir; burada num pozitif bir tam sayıdır. Kaydedilmiş eşleşmelere atıfta bulunur. Örneğin, "(.)\1" iki ardışık aynı karaktere karşılık gelir.
\n
n’e karşılık gelir; burada n, sekizlik bir kaçış değeridir. Sekizlik kaçış değerleri 1, 2 veya 3 basamaklı olmalıdır. Örneğin, "\11" ve "\011" her ikisi de bir sekme karakterine karşılık gelir. "\0011", "\001" ve "1"e eşittir. Sekizlik kaçış değerleri 256'yı aşmamalıdır. Aşılması durumunda, ifadede yalnızca ilk iki rakam dikkate alınır. ASCII kodlarının düzenli ifadelerde kullanılmasına olanak tanır.
\xn
n'e karşılık gelir; burada n onaltılık bir kaçış değeridir. Onaltılık kaçış değerleri mutlaka iki basamaklı olmalıdır. Örneğin, "\x41" "A" harfine karşılık gelir. "\x041" ise "\x04" ve "1" değerlerinin birleşimine eşittir. ASCII kodlarının düzenli ifadelerde kullanılmasına izin verir.

Bir şablondaki öğe, 1 veya daha fazla kopya halinde bulunabilir. 1 rakamı temsil eden \d sembolüyle ilgili birkaç örneği ele alalım:

şablon
anlam
\d
bir rakam
\d?
0 veya 1 rakam
\d*
0 veya daha fazla rakam
\d+
1 veya daha fazla rakam
\d{2}
2 rakam
\d{3,}
en az 3 rakam
\d{5,7}
5 ile 7 arasında rakam

Şimdi, bir karakter dizisi için beklenen biçimi tanımlayabilen bir model düşünelim:

aranan dize
model
gg/mm/yy formatında bir tarih
\d{2}/\d{2}/\d{2}
hh:mm:ss biçiminde bir saat
\d{2}:\d{2}:\d{2}
işaretsiz bir tamsayı
\d+
boş olabilir bir boşluk dizisi
\s*
önünde veya arkasında boşluk bulunabilen işaretsiz bir tamsayı
\s*\d+\s*
işareti olan bir tamsayı; başında veya sonunda boşluk olabilir
\s*[+|-]?\s*\d+\s*
önünde veya arkasında boşluk bulunabilen işaretsiz bir gerçek sayı
\s*\d+(.\d*)?\s*
işareti olan veya olmayan bir gerçek sayı; başında veya sonunda boşluk olabilir
\s*[+|]?\s*\d+(.\d*)?\s*
juste kelimesini içeren bir dize
\bjuste\b
  

Dizgide kalıbın nerede aranacağını belirtebiliriz:

şablon
anlam
^şablon
model, dizgenin başında yer alır
model$
şablon dizgiyi sonlandırır
^desen$
şablon dizgiyi başlatır ve bitirir
desen
desen, dizgenin başından başlayarak her yerinde aranır.
aranan dize
desen
ünlem işaretiyle biten bir dize
!$
nokta ile biten bir dize
\.$
// dizisiyle başlayan bir dize
^//
yalnızca bir kelimeden oluşan ve isteğe bağlı olarak öncesinde veya sonrasında boşluklar bulunan bir dize
^\s*\w+\s*$
önünde veya arkasında boşluk olabilecek iki kelimeden oluşan bir dize
^\s*\w+\s*\w+\s*$
secret kelimesini içeren bir dize
\bsecret\b

Bir şablonun alt kümeleri "çıkarılabilir". Böylece, bir dizgenin belirli bir şablona uyup uymadığını kontrol etmekle kalmaz, aynı zamanda bu dizgede parantezlerle çevrelenmiş şablonun alt kümelerine karşılık gelen öğeleri de çıkarabiliriz. Örneğin, gg/aa/yyyy biçiminde bir tarih içeren bir dizgiyi analiz ediyorsak ve bu tarihin gg, aa, yyyy öğelerini de ayıklamak istiyorsak, (\d\d)/(\d\d)/(\d\d) şablonunu kullanırız.

5.7.1. Bir dizgenin belirli bir modele uyup uymadığını kontrol etme

Regex türü bir nesne şu şekilde oluşturulur:

public Regex(string pattern)
construit un objet "expression régulière" à partir d'un modèle passé
en paramètre (pattern)

Şablon düzenli ifade oluşturulduktan sonra, IsMatch yöntemi kullanılarak karakter dizileriyle karşılaştırılabilir:

public bool IsMatch(string input)
vrai si la chaîne input correspond au modèle de l'expression
régulière

İşte bir örnek:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // bir düzenli ifade şablonu
            string modèle1 = @"^\s*\d+\s*$";
            Regex regex1 = new Regex(modèle1);
            // bir örneği şablonla karşılaştırma
            string exemplaire1 = "  123  ";
            if (regex1.IsMatch(exemplaire1)) {
                Console.WriteLine("[{0}] correspond au modèle [{1}]", exemplaire1, modèle1);
            } else {
                Console.WriteLine("[{0}] ne correspond pas au modèle [{1}]", exemplaire1, modèle1);
            }//if
            string exemplaire2 = "  123a  ";
            if (regex1.IsMatch(exemplaire2)) {
                Console.WriteLine("[{0}] correspond au modèle [{1}]", exemplaire2, modèle1);
            } else {
                Console.WriteLine("[{0}] ne correspond pas au modèle [{1}]", exemplaire2, modèle1);
            }//if
        }

    }
}

ve çalıştırma sonuçları:

[  123  ] correspond au modèle [^\s*\d+\s*$]
[  123a  ] ne correspond pas au modèle [^\s*\d+\s*$]

5.7.2. Bir dizede bir deseni içeren tüm örnekleri bulma

Matches yöntemi, bir dizedeki bir desene uyan öğeleri almayı sağlar:

public MatchCollection Matches(string input)
rend la collection des éléments de la chaîne input
correspondant au modèle

MatchCollection sınıfının, koleksiyondaki öğe sayısını gösteren Count özelliği vardır. résultats bir MatchCollection nesnesi ise, [i] bu koleksiyonun i'inci elemanıdır ve Match türündedir. Match sınıfı, aşağıdakiler dahil olmak üzere çeşitli özelliklere sahiptir:

  • Value: Match, nesnesinin değeri, yani modele karşılık gelen bir öğe
  • Index: Elemanın taranan dizede bulunduğu konum

Aşağıdaki örneği inceleyelim:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program2 {
        static void Main(string[] args) {
            // örnekteki kalıbın birden fazla tekrarı
            string modèle2 = @"\d+";
            Regex regex2 = new Regex(modèle2);
            string exemplaire3 = "  123  456  789 ";
            MatchCollection résultats = regex2.Matches(exemplaire3);
            Console.WriteLine("Modèle=[{0}],exemplaire=[{1}]", modèle2, exemplaire3);
            Console.WriteLine("Il y a {0} occurrences du modèle dans l'exemplaire ", résultats.Count);
            for (int i = 0; i < résultats.Count; i++) {
                Console.WriteLine("[{0}] trouvé en position {1}", résultats[i].Value, résultats[i].Index);
            }//for
        }
    }
}
  • 8. satır: Aranan şablon bir sayı dizisidir
  • 10. satır: Bu şablonun arandığı dize
  • 11. satır: exemplaire3 dizisindeki, modèle2 desenine uyan tüm öğeler alınır
  • 14-16. satırlar: Bunlar ekrana yazdırılır

Programın çalıştırılması sonucunda elde edilen sonuçlar şunlardır:

1
2
3
4
5
Modèle=[\d+],exemplaire=[  123  456  789 ]
Il y a 3 occurrences du modèle dans l'exemplaire
[123] trouvé en position 2
[456] trouvé en position 7
[789] trouvé en position 12

5.7.3. Bir şablonun parçalarını alma

Bir şablonun alt kümeleri "alınabilir". Böylece, bir dizgenin belirli bir şablona uyup uymadığını doğrulamakla kalmaz, aynı zamanda bu dizgeden şablonun parantezlerle çevrili alt kümelerine karşılık gelen öğeleri de alabiliriz. Örneğin, gg/aa/yyyy tarihini içeren bir dizgiyi analiz ediyorsak ve bu tarihin gg, aa, yyyy öğelerini de ayıklamak istiyorsak, (\d\d)/(\d\d)/(\d\d) şablonunu kullanırız.

Şu örneği inceleyelim:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program3 {
        static void Main(string[] args) {
            // şablondaki öğelerin yakalanması
            string modèle3 = @"(\d\d):(\d\d):(\d\d)";
            Regex regex3 = new Regex(modèle3);
            string exemplaire4 = "Il est 18:05:49";
            // şablon doğrulaması
            Match résultat = regex3.Match(exemplaire4);
            if (résultat.Success) {
                // örnek, şablona uyuyor
                Console.WriteLine("L'exemplaire [{0}] correspond au modèle [{1}]",exemplaire4,modèle3);
                // parantez grupları görüntülenir
                for (int i = 0; i < résultat.Groups.Count; i++) {
                    Console.WriteLine("groupes[{0}]=[{1}] trouvé en position {2}",i, résultat.Groups[i].Value,résultat.Groups[i].Index);
                }//for
            } else {
                // örnek, şablona uymuyor
                Console.WriteLine("L'exemplaire[{0}] ne correspond pas au modèle [{1}]", exemplaire4, modèle3);
            }
        }
    }
}

Bu programın çalıştırılması sonucunda aşağıdaki sonuçlar elde edilir:

1
2
3
4
5
L'exemplaire [Il est 18:05:49] correspond au modèle [(\d\d):(\d\d):(\d\d)]
groupes[0]=[18:05:49] trouvé en position 7
groupes[1]=[18] trouvé en position 7
groupes[2]=[05] trouvé en position 10
groupes[3]=[49] trouvé en position 13

Yenilik, 12-19. satırlarda yer almaktadır:

  • 12. satır: exemplaire4 dizesi, Match yöntemi aracılığıyla regex3 şablonuyla karşılaştırılır. Bu yöntem, daha önce tanıtılmış olan bir Match nesnesini döndürür. Burada bu sınıfın iki yeni özelliğini kullanıyoruz:
  • Success (13. satır): eşleşme olup olmadığını gösterir
  • Groups (17. ve 18. satırlar):
    • Groups[0]'in, şablona karşılık gelen dize kısmına karşılık geldiği koleksiyon
    • Groups[i] (i>=1), i numaralı parantez grubuna karşılık gelir

résultat, Match türündeyse, résultats.Groups, GroupCollection türündedir ve résultats.Groups[i], Group türündedir. Group sınıfının burada kullandığımız iki özelliği vardır:

  • Value (18. satır): Group nesnesinin değeri; bu, parantez içindeki içeriğe karşılık gelen öğedir
  • Index (satır 18): Elemanın taranan dizede bulunduğu konum

5.7.4. Bir alıştırma programı

Bir dizgenin belirli bir şablona uyup uymadığını kontrol etmeyi sağlayan düzenli ifadeyi bulmak bazen gerçek bir zorluktur. Aşağıdaki program bu konuda alıştırma yapmanıza olanak tanır. Program, bir şablon ve bir dizge ister ve dizgenin şablona uyup uymadığını belirtir.


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program4 {
        static void Main(string[] args) {
            // veriler
            string modèle, chaine;
            Regex regex = null;
            MatchCollection résultats;
            // kullanıcıdan, bu örnekle karşılaştırılacak şablonlar ve örnekler isteniyor
            while (true) {
                // şablon isteniyor
                Console.Write("Tapez le modèle à tester ou rien pour arrêter :");
                modèle = Console.In.ReadLine();
                // bitti mi?
                if (modèle.Trim() == "")
                    break;
                // düzenli ifade oluşturuluyor
                try {
                    regex = new Regex(modèle);
                } catch (Exception ex) {
                    Console.WriteLine("Erreur : " + ex.Message);
                    continue;
                }
                // kullanıcıdan şablonla karşılaştırılacak örnekleri istiyoruz
                while (true) {
                    Console.Write("Tapez la chaîne à comparer au modèle [{0}] ou rien pour arrêter :", modèle);
                    chaine = Console.ReadLine();
                    // bitti mi?
                    if (chaine.Trim() == "")
                        break;
                    // karşılaştırma yapılıyor
                    résultats = regex.Matches(chaine);
                    // başarılı mı?
                    if (résultats.Count == 0) {
                        Console.WriteLine("Je n'ai pas trouvé de correspondances");
                        continue;
                    }//if
                    // modele uyan öğeler görüntülenir
                    for (int i = 0; i < résultats.Count; i++) {
                        Console.WriteLine("J'ai trouvé la correspondance [{0}] en position [{1}]", résultats[i].Value, résultats[i].Index);
                        // alt öğeler
                        if (résultats[i].Groups.Count != 1) {
                            for (int j = 1; j < résultats[i].Groups.Count; j++) {
                                Console.WriteLine("\tsous-élément [{0}] en position [{1}]", résultats[i].Groups[j].Value, résultats[i].Groups[j].Index);
                            }
                        }
                    }
                }
            }
        }
    }
}

İşte bir çalışma örneği:

Tapez le modèle à tester ou rien pour arrêter :\d+
Tapez la chaîne à comparer au modèle [\d+] ou rien pour arrêter :123 456 789
J'ai trouvé la correspondance [123] en position [0]
J'ai trouvé la correspondance [456] en position [4]
J'ai trouvé la correspondance [789] en position [8]
Tapez la chaîne à comparer au modèle [\d+] ou rien pour arrêter :
Tapez le modèle à tester ou rien pour arrêter :(\d{2}):(\d\d)
Tapez la chaîne à comparer au modèle [(\d{2}):(\d\d)] ou rien pour arrêter :14:15 abcd 17:18 xyzt
J'ai trouvé la correspondance [14:15] en position [0]
        sous-élément [14] en position [0]
        sous-élément [15] en position [3]
J'ai trouvé la correspondance [17:18] en position [11]
        sous-élément [17] en position [11]
        sous-élément [18] en position [14]
Tapez la chaîne à comparer au modèle [(\d{2}):(\d\d)] ou rien pour arrêter :
Tapez le modèle à tester ou rien pour arrêter :^\s*\d+\s*$
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou rien pour arrêter :   1456
J'ai trouvé la correspondance [   1456] en position [0]
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou rien pour arrêter :
Tapez le modèle à tester ou rien pour arrêter :^\s*(\d+)\s*$
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou rien pour arrêter :1456
J'ai trouvé la correspondance [1456] en position [0]
        sous-élément [1456] en position [0]
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou rien pour arrêter :abcd 1456
Je n'ai pas trouvé de correspondances
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou rien pour arrêter :
Tapez le modèle à tester ou rien pour arrêter :

5.7.5. Split yöntemi

Bu yöntemi daha önce String dersinde görmüştük:


public string[] Split(char[] separator)
la chaîne est vue comme une suite de champs séparés par les
caractères présents dans le tableau separator. Le résultat est
le tableau de ces champs

Regex sınıfındaki Split yöntemi, ayırıcıyı bir şablona göre ifade etmemizi sağlar:


public string[] Split(string input)
La chaîne input est décomposée en champs, ceux-ci étant séparés
par un séparateur correspondant au modèle de l'objet Regex
courant.

Örneğin, bir metin dosyasında alan1, alan2, ..., alann şeklinde satırlar olduğunu varsayalım. Alanlar virgülle ayrılmıştır, ancak virgülün önünde veya arkasında boşluklar olabilir. Bu durumda, string sınıfındaki Split yöntemi uygun değildir. Çözümü, RegEx yönteminde bulabiliriz. Okunan satır ligne ise, alanlar şu şekilde elde edilebilir:

string[] champs=new Regex(@"s*,\s*").Split(ligne);

kullanılarak elde edilebilir; aşağıdaki örnekte gösterildiği gibi:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program5 {
        static void Main(string[] args) {
            // bir satır
            string ligne = "abc  , def  , ghi";
            // bir şablon
            Regex modèle = new Regex(@"\s*,\s*");
            // satırın alanlara ayrılması
            string[] champs = modèle.Split(ligne);
            // görüntüleme
            for (int i = 0; i < champs.Length; i++) {
                Console.WriteLine("champs[{0}]=[{1}]", i, champs[i]);
            }
        }
    }
}

Çalıştırma sonuçları:

1
2
3
champs[0]=[abc]
champs[1]=[def]
champs[2]=[ghi]

5.8. Örnek uygulama - V3

3.6 (sürüm 1) ve 4.10 (sürüm 2) paragraflarında incelenen uygulamayı tekrar ele alıyoruz.

İncelenen son sürümde, vergi hesaplaması AbstractImpot soyut sınıfında yapılıyordu:


namespace Chap2 {
    abstract class AbstractImpot : IImpot {

        // vergi hesaplaması için gerekli vergi dilimleri
        // harici bir kaynaktan alınır

        protected TrancheImpot[] tranchesImpot;

        // vergi hesaplaması
        public int calculer(bool marié, int nbEnfants, int salaire) {
            // pay sayısının hesaplanması
            decimal nbParts;
            if (marié) nbParts = (decimal)nbEnfants / 2 + 2;
            else nbParts = (decimal)nbEnfants / 2 + 1;
            if (nbEnfants >= 3) nbParts += 0.5M;
            // vergilendirilebilir gelir ve aile katsayısı hesaplaması
            decimal revenu = 0.72M * salaire;
            decimal QF = revenu / nbParts;
            // vergi hesaplaması
            tranchesImpot[tranchesImpot.Length - 1].Limite = QF + 1;
            int i = 0;
            while (QF > tranchesImpot[i].Limite) i++;
            // sonuç döndürme
            return (int)(revenu * tranchesImpot[i].CoeffR - nbParts * tranchesImpot[i].CoeffN);
        }//hesapla
    }//sınıf

}
  1. satırdaki calculer yöntemi, 35. satırdaki tranchesImpot tablosunu kullanmaktadır; bu tablo, AbstractImpot sınıfı tarafından başlatılmamıştır. Bu nedenle bu yöntem soyuttur ve kullanılabilir hale gelmesi için türetilmesi gerekir. Bu başlatma işlemi, türetilmiş HardwiredImpot sınıfı tarafından gerçekleştiriliyordu:

using System;

namespace Chap2 {
    class HardwiredImpot : AbstractImpot {

        // vergi hesaplaması için gerekli veri tabloları
        decimal[] limites = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M };
        decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };
        decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M };

        public HardwiredImpot() {
                // vergi dilimleri tablosunun oluşturulması
            tranchesImpot = new TrancheImpot[limites.Length];
                // doldurma
            for (int i = 0; i < tranchesImpot.Length; i++) {
                tranchesImpot[i] = new TrancheImpot { Limite = limites[i], CoeffR = coeffR[i], CoeffN = coeffN[i] };
                }
        }
    }// sınıf
}// ad alanı

Yukarıda, vergi hesaplaması için gerekli veriler sınıf koduna "sabit" olarak yerleştirilmişti. Örneğin yeni sürümünde ise bu veriler bir metin dosyasına yerleştirilmiştir:

4962:0:0
8382:0,068:291,09
14753:0,191:1322,92
23888:0,283:2668,39
38868:0,374:4846,98
47932:0,426:6883,66
0:0,481:9505,54

Bu dosyanın işlenmesi sırasında istisnalar ortaya çıkabileceğinden, bunları yönetmek için özel bir sınıf oluşturuyoruz:


using System;

namespace Chap3 {
    class FileImpotException : Exception {
        // hata kodları
        [Flags]
        public enum CodeErreurs { Acces = 1, Ligne = 2, Champ1 = 4, Champ2 = 8, Champ3 = 16 };
    
        // hata kodu
        public CodeErreurs Code { get; set; }

        // yapıcılar
        public FileImpotException() {
        }
        public FileImpotException(string message)
            : base(message) {
        }
        public FileImpotException(string message, Exception e)
            : base(message,e) {
        }
    }
}
  • 4. satır: FileImpotException sınıfı, Exception sınıfından türetilmiştir. Bu sınıf, veri metin dosyasının işlenmesi sırasında ortaya çıkan tüm hataları kaydetmek için kullanılacaktır.
  • 7. satır: Hata kodlarını temsil eden bir sıralama:
    • Acces: veri metin dosyasına erişim hatası
    • Ligne: Satırda beklenen üç alan bulunmuyor
    • Champ1: 1 numaralı alan hatalı
    • Champ2: 2 numaralı alan hatalı
    • Champ3: 3 numaralı alan hatalı

Bu hatalardan bazıları bir arada görülebilir (Champ1, Champ2, Champ3). Bu nedenle, CodeErreurs numaralı sıralamaya [Flags] özniteliği eklenmiştir; bu, sıralamadaki farklı değerlerin 2'nin kuvvetleri olması gerektiğini belirtir. 1 ve 2 numaralı alanlarda bir hata olması durumunda, Champ1 | Champ2 hata kodu verilir.

  • 10. satır: Code otomatik özelliği, hata kodunu kaydedecektir.
  • 15. satır: Bir hata mesajını parametre olarak geçirerek FileImpotException nesnesini oluşturmaya yarayan bir oluşturucu.
  • 18. satır: Bir hata mesajını ve hataya neden olan istisnayı parametre olarak alarak FileImpotException nesnesini oluşturmaya yarayan bir oluşturucu.

AbstractImpot sınıfının tranchesImpot dizisini başlatan sınıf artık şu şekildedir:


using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace Chap3 {
    class FileImpot : AbstractImpot {

        public FileImpot(string fileName) {
            // veriler
            List<TrancheImpot> listTranchesImpot = new List<TrancheImpot>();
            int numLigne = 1;
            // istisna
            FileImpotException fe = null;
            // fileName dosyasının içeriğini satır satır okuma
            Regex pattern = new Regex(@"s*:\s*");
            // başlangıçta hata yok
            FileImpotException.CodeErreurs code = 0;
            try {
                using (StreamReader input = new StreamReader(fileName)) {
                    while (!input.EndOfStream && code == 0) {
                        // geçerli satır
                        string ligne = input.ReadLine().Trim();
                        // boş satırlar atlanır
                        if (ligne == "") continue;
                        // satır, aşağıdakilerle ayrılmış üç alana bölünmüştür:
                        string[] champsLigne = pattern.Split(ligne);
                        // 3 alan var mı?
                        if (champsLigne.Length != 3) {
                            code = FileImpotException.CodeErreurs.Ligne;
                        }
                        // 3 alanın dönüştürülmesi
                        decimal limite = 0, coeffR = 0, coeffN = 0;
                        if (code == 0) {
                            if (!Decimal.TryParse(champsLigne[0], out limite)) code = FileImpotException.CodeErreurs.Champ1;
                            if (!Decimal.TryParse(champsLigne[1], out coeffR)) code |= FileImpotException.CodeErreurs.Champ2;
                            if (!Decimal.TryParse(champsLigne[2], out coeffN)) code |= FileImpotException.CodeErreurs.Champ3; ;
                        }
                        // hata mı?
                        if (code != 0) {
                            // hatk not ediliyor
                            fe = new FileImpotException(String.Format("Ligne n° {0} incorrecte", numLigne)) { Code = code };
                        } else {
                            // yeni vergi dilimini kaydediyoruz
                            listTranchesImpot.Add(new TrancheImpot() { Limite = limite, CoeffR = coeffR, CoeffN = coeffN });
                            // sonraki satır
                            numLigne++;
                        }
                    }
                }
                // listImpot listesi tranchesImpot tablosuna aktarılıyor
                if (code == 0) {
                    // listImpot listesi tranchesImpot tablosuna aktarılır
                    tranchesImpot = listTranchesImpot.ToArray();
                }
            } catch (Exception e) {
                // hata kaydedilir
                fe= new FileImpotException(String.Format("Erreur lors de la lecture du fichier {0}", fileName), e) { Code = FileImpotException.CodeErreurs.Acces };
            }
            // hatayı bildirmeli miyiz?
            if (fe != null) throw fe;
        }
    }
}
  • 7. satır: FileImpot sınıfı, sürüm 2'de HardwiredImpot sınıfının yaptığı gibi AbstractImpot sınıfından türetilmiştir.
  • 9. satır: FileImpot sınıfının oluşturucusunun görevi, temel sınıfı olan AbstractImpot'in trancheImpot alanını başlatmaktır. Bu oluşturucu, verileri içeren metin dosyasının adını parametre olarak alır.
  • 11. satır: AbstractImpot temel sınıfının tranchesImpot alanı, parametre olarak geçirilen filename dosyasındaki verilerle doldurulması gereken bir dizidir. Metin dosyasının okunması sıralıdır. Satır sayısı, dosyanın tamamı okunduktan sonra öğrenilebilir. Bu nedenle tranchesImpot dizisinin boyutunu önceden belirleyemeyiz. Verileri geçici olarak listTranchesImpot genel listesinde saklayacağız.

TrancheImpot türünün bir yapı olduğunu hatırlatmak isteriz:


namespace Chap3 {
    // bir vergi dilimi
    struct TrancheImpot {
        public decimal Limite { get; set; }
        public decimal CoeffR { get; set; }
        public decimal CoeffN { get; set; }
    }
}
  • 14. satır: fe türü FileImpotException, metin dosyasının işlenmesinde meydana gelebilecek olası bir hatayı kapsüllemek için kullanılır.
  • 16. satır: Metin dosyasındaki bir champ1:champ2:champ3 satırındaki alan ayırıcısının düzenli ifadesi. Alanlar, önünde ve arkasında herhangi bir sayıda boşluk bulunan : karakteriyle ayrılır.
  • 18. satır: Hata durumunda hata kodu
  • 20. satır: metin dosyasının StreamReader ile işlenmesi
  • 21. satır: Okunacak satır kaldığı ve hata oluşmadığı sürece döngü devam eder
  • 27. satır: Okunan satır, 16. satırdaki düzenli ifade kullanılarak alanlara bölünür
  • 29-31. satırlar: satırın üç alana sahip olduğu kontrol edilir; olası bir hata kaydedilir
  • 33-38. satırlar: Üç dize, üç ondalık sayıya dönüştürülür; olası hatalar kaydedilir
  • 40-43. satırlar: Hata varsa, FileImpotException türünde bir istisna oluşturulur.
  • 44-47. satırlar: Hata yoksa, mevcut satırdan alınan veriler kaydedildikten sonra metin dosyasının bir sonraki satırının okunmasına geçilir.
  • satır 52-55: while çıkışında, listTranchesImpot genel listesindeki veriler, AbstractImpot temel sınıfının tranchesImpot tablosuna kopyalanır. Bunun, geliştiricinin amacının bu olduğu hatırlatılmalıdır.
  • 56-59. satırlar: Olası bir istisnanın yönetimi. Bu istisna, FileImpotException türünde bir nesne içinde kapsüllenir.
  • 61. satır: 18. satırdaki fe istisnası başlatılmışsa, bu istisna tetiklenir.

C# projesinin tamamı şu şekildedir:

  • [1] içinde: projenin tamamı
  • [2,3]'te: [DataImpot.txt] ve [2] dosyalarının özellikleri. [Copy to Output Directory] ve [3] özellikleri "always" olarak ayarlanır. Bu ayar sayesinde, [DataImpot.txt] dosyası her çalıştırmada bin/Release klasörüne (Release modu) veya bin/Debug klasörüne (Debug modu) kopyalanır. Yürütülebilir dosya bu klasörlerde dosyayı arar.
  • [4] dosyasında: [DataImpotInvalide.txt] dosyası için de aynı işlem yapılır.

[DataImpot.txt] dosyasının içeriği şöyledir:

4962:0:0
8382:0,068:291,09
14753:0,191:1322,92
23888:0,283:2668,39
38868:0,374:4846,98
47932:0,426:6883,66
0:0,481:9505,54

[DataImpotInvalide.txt] dosyasının içeriği şöyledir:

a:b:c

[Program.cs] test programı değişmemiştir: Bu, 2. sürümün 4.10. paragrafındaki programdır, ancak şu farkla:


using System;

namespace Chap3 {
    class Program {
        static void Main() {
...
             // bir nesnenin oluşturulması IImpot
            IImpot impot = null;
            try {
                // bir nesnenin oluşturulması IImpot
                impot = new FileImpot("DataImpot.txt");
            } catch (FileImpotException e) {
                // hata görüntüleme
                string msg = e.InnerException == null ? null : String.Format(", Exception d'origine : {0}", e.InnerException.Message);
                Console.WriteLine("L'erreur suivante s'est produite : [Code={0},Message={1}{2}]", e.Code, e.Message, msg == null ? "" : msg);
                // programın durdurulması
                Environment.Exit(1);
            }

            // sonsuz döngü
            while (true) {
...
            }//while
        }
    }
}
  • 8. satır: impot nesnesi, IImpot arayüz türündedir
  • 11. satır: impot nesnesinin, FileImpot türündeki bir nesneyle örneklendirilmesi. Bu işlem, 9., 12. ve 18. satırlardaki try / catch bloğu tarafından yönetilen bir istisna oluşturabilir.

İşte bazı çalıştırma örnekleri:

[DataImpot.txt] dosyası ile

1
2
3
Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :o 2 60000
Impot=4282 euros
Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :

[xx] dosyası mevcut değilken

L'erreur suivante s'est produite : [Code=Acces,Message=Erreur lors de la lecture du fichier xx, Exception d'origine : Could not find file 'C:\data\2007-2008\c#2008\poly\Chap3\10\bin\Release\xx'.]

[DataImpotInvalide.txt] dosyası ile

L'erreur suivante s'est produite : [Code=Champ1, Champ2, Champ3,Message=Ligne n° 1 incorrecte]