Skip to content

5. کلاس‌های پرکاربرد .NET

در اینجا چند کلاس پرکاربرد از پلتفرم .NET را معرفی می‌کنیم. پیش از آن، نشان می‌دهیم که چگونه می‌توان اطلاعات مربوط به چند صد کلاس موجود را به دست آورد. این راهنما حتی برای توسعه‌دهندگان باتجربه C# نیز ضروری است. کیفیت مستندات (سهولت دسترسی، سازماندهی واضح، مرتبط بودن اطلاعات و غیره) می‌تواند یک محیط توسعه را بسازد یا نابود کند.

5.1. جستجوی راهنما برای کلاس‌های .NET

در اینجا چند نکته برای یافتن راهنما در Visual Studio.NET ارائه شده است

5.1.1. راهنما/فهرست مطالب

  • در [1]، گزینه Help/Contents را از منو انتخاب کنید.
  • در [2]، گزینه Visual C# Express Edition را انتخاب کنید
  • در [3]، درخت راهنمای C#
  • در [4]، گزینهٔ مفید دیگری .NET Framework است که دسترسی به تمام کلاس‌های فریم‌ورک .NET را فراهم می‌کند.

بیایید نگاهی به عناوین فصل‌ها در راهنمای C# بیندازیم:

  • [1]: مروری بر C#
  • [2]: مجموعه‌ای از مثال‌ها که جنبه‌های خاصی از C# را نشان می‌دهند
  • [3]: یک دورهٔ C# – می‌تواند به‌طور مفید این سند را جایگزین کند…
  • [4]: برای بررسی دقیق‌تر C#
  • [5]: مفید برای توسعه‌دهندگان C++ یا Java. به شما کمک می‌کند از چند تله اجتناب کنید.
  • [6]: وقتی به دنبال مثال‌ها هستید، می‌توانید از اینجا شروع کنید.
  • [7]: آنچه برای ایجاد رابط‌های کاربری گرافیکی باید بدانید
  • [8]: چگونه بیشترین بهره را از IDE ویژوال استودیو اکسپرس ببرید
  • [9]: SQL Server Express 2005 یک نرم‌افزار توزیع‌شده با کیفیت بالا است که به‌صورت رایگان ارائه می‌شود. ما در این دوره از آن استفاده خواهیم کرد.

راهنمای C# تنها بخشی از نیاز یک توسعه‌دهنده است. بخش دیگر، راهنمای صدها کلاس در فریم‌ورک .NET است که کار آن‌ها را آسان‌تر می‌کند.

  • [1]: راهنمای فریم‌ورک .NET را انتخاب کنید
  • [2]: راهنما در شاخه Framework .NET قرار دارد SDK
  • [3]: شاخهٔ کتابخانهٔ کلاس‌های فریم‌ورک .NET تمام کلاس‌ها را .NET بر اساس فضای نامی که به آن تعلق دارند فهرست می‌کند
  • [4]: فضای نام System که در مثال‌های فصل‌های قبلی بیشترین استفاده را داشت
  • [5]: در فضای نام System، یک مثال، در این مورد ساختار DateTime
  • [6]: راهنما درباره ساختار DateTime

5.1.2. راهنما/فهرست/جستجو

راهنمای ارائه‌شده توسط MSDN بسیار گسترده است و ممکن است ندانید کجا را جستجو کنید. در این صورت می‌توانید از نمایه راهنما استفاده کنید:

  • در [1]، اگر پنجرهٔ راهنما هنوز باز نیست، از گزینهٔ [Help/Index] استفاده کنید؛ در غیر این صورت، در یک پنجرهٔ راهنمای موجود از [2] استفاده کنید.
  • برای [3]، ناحیه‌ای را که جستجو باید در آن انجام شود مشخص کنید
  • در [4]، مشخص کنید که به دنبال چه چیزی هستید – در این مورد، یک کلاس
  • در [5]، نتیجه

راه دیگری برای یافتن کمک، استفاده از قابلیت جستجوی کمک است:

  • در [1]، اگر پنجرهٔ راهنما هنوز باز نیست، از گزینهٔ [Help/Search] استفاده کنید؛ در غیر این صورت، در پنجرهٔ راهنمای موجود از [2] استفاده کنید.
  • برای [3]، مشخص کنید که به دنبال چه چیزی هستید
  • در [4]، حوزه‌های جستجو را فیلتر کنید
  • در [5]، نتایج به صورت موضوعات مختلف نمایش داده می‌شوند که متن جستجو شده در آن‌ها یافت شده است.

5.2. رشته‌های کاراکتری

5.2.1. کلاس System.String

کلاس System.String با نوع رشته ساده یکسان است. این کلاس دارای خواص و متدهای متعددی است. در اینجا به چند مورد از آن‌ها اشاره می‌شود:


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

توجه به این نکته مهم است که وقتی یک متد رشته‌ای را بازمی‌گرداند، این رشته با رشته‌ای که متد روی آن اعمال شده است متفاوت است. بنابراین، S1.Trim() رشته S2 را برمی‌گرداند، و S1 و S2 دو رشته متفاوت هستند.

یک رشته C را می‌توان به‌عنوان یک آرایه از کاراکترها در نظر گرفت. بنابراین

  • C[i] کاراکتر i-ام C است
  • C.Length تعداد کاراکترهای C است

مثال زیر را در نظر بگیرید:


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] + "]");
            }//برای
            affiche("Join(\":\",champs)=" + System.String.Join(":", champs));
            affiche("(\"  abc  \").Trim()=[" + "  abc  ".Trim() + "]");
        }//اصلی

        public static void affiche(string msg) {
            // پیام را نمایش می‌دهد
            Console.WriteLine(msg);
        }//نمایش می‌دهد
    }//کلاس
}//فضای نام

اجرا نتایج زیر را تولید می‌کند:

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]

بیایید مثال دیگری را در نظر بگیریم:


using System;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // خطی که باید تحلیل شود
            string ligne = "un:deux::trois:";
            //جداکننده‌های فیلد
            char[] séparateurs = new char[] { ':' };
            // تفکیک
            string[] champs = ligne.Split(séparateurs);
            for (int i = 0; i < champs.Length; i++) {
                Console.WriteLine("Champs[" + i + "]=" + champs[i]);
            }
            //  الحاق
            Console.WriteLine("join=[" + System.String.Join(":", champs) + "]");
        }
    }
}

و نتایج اجرا:

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

متد Split کلاس String امکان قرار دادن عناصر یک رشتهٔ کاراکتری در یک آرایه را فراهم می‌کند. تعریف متد Split که در اینجا استفاده شده به شرح زیر است:


    public string[] Split(char[] separator);
separator
آرایه کاراکتری. این کاراکترها نمایانگر کاراکترهای مورد استفاده برای جدا کردن فیلدها در رشته هستند. بنابراین، اگر رشته "champ1, champ2, champ3"" باشد، می‌توانید از `separator=new char[] {','}` استفاده کنید. اگر جداکننده دنباله‌ای از فضاها باشد، از separator=null استفاده می‌کنیم.
résultat
یک آرایه از رشته‌ها که هر عنصر آن یک فیلد از رشته است.

متد Join یک متد ایستا از کلاس String است:


    public static string Join(string separator, string[] value);
value
آرایهٔ رشته‌ها
separator
یک رشته برای استفاده به عنوان جداکننده فیلد
résultat
یک رشته که با الحاق عناصر آرایه value تشکیل شده و با رشته separator از هم جدا شده‌اند.

5.2.2. کلاس System.Text.StringBuilder

قبلاً اشاره کردیم که متدهای کلاس String که بر روی یک رشته کاراکتری S1 اعمال می‌شوند، یک رشته دیگر S2 را بازمی‌گردانند. کلاس System.Text.StringBuilder به شما امکان می‌دهد S1 را بدون نیاز به ایجاد یک رشته S2 دستکاری کنید. این امر با جلوگیری از پراکنده شدن رشته‌هایی با چرخه عمر بسیار کوتاه، عملکرد را بهبود می‌بخشد.

این کلاس از سازندهای مختلفی پشتیبانی می‌کند:

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.

یک شیء StringBuilder با بلوک‌های کاراکتری از نوع capacité برای ذخیرهٔ رشتهٔ زیربنایی کار می‌کند. به‌طور پیش‌فرض، capacité روی 16 تنظیم شده است. سومین سازندهٔ بالا به شما امکان می‌دهد ظرفیت بلوک‌ها را مشخص کنید. تعداد بلوک‌های کاراکتری capacité مورد نیاز برای ذخیرهٔ رشتهٔ S به‌طور خودکار توسط کلاس StringBuilder تنظیم می‌شود. سازنده‌هایی برای تنظیم حداکثر تعداد کاراکترها در یک شیء StringBuilder وجود دارد. به طور پیش‌فرض، این حداکثر ظرفیت ۲٬۱۴۷٬۴۸۳٬۶۴۷ است.

در اینجا مثالی برای روشن‌سازی این مفهوم در capacité آورده شده است:


using System.Text;
using System;
namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // رشته
            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);
            }
        }
    }
}
  • خط ۷: ایجاد یک شیء StringBuilder با اندازه بلوک ۱۶ کاراکتر
  • خط ۸: str.Length تعداد فعلی کاراکترها در رشته str است. str.Capacity تعداد کاراکترهایی است که رشته فعلی str می‌تواند ذخیره کند قبل از تخصیص مجدد یک بلوک جدید.
  • خط ۱۰: str.Append(String S) برای الحاق رشته S (از نوع String) به رشته str (از نوع StringBuilder) استفاده می‌شود.
  • خط ۱۴: ایجاد یک شیء StringBuilder با ظرفیت بلوک ۱۰ کاراکتر

نتیجه اجرای کد:

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

این نتایج نشان می‌دهد که کلاس هنگام ناکافی بودن ظرفیت، از الگوریتم خاص خود برای تخصیص بلوک‌های جدید پیروی می‌کند:

  • خطوط ۴–۵: ظرفیت ۱۶ کاراکتری افزایش یافت
  • خطوط ۸–۹: ظرفیت به ۳۲ کاراکتر افزایش یافت، در حالی که ۱۶ کاراکتر کافی بود.

در اینجا برخی از متدهای کلاس آورده شده است:


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.

در اینجا یک مثال آورده شده است:


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"));
        }
    }
}

و نتایج آن:

XYZTstabCD

5.3. آرایه‌ها

آرایه‌ها از کلاس Array مشتق شده‌اند:

کلاس Array دارای متدهای مختلفی برای مرتب‌سازی آرایه، جستجو برای یک عنصر در آرایه، تغییر اندازه آرایه و غیره است. ما برخی از ویژگی‌ها و متدهای این کلاس را ارائه می‌دهیم. تقریباً همه آن‌ها دارای بارگذاری مجدد (overloaded) هستند، c.a.d، به این معنی که در گونه‌های مختلفی وجود دارند. هر آرایه از این کلاس ارث می‌برد.

ویژگی‌ها

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

روش‌ها

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.

برنامه زیر کاربرد برخی از متدهای کلاس Array را نشان می‌دهد:


using System;

namespace Chap3 {
    class Program {
        // نوع جستجو
        enum TypeRecherche { linéaire, dichotomique };

        // روش اصلی
        static void Main(string[] args) {
            // خواندن عناصر آرایه وارد شده از طریق صفحه‌کلید
            double[] éléments;
            Saisie(out éléments);
            // نمایش آرایه مرتب‌نشده
            Affiche("Tableau non trié", éléments);
            // جستجوی خطی در یک آرایه مرتب‌نشده
            Recherche(éléments, TypeRecherche.linéaire);
            //مرتب‌سازی آرایه
            Array.Sort(éléments);
            //نمایش یک آرایه مرتب‌شده
            Affiche("Tableau trié", éléments);
            //جستجوی دودویی در آرایه مرتب‌شده
            Recherche(éléments, TypeRecherche.dichotomique);
        }

        // وارد کردن مقادیر برای عناصر آرایه
        // عناصر: ارجاع به آرایه‌ای که توسط متد ایجاد شده است
        static void Saisie(out double[] éléments) {
            bool terminé = false;
            string réponse;
            bool erreur;
            double élément = 0;
            int i = 0;
            //در ابتدا آرایه وجود ندارد
            éléments = null;
            //حلقه برای وارد کردن عناصر آرایه
            while (!terminé) {
                // پرس‌وجو
                Console.Write("Elément (réel) " + i + " du tableau (rien pour terminer) : ");
                // خواندن پاسخ
                réponse = Console.ReadLine().Trim();
                // پایان ورودی اگر رشته خالی باشد
                if (réponse.Equals(""))
                    break;
                //اعتبارسنجی ورودی
                try {
                    élément = Double.Parse(réponse);
                    erreur = false;
                } catch {
                    Console.Error.WriteLine("Saisie incorrecte, recommencez");
                    erreur = true;
                }//try-catch
                // اگر خطایی وجود نداشته باشد
                if (!erreur) {
                    // افزودن یک عنصر دیگر به آرایه
                    i += 1;
                    // تغییر اندازه آرایه برای جای دادن عنصر جدید
                    Array.Resize(ref éléments, i);
                    // افزودن عنصر جدید
                    éléments[i - 1] = élément;
                }
            }//در حالی که
        }

        // روش عمومی برای نمایش عناصر یک آرایه
        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);
            }
        }

        // جستجو برای یک عنصر در آرایه
        // عناصر: آرایه‌ای از اعداد حقیقی
        // TypeRecherche: دوگانه یا خطی
        static void Recherche(double[] éléments, TypeRecherche type) {
            //جستجو
            bool terminé = false;
            string réponse = null;
            double élément = 0;
            bool erreur = false;
            int i = 0;
            while (!terminé) {
                // سؤال
                Console.WriteLine("Elément cherché (rien pour arrêter) : ");
                // خواندن و بررسی پاسخ
                réponse = Console.ReadLine().Trim();
                // تمام شد؟
                if (réponse.Equals(""))
                    break;
                //بررسی
                try {
                    élément = Double.Parse(réponse);
                    erreur = false;
                } catch {
                    Console.WriteLine("Erreur, recommencez...");
                    erreur = true;
                }//try-catch
                // اگر خطا وجود نداشته باشد
                if (!erreur) {
                    // جستجو برای عنصر در آرایه
                    if (type == TypeRecherche.dichotomique)
                        // جستجوی دودویی
                        i = Array.BinarySearch(éléments, élément);
                    else
                        // جستجوی خطی
                        i = Array.IndexOf(éléments, élément);
                    // نمایش نتیجه
                    if (i >= 0)
                        Console.WriteLine("Trouvé en position " + i);
                    else
                        Console.WriteLine("Pas dans le tableau");
                }//اگر
            }//در حالی که
        }
    }
}
  • خطوط ۲۷–۶۲: متد Saisie عناصر آرایه éléments را که از طریق صفحه‌کلید وارد شده‌اند، دریافت می‌کند. از آنجا که نمی‌توان اندازه آرایه را از پیش تعیین کرد (اندازه نهایی آن نامشخص است)، هر بار که عنصر جدیدی اضافه می‌شود باید اندازه آن تغییر کند (خط ۵۷). الگوریتم کارآمدتری این بود که فضا را برای آرایه به صورت گروه‌های N عنصری تخصیص می‌داد. با این حال، آرایه برای تغییر اندازه طراحی نشده است. این سناریو بهتر است با استفاده از یک لیست (ArrayList, List<T>) مدیریت شود.
  • خطوط ۷۵–۱۱۳: متد Recherche امکان جستجو در آرایه éléments را برای عنصری که از طریق صفحه‌کلید وارد شده است، فراهم می‌کند. روش جستجو بسته به مرتب بودن یا نبودن آرایه متفاوت است. برای آرایه‌ای که مرتب نشده است، یک جستجوی خطی با استفاده از روش IndexOf در خط ۱۰۶ انجام می‌شود. برای آرایه‌ای که مرتب شده است، یک جستجوی دودویی با استفاده از روش BinarySearch در خط ۱۰۳ انجام می‌شود.
  • خط ۱۸: آرایه éléments مرتب شده است. در اینجا، از یک واریانت از Sort استفاده می‌شود که تنها یک پارامتر دارد: آرایه‌ای که باید مرتب شود. بنابراین رابطه مرتب‌سازی مورد استفاده برای مقایسه عناصر آرایه، رابطه ضمنی برای این عناصر است. در اینجا، عناصر عددی هستند. از مرتب‌سازی طبیعی اعداد استفاده می‌شود.

خروجی صفحه به شرح زیر است:

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. مجموعه‌های عمومی

علاوه بر آرایه‌ها، کلاس‌های مختلفی برای ذخیره‌سازی مجموعه‌های عناصر وجود دارند. نسخه‌های عمومی در فضای نام System.Collections.Generic در دسترس هستند، در حالی که نسخه‌های غیرعمومی در System.Collections موجودند. ما به دو مجموعه عمومی پرکاربرد می‌پردازیم: لیست و فرهنگ لغت.

فهرست مجموعه‌های عمومی به شرح زیر است:

Image

5.4.1. کلاس عمومی List<T>

کلاس System.Collections.Generic.List<T> امکان پیاده‌سازی مجموعه‌هایی از اشیاء از نوع T را فراهم می‌کند که اندازه آن‌ها در حین اجرای برنامه تغییر می‌کند. یک شیء از نوع List<T> تقریباً مانند یک آرایه با آن برخورد می‌شود. بنابراین، عنصر i از یک لیست l با l[i] نشان داده می‌شود.

همچنین یک نوع لیست غیرکلی وجود دارد: ArrayList، که قادر به ذخیره ارجاع به هر شیئی است. ArrayList از نظر عملکردی معادل List<Object>. است. یک شیء ArrayList به این صورت است:

در مثال بالا، عناصر 0، 1 و i در لیست به اشیایی با انواع مختلف اشاره می‌کنند. یک شیء باید ابتدا ایجاد شود تا مرجع آن بتواند به لیست ArrayList اضافه شود. اگرچه یک ArrayList مراجع اشیاء را ذخیره می‌کند، اما امکان ذخیره اعداد در آن نیز وجود دارد. این امر از طریق مکانیزمی به نام Boxing انجام می‌شود: عدد در درون یک شیء O از نوع Object قرار می‌گیرد و مرجع به O در لیست ذخیره می‌شود. این مکانیزم برای توسعه‌دهنده شفاف است. بنابراین می‌توانیم بنویسیم:

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

این کار نتیجهٔ زیر را تولید می‌کند:

در مثال بالا، عدد ۴ در یک شیء O بسته‌بندی شده و مرجع O در لیست ذخیره شده است. برای بازیابی آن می‌توان نوشت:


            int i = (int)liste[0];

عملیات Object -> int، Unboxing نامیده می‌شود. اگر یک لیست کاملاً از نوع int تشکیل شده باشد، اعلام آن به عنوان List<int> عملکرد را بهبود می‌بخشد. این به این دلیل است که اعداد از نوع int سپس در خود لیست ذخیره می‌شوند، نه در انواع Object که خارج از لیست قرار دارند. عملیات باکسینگ و آنباکسینگ دیگر انجام نمی‌شوند.

برای یک شیء از نوع List<T> یا T که یک کلاس است، لیست دوباره ارجاع به اشیاء از نوع T را ذخیره می‌کند:

در اینجا برخی از ویژگی‌ها و متدهای لیست‌های عمومی آورده شده است:

ویژگی‌ها

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.

روش‌ها

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

بیایید به مثال قبلی بازگردیم که شامل یک شیء از نوع Array بود و اکنون آن را با استفاده از یک شیء از نوع List<T>. پردازش کنیم. از آنجایی که این لیست شبیه به یک آرایه است، کد تغییر بسیار کمی دارد. ما فقط تغییرات قابل توجه را برجسته خواهیم کرد:


using System;
using System.Collections.Generic;

namespace Chap3 {
    class Program {
        // نوع جستجو
        enum TypeRecherche { linéaire, dichotomique };

        // روش اصلی
        static void Main(string[] args) {
            // اقلام فهرست مطالعه وارد شده از طریق صفحه‌کلید
            List<double> éléments;
            Saisie(out éléments);
            // تعداد موارد
            Console.WriteLine("La liste a {0} éléments et une capacité de {1} éléments", éléments.Count, éléments.Capacity);
            //نمایش لیست مرتب‌نشده
            Affiche("Liste non triée", éléments);
            // جستجوی خطی در لیست مرتب‌نشده
            Recherche(éléments, TypeRecherche.linéaire);
            //مرتب‌سازی لیست
            éléments.Sort();
            //نمایش لیست مرتب‌شده
            Affiche("Liste triée", éléments);
            //جستجوی دودویی در لیست مرتب‌شده
            Recherche(éléments, TypeRecherche.dichotomique);
        }

        // وارد کردن مقادیر از فهرست عناصر
        // عناصر: ارجاع به لیستی که توسط متد ایجاد شده است
        static void Saisie(out List<double> éléments) {
...
            //در ابتدا، لیست خالی است
            éléments = new List<double>();
            //حلقه برای وارد کردن عناصر لیست
            while (!terminé) {
...
                // اگر خطایی رخ ندهد
                if (!erreur) {
                    // یک مورد دیگر به فهرست اضافه کنید
                    éléments.Add(élément);
                }
            }//در حالی که
        }

        // روش عمومی برای نمایش عناصر یک شی قابل شمارش
        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);
            }
        }

        // جستجو برای یک عنصر در لیست
        // عناصر: فهرست اعداد حقیقی
        // TypeRecherche: دوگانه یا خطی
        static void Recherche(List<double> éléments, TypeRecherche type) {
...
            while (!terminé) {
...
                // اگر خطایی وجود نداشته باشد
                if (!erreur) {
                    //جستجو برای عنصر در لیست
                    if (type == TypeRecherche.dichotomique)
                        // جستجوی دودویی
                        i = éléments.BinarySearch(élément);
                    else
                        // جستجوی خطی
                        i = éléments.IndexOf(élément);
                    // نمایش پاسخ
...
                }//اگر
            }//در حالی که
        }
    }
}
  • خطوط ۴۶–۵۱: متد عمومی Affiche<T> دو پارامتر می‌گیرد:
  • پارامتر اول متنی است که باید نوشته شود
  • پارامتر دوم یک شیء است که رابط عمومی IEnumerable<T> را پیاده‌سازی می‌کند:
1
2
3
4
public interface IEnumerable<T>{
    IEnumerator GetEnumerator();
    IEnumerator<T> GetEnumerator();
}

ساختار foreach( T element in elements) در خط ۴۸ برای هر شیء éléments که رابط IEnumerable را پیاده‌سازی می‌کند، معتبر است. آرایه‌ها (Array) و لیست‌ها (List<T>) رابط IEnumerable<T> را پیاده‌سازی می‌کنند. بنابراین متد Affiche به یک اندازه برای نمایش هر دو جدول و فهرست‌ها مناسب است.

خروجی برنامه همانند مثالی است که از کلاس Array استفاده می‌کند.

5.4.2. کلاس Dictionary&lt;TKey,TValue&gt;

کلاس System.Collections.Generic.Dictionary<TKey,TValue> امکان پیاده‌سازی یک فرهنگ لغت را فراهم می‌کند. یک فرهنگ لغت را می‌توان به عنوان یک آرایه دو ستونی در نظر گرفت:

کلید
value
key1
value1
key2
value2
..
...

در کلاس Dictionary&lt;TKey,TValue&gt;, کلیدها از نوع Tkey و مقادیر از نوع TValue هستند. کلیدها یکتا هستند، c.a.d، به این معنی که هیچ دو کلیدی نمی‌توانند یکسان باشند. چنین دیکشنری ممکن است به این شکل به نظر برسد اگر انواع TKey و TValue نشان‌دهنده کلاس‌ها باشند:

مقدار مرتبط با کلید C در یک دیکشنری D با D[C] نشان داده می‌شود. این مقدار هم قابل خواندن و هم قابل نوشتن است. بنابراین، می‌توان نوشت:

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

اگر کلید c در دیکشنری D وجود نداشته باشد، نشانه D[c] یک استثنا (exception) را پرتاب (throw) می‌کند.

روش‌ها و ویژگی‌های اصلی کلاس Dictionary<TKey,TValue> به شرح زیر است:

سازنده‌ها

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

خواص

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.

روش‌ها

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)

بیایید برنامهٔ مثال زیر را در نظر بگیریم:


using System;
using System.Collections.Generic;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // ایجاد یک دیکشنری <string,int>
            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]);
            }//برای
            //تعداد عناصر در فرهنگ لغت
            Console.WriteLine("Le dictionnaire a " + dico.Count + " éléments");
            // فهرست کلیدها
            Affiche("[Liste des clés]",dico.Keys);
            // فهرست مقادیر
            Affiche("[Liste des valeurs]", dico.Values);
            // فهرست کلیدها و مقادیر
            Console.WriteLine("[Liste des clés & valeurs]");
            foreach (string clé in dico.Keys) {
                Console.WriteLine("clé=" + clé + " valeur=" + dico[clé]);
            }
            //کلید «paul» حذف شد
            Console.WriteLine("[Suppression d'une clé]");
            dico.Remove("paul");
            // فهرست کلیدها و مقادیر
            Console.WriteLine("[Liste des clés & valeurs]");
            foreach (string clé in dico.Keys) {
                Console.WriteLine("clé=" + clé + " valeur=" + dico[clé]);
            }
            // جستجو در فرهنگ لغت
            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");
                }
                // جستجوی بعدی
                Console.Out.Write("Nom recherché (rien pour arrêter) : ");
                nomCherché = Console.ReadLine().Trim();
            }//در حالی که
        }

        // روش عمومی برای نمایش عناصر یک نوع قابل شمارش
        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);
            }
        }

    }
}
  • خط ۸: یک آرایه از string که برای مقداردهی اولیه فرهنگ لغت <string,int> استفاده خواهد شد
  • خط ۱۱: دیکشنری <string,int>
  • خطوط ۱۲–۱۵: مقداردهی اولیه آن با استفاده از آرایه string از خط ۸
  • خط 17: تعداد موارد در فرهنگ لغت
  • خط ۱۹: کلیدهای دیکشنری
  • خط ۲۱: مقادیر فرهنگ لغت
  • خط ۲۹: حذف یک مدخل از فرهنگ لغت
  • خط ۴۱: جستجو برای یک کلید در دیکشنری. اگر وجود نداشته باشد، متد TryGetValue مقدار value را روی ۰ قرار می‌دهد، زیرا value از نوع عددی است. این تکنیک تنها در اینجا قابل استفاده است زیرا می‌دانیم مقدار 0 در دیکشنری وجود ندارد.

نتایج اجرا به شرح زیر است:

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. فایل‌های متنی

5.5.1. کلاس StreamReader

کلاس System.IO.StreamReader به شما امکان می‌دهد محتوای یک فایل متنی را بخوانید. در واقع، این کلاس قادر است جریان‌هایی را که فایل نیستند نیز پردازش کند. در اینجا برخی از ویژگی‌ها و متدهای آن آورده شده است:

سازنده‌ها

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é.

خواص

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

روش‌ها

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.

در اینجا یک مثال آورده شده است:


using System;
using System.IO;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // دایرکتوری زمان اجرا
            Console.WriteLine("Répertoire d'exécution : "+Environment.CurrentDirectory);
            string ligne = null;
            StreamReader fluxInfos = null;
            // خواندن محتویات فایل infos.txt
            try {
                //خواندن ۱
                Console.WriteLine("Lecture 1----------------");
                using (fluxInfos = new StreamReader("infos.txt")) {
                    ligne = fluxInfos.ReadLine();
                    while (ligne != null) {
                        Console.WriteLine(ligne);
                        ligne = fluxInfos.ReadLine();
                    }
                }
                // خواندن ۲
                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);
            }
        }
    }
}
  • خط ۸: نام دایرکتوری اجرای برنامه را نمایش می‌دهد
  • خطوط ۱۲ و ۲۷: یک بلوک try/catch برای رسیدگی به هرگونه استثنا.
  • خط ۱۵: ساختار `using flux=new StreamReader(...)` یک ویژگی تسهیلی است تا شما مجبور نباشید پس از استفاده از استریم، آن را به صورت صریح ببندید. استریم به محض خروج از دامنه `using` به طور خودکار بسته می‌شود.
  • خط ۱۵: فایلی که در حال خواندن است infos.txt نام دارد. از آنجایی که این یک مسیر نسبی است، در دایرکتوری اجرای نمایش داده شده در خط ۸ جستجو خواهد شد. اگر پیدا نشود، یک استثنا پرتاب شده و توسط بلاک try/catch مدیریت می‌شود.
  • خطوط ۱۶–۲۰: فایل خط به خط خوانده می‌شود
  • خط ۲۵: فایل یک‌جا خوانده می‌شود

فایل infos.txt به شرح زیر است:

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

و در پوشه زیر در پروژه C# قرار دارد:

مشاهده خواهیم کرد که bin/Release پوشه خروجی است وقتی پروژه با استفاده از Ctrl-F5 اجرا می‌شود.

اجرای پروژه نتایج زیر را تولید می‌کند:

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

اگر در خط ۱۵ نام فایل xx.txt را وارد کنیم، نتایج زیر را دریافت می‌کنیم:

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

کلاس System.IO.StreamReader به شما امکان می‌دهد در یک فایل متنی بنویسید. مانند کلاس StreamReader، در واقع قادر به مدیریت جریان‌هایی است که فایل نیستند. در اینجا برخی از ویژگی‌ها و متدهای آن آورده شده است:

سازنده‌ها

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é.

ویژگی‌ها

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".

روش‌ها

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.

بیایید مثال زیر را در نظر بگیریم:


using System;
using System.IO;

namespace Chap3 {
    class Program2 {
        static void Main(string[] args) {
            // دایرکتوری اجرای برنامه
            Console.WriteLine("Répertoire d'exécution : " + Environment.CurrentDirectory);
            string ligne = null;                        //یک خط متن
            StreamWriter fluxInfos = null;    // فایل متنی
            try {
                // ایجاد فایل متنی
                using (fluxInfos = new StreamWriter("infos2.txt")) {
                    Console.WriteLine("Mode AutoFlush : {0}", fluxInfos.AutoFlush);
                    // خواندن یک خط تایپ‌شده روی صفحه‌کلید
                    Console.Write("ligne (rien pour arrêter) : ");
                    ligne = Console.ReadLine().Trim();
                    // حلقه تا زمانی که خط وارد شده خالی نباشد
                    while (ligne != "") {
                        //نوشتن یک خط در فایل متنی
                        fluxInfos.WriteLine(ligne);
                        //خواندن خط جدید از صفحه‌کلید
                        Console.Write("ligne (rien pour arrêter) : ");
                        ligne = Console.ReadLine().Trim();
                    }//در حالی که
                }
            } catch (Exception e) {
                Console.WriteLine("L'erreur suivante s'est produite : " + e.Message);
            }
        }
    }
}
  • خط ۱۳: بار دیگر، از دستوری با سینتکس using(flux) استفاده می‌کنیم تا مجبور نباشیم جریان را به‌صورت صریح با استفاده از عملیات Close ببندیم. این بستن به‌طور خودکار هنگام خروج از using انجام می‌شود.
  • چرا در خطوط ۱۱ و ۲۷ از بلوک try/catch استفاده می‌کنیم؟ در خط ۱۳ می‌توانستیم نام فایل را به صورت /rep1/rep2/ .../ مشخص کنیم.fichier با مسیری /rep1/rep2/... که وجود ندارد، و در نتیجه ایجاد fichier غیرممکن می‌شود. سپس یک استثنا پرتاب (throw) می‌شود. سناریوهای استثنای دیگری نیز وجود دارد (پر شدن دیسک، مجوزهای ناکافی و غیره).

نتایج اجرا به شرح زیر است:

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 در پوشه bin/Release پروژه ایجاد شد:

 

5.6. فایل‌های باینری

کلاس‌های System.IO.BinaryReader و System.IO.BinaryWriter برای خواندن و نوشتن فایل‌های باینری استفاده می‌شوند.

برنامه زیر را در نظر بگیرید:

// pg نحو: متن، باینری، لاگ‌ها
//یک فایل متنی (text) را بخوانید و محتویات آن را در یک فایل باینری (bin) ذخیره کنید
// فایل متنی شامل خطوطی به شکل نام : سن است که در ساختاری متشکل از یک رشته و یک عدد صحیح ذخیره خواهد شد
//(logs) یک فایل لاگ متنی است

فایل متنی حاوی موارد زیر است:

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

jacques : 11
sylvain : 12
xx : -1

xx: yy : zz
xx : yy

برنامه به شرح زیر است:


using System;
using System.IO;

// سینتکس pg متن باینری لاگ‌ها
//یک فایل متنی (text) خوانده می‌شود و محتویات آن در یک فایل دودویی (bin) ذخیره می‌شود
// فایل متنی شامل خطوطی به شکل نام: سن است که در ساختاری شامل یک رشته و یک عدد صحیح ذخیره خواهد شد
//(logs) یک فایل لاگ متنی است

namespace Chap3 {
    class Program {
        static void Main(string[] arguments) {
            //سه آرگومان لازم است
            if (arguments.Length != 3) {
                Console.WriteLine("syntaxe : pg texte binaire log");
                Environment.Exit(1);
            }//اگر

            //متغیرها
            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;
            // خواندن یک فایل متنی – نوشتن یک فایل باینری
            try {
                // باز کردن فایل متنی برای خواندن
                input = new StreamReader(arguments[0]);
                //باز کردن فایل باینری برای نوشتن
                output = new BinaryWriter(new FileStream(arguments[1], FileMode.Create, FileAccess.Write));
                // باز کردن فایل لاگ برای نوشتن
                logs = new StreamWriter(arguments[2]);
                // پردازش فایل متنی
                while ((ligne = input.ReadLine()) != null) {
                    // یک خط دیگر
                    numLigne++;
                    // خط خالی؟
                    if (ligne.Trim() == "") {
                        // نادیده گرفته شد
                        continue;
                    }
                    // یک خط شامل: نام : سن
                    champs = ligne.Split(séparateurs);
                    // ما به دو فیلد نیاز داریم
                    if (champs.Length != 2) {
                        // خطا را ثبت کن
                        logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un nombre de champs incorrect", numLigne, arguments[0]);
                        // خط بعدی
                        continue;
                    }//اگر
                    // میدان اول نباید خالی باشد
                    erreur = false;
                    nom = champs[0].Trim();
                    if (nom == "") {
                        // خطا را ثبت کن
                        logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un nom vide", numLigne, arguments[0]);
                        erreur = true;
                    }
                    // دومین فیلد باید یک عدد صحیح ≥ 0 باشد
                    if (!int.TryParse(champs[1],out age) || age<0) {
                        //خطا را ثبت کنید
                        logs.WriteLine("La ligne n° [{0}] du fichier [{1}] a un âge [{2}] incorrect", numLigne, arguments[0], champs[1].Trim());
                        erreur = true;
                    }//اگر
                    // اگر خطایی وجود نداشته باشد، داده‌ها را در فایل دودویی بنویسید
                    if (!erreur) {
                        output.Write(nom);
                        output.Write(age);
                    }
                    // خط بعدی
                }//در حالی که
            }catch(Exception e){
                Console.WriteLine("L'erreur suivante s'est produite : {0}", e.Message);
            } finally {
                // بستن فایل‌ها
                if(input!=null) input.Close();
                if(output!=null) output.Close();
                if(logs!=null) logs.Close();
            }
        }
    }
}

بیایید نگاهی دقیق‌تر به عملیات مربوط به کلاس BinaryWriter بیندازیم:

  • خط ۳۴: شیء BinaryWriter توسط عملیات باز می‌شود

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

آرگومان سازنده باید یک استریم باشد. در اینجا، این استریمی است که از یک فایل (FileStream) ایجاد شده است، که برای آن فراهم می‌کنیم:

  • (ادامه)
    • نام
    • عملی که باید انجام شود، در این مورد FileMode.Create برای ایجاد فایل
    • نوع دسترسی، در اینجا FileAccess.Write برای دسترسی نوشتاری به فایل
  • خطوط ۷۰–۷۳: عملیات نوشتن
             // داده‌ها را به فایل باینری می‌نویسد
            output.Write(nom);
            output.Write(age);

کلاس BinaryWriter دارای متدهای مختلفی است که بازنویسی شده‌اند، مانند Write، برای نوشتن انواع مختلف داده‌های ساده

  • خط ۸۱: عملیات بستن استریم
        output.Close();

سه آرگومان برای متد Main به پروژه (از طریق ویژگی‌های آن) [1] ارائه می‌شوند، و فایل متنی که باید پردازش شود در پوشه bin/Release [2] قرار داده می‌شود:

با فایل زیر [personnes1.txt]:

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

jacques : 11
sylvain : 12
xx : -1

xx: yy : zz
xx : yy

نتایج اجرای برنامه به شرح زیر است:

  • در [1]، فایل باینری [personnes1.bin] و همچنین فایل لاگ [logs.txt] ایجاد شدند. فایل دوم محتوای زیر را دارد:
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] توسط برنامه زیر ارائه خواهد شد. این برنامه همچنین سه آرگومان را می‌پذیرد:

//pg bin text logs syntax
//یک فایل باینری «bin» را می‌خواند و محتویات آن را در یک فایل متنی (text) ذخیره می‌کند
// فایل باینری دارای ساختاری متشکل از رشته‌ها و اعداد صحیح است
// فایل متنی شامل خطوطی به شکل نام : سن است
// logs یک فایل متنی حاوی لاگ‌ها است

بنابراین عملیات معکوس را انجام می‌دهیم. یک فایل باینری را می‌خوانیم تا یک فایل متنی ایجاد کنیم. اگر فایل متنی حاصل با فایل اصلی یکسان باشد، خواهیم دانست که تبدیل متن ← باینری ← متن با موفقیت انجام شده است. کد به شرح زیر است:


using System;
using System.IO;

// دستور: pg, bin, text, logs
//یک فایل باینری «bin» خوانده می‌شود و محتویات آن در یک فایل متنی («text») نوشته می‌شود
// فایل باینری ساختاری متشکل از رشته‌ها و اعداد صحیح دارد
// فایل متنی شامل خطوطی به شکل نام : سن است
// logs یک فایل متنی حاوی لاگ‌ها است

namespace Chap3 {
    class Program2 {
        static void Main(string[] arguments) {
            //سه آرگومان لازم است
            if (arguments.Length != 3) {
                Console.WriteLine("syntaxe : pg binaire texte log");
                Environment.Exit(1);
            }//اگر

            // متغیرها
            string nom = null;
            int age = 0;
            int numPersonne = 1;
            BinaryReader input = null;
            StreamWriter output = null;
            StreamWriter logs = null;
            bool fini;
            // خواندن فایل باینری – نوشتن فایل متنی
            try {
                // باز کردن فایل باینری برای خواندن
                input = new BinaryReader(new FileStream(arguments[0], FileMode.Open, FileAccess.Read));
                // باز کردن فایل متنی برای نوشتن
                output = new StreamWriter(arguments[1]);
                // باز کردن فایل لاگ برای نوشتن
                logs = new StreamWriter(arguments[2]);
                //پردازش فایل باینری
                fini = false;
                while (!fini) {
                    try {
                        // خواندن نام
                        nom = input.ReadString().Trim();
                        // خواندن سن
                        age = input.ReadInt32();
                        //نوشتن در فایل متنی
                        output.WriteLine(nom + ":" + age);
                        // فرد بعدی
                        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);
                    }
                }//در حالی که
            } catch (Exception e) {
                Console.WriteLine("L'erreur suivante s'est produite : {0}", e.Message);
            } finally {
                //بستن فایل‌ها
                if (input != null)
                    input.Close();
                if (output != null)
                    output.Close();
                if (logs != null)
                    logs.Close();
            }
        }
    }
}

بیایید نگاهی دقیق‌تر به عملیات مربوط به کلاس BinaryReader بیندازیم:

  • خط ۳۰: شیء BinaryReader توسط عملیات باز می‌شود

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

آرگومان سازنده باید یک استریم باشد. در اینجا، این یک استریم ساخته‌شده از یک فایل (FileStream) است که برای آن فراهم می‌کنیم:

  • (ادامه)
    • نام
    • عملی که باید انجام شود، در اینجا FileMode.Open برای باز کردن یک فایل موجود
    • نوع دسترسی، در اینجا FileAccess.Read برای دسترسی خواندنی به فایل
  • خطوط ۴۰، ۴۲: عملیات خواندن
nom=input.ReadString().Trim();
age=input.ReadInt32();

کلاس BinaryReader دارای متدهای مختلفی ReadXX برای خواندن انواع مختلف داده‌های ساده است

  • خط ۶۰: عملیات بستن استریم
        input.Close();

اگر دو برنامه را پشت سر هم اجرا کنیم، ابتدا personnes1.txt را به personnes1.bin و سپس personnes1.bin را به personnes2.txt2 تبدیل کنیم، نتایج زیر را به دست می‌آوریم:

  • به [1]؛ پروژه طوری پیکربندی شده است که برنامه دوم را اجرا کند
  • در [2]، آرگومان‌هایی که به Main ارسال می‌شوند
  • در [3]، فایل‌های تولید شده توسط اجرای برنامه.

محتویات [personnes2.txt] به شرح زیر است:

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

5.7. بیان‌های منظم

کلاس System.Text.RegularExpressions.Regex امکان استفاده از عبارت‌های منظم را فراهم می‌کند. این‌ها امکان تست فرمت یک رشته کاراکتری را فراهم می‌کنند. برای مثال، می‌توان بررسی کرد که آیا رشته‌ای که نشان‌دهنده یک تاریخ است، واقعاً در فرمت dd/mm/yy است. برای این کار، از یک الگو استفاده می‌شود و رشته با این الگو مقایسه می‌شود. بنابراین، در این مثال، d، m و y باید اعداد باشند. الگوی یک فرمت تاریخ معتبر بنابراین «\d\d/\d\d/\d\d» است، که در آن نماد \d نشان‌دهنده یک رقم است. نمادهایی که می‌توان در یک الگو استفاده کرد به شرح زیر است:

Caractère
توضیحات
\ 
کاراکتر زیر را به‌عنوان یک کاراکتر ویژه یا صریح علامت‌گذاری کنید. برای مثال، «n» معادل کاراکتر «n» است. «\n» معادل کاراکتر خط جدید است. توالی «\\» معادل «\» است، در حالی که «\()» معادل «()» است.
^ 
مطابقت دارد با شروع ورودی.
$ 
نماینده پایان ورودی است.
* 
با کاراکتر قبلی صفر یا چند بار مطابقت دارد. بنابراین، 'zo*' با 'z' یا 'zoo' مطابقت دارد.
+ 
با کاراکتر قبلی یک یا چند بار مطابقت دارد. بنابراین، «zo+» با «zoo» مطابقت دارد، اما با «z» مطابقت ندارد.
? 
با کاراکتر قبلی صفر یا یک بار مطابقت دارد. برای مثال، «a?ve?» با «ve» در «lever» مطابقت دارد.
.
با هر کاراکتر واحدی مطابقت دارد، به جز کاراکتر خط جدید.
(modèle) 
به دنبال modèle می‌گردد و تطبیق را ذخیره می‌کند. زیر رشته‌ی مطابقت‌یافته را می‌توان با استفاده از آیتم [0]...[n] از مجموعه Matches حاصل استخراج کرد. برای یافتن مطابقت‌هایی که شامل کاراکترها در پرانتز ( ) هستند، از "(" یا ")" استفاده کنید.
x|y
مطابقت می‌کند با x یا y. برای مثال، «z|foot» با «z» یا «foot» مطابقت دارد. «(z|f)oo» با «zoo» یا «foo» مطابقت دارد.
{n}
n یک عدد صحیح غیرمنفی است. دقیقاً با n بار آن کاراکتر مطابقت دارد. برای مثال، «o{2}» با «o» در «Bob» مطابقت ندارد، اما با دو «o» اول در «fooooot» مطابقت دارد.
{n,} 
n یک عدد صحیح غیرمنفی است. این عبارت حداقل به n بار از این کاراکتر مطابقت دارد. برای مثال، «o{2,}» با «o» در «Bob» مطابقت ندارد، اما با تمام «o»ها در «fooooot» مطابقت دارد. «o{1,}» معادل «o+» و «o{0,}» معادل «o*» است.
{n,m} 
m و n اعداد صحیح غیرمنفی هستند. با کاراکتر حداقل n و حداکثر m بار مطابقت می‌دهد. برای مثال، «o{1,3}» با سه «o» اول در «foooooot» مطابقت دارد و «o{0,1}» معادل «o؟» است.
[xyz] 
مجموعه کاراکترها. یکی از کاراکترهای مشخص‌شده را مطابقت می‌دهد. برای مثال، «[abc]» با «a» در «plat» مطابقت دارد.
[^xyz] 
مجموعه کاراکتر منفی. با هر کاراکتری که در لیست ذکر نشده مطابقت دارد. برای مثال، "[^abc]" با "p" در "plat" مطابقت دارد.
[a-z] 
محدوده کاراکتری. با هر کاراکتری که در محدوده مشخص شده باشد مطابقت دارد. برای مثال، "[a-z]" با هر کاراکتر الفبایی کوچک بین "a" و "z" مطابقت دارد.
[^m-z] 
محدوده کاراکتری منفی. با هر کاراکتری که در محدوده مشخص‌شده یافت نشود مطابقت دارد. برای مثال، «[^m-z]» با هر کاراکتری که بین «m» و «z» یافت نشود مطابقت دارد.
\b 
مطابقت می‌دهد با مرز کلمه، یعنی موقعیت بین یک کلمه و یک فاصله. برای مثال، "er\b" با "er" در "lever" مطابقت دارد، اما با "er" در "verbe" مطابقت ندارد.
\B 
یک مرز را مطابقت می‌دهد که نمایانگر یک کلمه نیست. «en*t\B» با «ent» در «bien entendu» مطابقت دارد.
\d 
با یک کاراکتر نمایانگر یک رقم مطابقت دارد. معادل [0-9] است.
\D 
مطابقت می‌دهد با یک کاراکتر که نمایانگر یک رقم نیست. معادل [^0-9] است.
\f 
مربوط به کاراکتر شکست صفحه است.
\n 
مطابق با کاراکتر پایان خط است.
\r 
مطابق با کاراکتر بازگشت به خانه است.
\s 
مطابقت دارد با هر فضای خالی، از جمله فاصله، تب، پایان صفحه و غیره. معادل "[ \f\n\r\t\v]".
\S 
هر کاراکتر غیرفاصله‌ای را مطابقت می‌دهد. معادل "[^ \f\n\r\t\v]".
\t 
با کاراکتر تب مطابقت دارد.
\v 
مطابقت می‌دهد با کاراکتر تب عمودی.
\w 
مطابقت دارد با هر کاراکتری که یک کلمه را نشان می‌دهد و شامل خط تیره (_) است. معادل "[A-Za-z0-9_]".
\W 
مطابقت دارد با هر کاراکتری که نمایانگر یک کلمه نباشد. معادل "[^A-Za-z0-9_]".
\num 
با num مطابقت دارد، که در آن num یک عدد صحیح مثبت است. به تطابق‌های ذخیره‌شده اشاره می‌کند. برای مثال، «(.)\1» با دو کاراکتر متوالی یکسان مطابقت دارد.
\n
مطابق با n است، که در آن n یک مقدار فرار اکتال است. مقادیر فرار اکتال باید شامل ۱، ۲ یا ۳ رقم باشند. برای مثال، «\11» و «\011» هر دو معادل یک کاراکتر تب هستند. "\0011" معادل "\001" و "1" است. مقادیر فرار اکتال نباید از ۲۵۶ تجاوز کنند. اگر این اتفاق بیفتد، تنها دو رقم اول در عبارت در نظر گرفته می‌شوند. امکان استفاده از کدهای ASCII در عبارات باقاعده را فراهم می‌کند.
\xn
مطابق با n است، که در آن n یک مقدار فرار هگزادسیمال است. مقادیر فرار هگزادسیمال باید از دو رقم تشکیل شوند. به عنوان مثال، "\x41" معادل "A" است. "\x041" معادل "\x04" و "1" است. امکان استفاده از کدهای ASCII در عبارات منظم فراهم است.

یک عنصر در یک الگو ممکن است یک یا چند بار ظاهر شود. بیایید به چند مثال مربوط به نماد \d که نماینده یک رقم است، نگاهی بیندازیم:

الگو
به معنای
\d
یک رقم
\d؟
۰ یا ۱ رقم
*
صفر یا بیشتر رقم
اعداد مثبت
۱ یا چند رقم
\d{2}
۲ رقم
\d{3,}
حداقل ۳ رقم
\d{5,7}
بین ۵ تا ۷ رقم

اکنون بیایید الگویی را در نظر بگیریم که قادر به توصیف قالب مورد انتظار برای یک رشته باشد:

رشته مقصد
الگو
یک تاریخ در قالب dd/mm/yy
\d{2}/\d{2}/\d{2}
یک زمان در قالب ساعت:دقیقه:ثانیه
\d{2}:\d{2}:\d{2}
یک عدد صحیح بدون علامت
\d+
یک دنباله از فاصله، که ممکن است خالی باشد
\s*
یک عدد صحیح بدون علامت که ممکن است قبل یا بعد از آن فضاها قرار داشته باشد
\s*\d+\s*
یک عدد صحیح که ممکن است علامت‌دار باشد و قبل یا بعد از آن فضاها قرار گیرند
\s*[+|-]?\s*\d+\s*
یک عدد حقیقی بدون علامت که ممکن است قبل یا بعد از آن فضا باشد
\s*\d+(.\d*)?\s*
یک عدد حقیقی که ممکن است علامت‌دار باشد و قبل یا بعد از آن فاصله‌ها قرار گیرند
\s*[+|]?\s*\d+(.\d*)?\s*
یک رشته حاوی کلمه juste
\bjuste\b
  

می‌توانید مشخص کنید که الگو را در کجای رشته جستجو کنید:

الگو
به معنی
^pattern
الگو در ابتدای رشته آغاز می‌شود
pattern$
الگو رشته را پایان می‌دهد
^الگو$
الگو رشته را شروع و پایان می‌دهد
الگو
الگو در هر جای رشته، از ابتدا جستجو می‌شود.
رشته جستجو
الگو
یک رشته که با علامت تعجب به پایان می‌رسد
!$
یک رشته که با نقطه پایان می‌یابد
\.$
یک رشته که با توالی // آغاز می‌شود
^//
یک رشته متشکل از یک کلمه واحد، که اختیاریاً قبل یا بعد از آن فاصله‌ها قرار می‌گیرند
^[ \t*]+[ \t*]$
یک رشته متشکل از دو کلمه، که اختیاریاً می‌تواند قبل یا بعد از آن فضا باشد
^\s*\w+\s*\w+\s*$
یک رشته حاوی کلمه secret
\bsecret\b

زیرالگوها در یک الگو را می‌توان «استخراج» کرد. بنابراین، نه تنها می‌توانیم بررسی کنیم که آیا یک رشته با الگوی خاصی مطابقت دارد، بلکه می‌توانیم عناصر مربوط به زیرالگوهای الگو که در کروشه‌ها قرار گرفته‌اند را از آن رشته استخراج کنیم. برای مثال، اگر در حال تحلیل رشته‌ای هستیم که تاریخ را به فرمت dd/mm/yy دارد و همچنین می‌خواهیم عناصر dd، mm و yy را از آن تاریخ استخراج کنیم، از الگوی (\d\d)/(\d\d)/(\d\d) استفاده خواهیم کرد.

5.7.1. بررسی اینکه آیا یک رشته با یک الگوی داده‌شده مطابقت دارد

یک شیء Regex به صورت زیر ساخته می‌شود:

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

پس از ساخت الگوی عبارت منظم، می‌توان آن را با رشته‌ها با استفاده از متد IsMatch مقایسه کرد:

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

در اینجا یک مثال آورده شده است:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program {
        static void Main(string[] args) {
            // یک الگوی عبارت منظم
            string modèle1 = @"^\s*\d+\s*$";
            Regex regex1 = new Regex(modèle1);
            // مقایسهٔ یک نمونه با الگو
            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);
            }//اگر
            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);
            }//اگر
        }

    }
}

و نتایج اجرا:

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

5.7.2. یافتن تمام رخدادهای یک الگو در یک رشته

متد Matches برای بازیابی عناصری از یک رشته که با یک الگو مطابقت دارند، استفاده می‌شود:

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

کلاس MatchCollection دارای خاصیت Count است که تعداد عناصر مجموعه را نشان می‌دهد. اگر résultats یک نمونه از کلاس MatchCollection باشد، آنگاه results[i] عنصر i-ام این مجموعه است و از نوع Match می‌باشد. کلاس Match دارای خواص مختلفی است، از جمله موارد زیر:

  • Value: مقدار شیء Match,، یعنی عنصری که با الگو مطابقت دارد
  • Index: موقعیتی که عنصر در رشته‌ای که در حال پیمایش است، در آن یافت شده است

بیایید مثال زیر را در نظر بگیریم:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program2 {
        static void Main(string[] args) {
            // چندین وقوع الگو در نمونه
            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);
            }//برای
        }
    }
}
  • خط ۸: الگوی مورد جستجو یک دنباله از ارقام است
  • خط ۱۰: رشته‌ای که این الگو در آن جستجو می‌شود
  • خط ۱۱: تمام عناصر exemplaire3 که با الگوی modèle2 مطابقت دارند، بازیابی می‌شوند
  • خطوط ۱۴–۱۶: این‌ها نمایش داده می‌شوند

خروجی اجرای برنامه به شرح زیر است:

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. استخراج بخش‌های یک الگو

زیرمجموعه‌های یک الگو را می‌توان «استخراج» کرد. بنابراین، نه تنها می‌توانیم بررسی کنیم که یک رشته با الگوی خاصی مطابقت دارد، بلکه می‌توانیم عناصر مربوط به زیرمجموعه‌های الگو که در کروشه‌ها قرار گرفته‌اند را از آن رشته استخراج کنیم. برای مثال، اگر در حال تحلیل رشته‌ای هستیم که تاریخ را به فرمت dd/mm/yy دارد و می‌خواهیم عناصر dd، mm و yy را از آن تاریخ استخراج کنیم، از الگوی (\d\d)/(\d\d)/(\d\d) استفاده خواهیم کرد.

بیایید به مثال زیر نگاه کنیم:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program3 {
        static void Main(string[] args) {
            // استخراج عناصر در قالب
            string modèle3 = @"(\d\d):(\d\d):(\d\d)";
            Regex regex3 = new Regex(modèle3);
            string exemplaire4 = "Il est 18:05:49";
            //اعتبارسنجی قالب
            Match résultat = regex3.Match(exemplaire4);
            if (résultat.Success) {
                // مثال با الگو مطابقت دارد
                Console.WriteLine("L'exemplaire [{0}] correspond au modèle [{1}]",exemplaire4,modèle3);
                //نمایش گروه‌های پرانتز
                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);
                }//برای
            } else {
                // مثال با قالب مطابقت ندارد
                Console.WriteLine("L'exemplaire[{0}] ne correspond pas au modèle [{1}]", exemplaire4, modèle3);
            }
        }
    }
}

اجرای این برنامه نتایج زیر را تولید می‌کند:

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

ویژگی جدید در خطوط ۱۲–۱۹ یافت می‌شود:

  • خط ۱۲: رشته exemplaire4 با قالب regex3 با استفاده از متد Match مقایسه می‌شود. این متد یک شیء Match را بازمی‌گرداند که قبلاً معرفی شده است. در اینجا از دو ویژگی جدید این کلاس استفاده می‌کنیم:
  • Success (خط ۱۳): نشان می‌دهد که آیا تطابقی یافت شده است یا خیر
  • Groups (خطوط 17، 18): یک مجموعه که
    • Groups[0] با بخشی از رشته که با الگو مطابقت دارد، متناظر است
    • Groups[i] (i ≥ 1) معادل مجموعهٔ i-ام کروشه‌ها است

اگر résultat از نوع Match باشد، در این صورت résultats.Groups از نوع GroupCollection و résultats.Groups[i] از نوع Group است. کلاس Group دارای دو خاصیت است که ما در اینجا از آن‌ها استفاده می‌کنیم:

  • Value (خط ۱۸): مقدار شیء Group، که عنصری است که با محتوای یک پرانتز مطابقت دارد
  • Index (خط ۱۸): موقعیتی که عنصر در رشته در حال تجزیه در آن یافت شده است

5.7.4. یک برنامه آموزشی

یافتن عبارت منظم (regular expression) که بررسی کند آیا یک رشته با الگوی خاصی مطابقت دارد یا خیر، گاهی اوقات می‌تواند یک چالش واقعی باشد. برنامه زیر به شما امکان می‌دهد تمرین کنید. این برنامه یک الگو و یک رشته را به عنوان ورودی می‌گیرد و مشخص می‌کند که آیا رشته با الگو مطابقت دارد یا خیر.


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program4 {
        static void Main(string[] args) {
            //داده‌ها
            string modèle, chaine;
            Regex regex = null;
            MatchCollection résultats;
            //از کاربر خواسته می‌شود قالب‌ها و نمونه‌هایی را که باید با این مورد مقایسه شوند، مشخص کند
            while (true) {
                //قالب درخواست می‌شود
                Console.Write("Tapez le modèle à tester ou rien pour arrêter :");
                modèle = Console.In.ReadLine();
                //انجام شد؟
                if (modèle.Trim() == "")
                    break;
                //عبارت منظم ایجاد می‌شود
                try {
                    regex = new Regex(modèle);
                } catch (Exception ex) {
                    Console.WriteLine("Erreur : " + ex.Message);
                    continue;
                }
                //از کاربر می‌پرسیم که کدام نمونه‌ها را باید با قالب مقایسه کنیم
                while (true) {
                    Console.Write("Tapez la chaîne à comparer au modèle [{0}] ou rien pour arrêter :", modèle);
                    chaine = Console.ReadLine();
                    //انجام شد؟
                    if (chaine.Trim() == "")
                        break;
                    // انجام مقایسه
                    résultats = regex.Matches(chaine);
                    // موفق بود؟
                    if (résultats.Count == 0) {
                        Console.WriteLine("Je n'ai pas trouvé de correspondances");
                        continue;
                    }//اگر
                    // عناصر مطابقت‌یافته با الگو نمایش داده می‌شوند
                    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);
                        // زیرعنصرها
                        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);
                            }
                        }
                    }
                }
            }
        }
    }
}

در اینجا مثالی از اجرای برنامه آورده شده است:

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

ما قبلاً با این روش در کلاس String مواجه شده‌ایم:


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

متد Split از کلاس Regex به ما امکان می‌دهد جداکننده را بر اساس یک قالب مشخص کنیم:


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.

فرض کنید، برای مثال، یک فایل متنی شامل خطوطی به شکل field1, field2, …, fieldn باشد. فیلدها با ویرگول از هم جدا شده‌اند، اما ممکن است قبل یا بعد از آن فاصله‌ها قرار داشته باشند. روش Split از کلاس string بنابراین مناسب نیست. روش RegEx راه‌حل را ارائه می‌دهد. اگر ligne خط خوانده‌شده باشد، می‌توان فیلدها را با استفاده از

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

همان‌طور که در مثال زیر نشان داده شده است:


using System;
using System.Text.RegularExpressions;

namespace Chap3 {
    class Program5 {
        static void Main(string[] args) {
            //یک ردیف
            string ligne = "abc  , def  , ghi";
            //یک قالب
            Regex modèle = new Regex(@"\s*,\s*");
            // تفکیک خط به فیلدها
            string[] champs = modèle.Split(ligne);
            //نمایش
            for (int i = 0; i < champs.Length; i++) {
                Console.WriteLine("champs[{0}]=[{1}]", i, champs[i]);
            }
        }
    }
}

نتایج اجرا:

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

5.8. مثال کاربردی – V3

به سراغ برنامه‌ای می‌رویم که در بخش‌های ۳.۶ (نسخه ۱) و ۴.۱۰ (نسخه ۲) مورد بحث قرار گرفت.

در جدیدترین نسخه بررسی‌شده، محاسبه مالیات در کلاس انتزاعی AbstractImpot انجام شده است:


namespace Chap2 {
    abstract class AbstractImpot : IImpot {

        // محدوده‌های مالیاتی مورد نیاز برای محاسبه مالیات
        // از یک منبع خارجی تأمین می‌شوند

        protected TrancheImpot[] tranchesImpot;

        //محاسبه مالیات
        public int calculer(bool marié, int nbEnfants, int salaire) {
            //محاسبه تعداد سهام
            decimal nbParts;
            if (marié) nbParts = (decimal)nbEnfants / 2 + 2;
            else nbParts = (decimal)nbEnfants / 2 + 1;
            if (nbEnfants >= 3) nbParts += 0.5M;
            //محاسبه درآمد مشمول مالیات و ضریب خانوادگی
            decimal revenu = 0.72M * salaire;
            decimal QF = revenu / nbParts;
            // محاسبه مالیات
            tranchesImpot[tranchesImpot.Length - 1].Limite = QF + 1;
            int i = 0;
            while (QF > tranchesImpot[i].Limite) i++;
            // نتیجه اظهارنامه
            return (int)(revenu * tranchesImpot[i].CoeffR - nbParts * tranchesImpot[i].CoeffN);
        }//محاسبه
    }// محدوده

}

متد calculer در خط ۳۸ از آرایه tranchesImpot در خط ۳۵ استفاده می‌کند، آرایه‌ای که توسط کلاس AbstractImpot مقداردهی اولیه نشده است. به همین دلیل این کلاس انتزاعی است و باید از آن ارث‌بری شود تا مفید باشد. این مقداردهی اولیه توسط کلاس مشتق HardwiredImpot انجام شده است:


using System;

namespace Chap2 {
    class HardwiredImpot : AbstractImpot {

        // جدول‌های داده‌ای مورد نیاز برای محاسبه مالیات
        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() {
                // ایجاد جدول رده‌های مالیاتی
            tranchesImpot = new TrancheImpot[limites.Length];
                //پر کردن
            for (int i = 0; i < tranchesImpot.Length; i++) {
                tranchesImpot[i] = new TrancheImpot { Limite = limites[i], CoeffR = coeffR[i], CoeffN = coeffN[i] };
                }
        }
    }//کلاس
}// فضای نام

در مثال بالا، داده‌های مورد نیاز برای محاسبه مالیات به‌صورت سخت‌کد در کلاس قرار داده شده بود. نسخه جدید مثال آن را در یک فایل متنی قرار می‌دهد:

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

از آنجا که پردازش این فایل ممکن است به خطاها (exceptions) منجر شود، ما یک کلاس ویژه برای رسیدگی به آن‌ها ایجاد می‌کنیم:


using System;

namespace Chap3 {
    class FileImpotException : Exception {
        // کدهای خطا
        [Flags]
        public enum CodeErreurs { Acces = 1, Ligne = 2, Champ1 = 4, Champ2 = 8, Champ3 = 16 };
    
        // کد خطا
        public CodeErreurs Code { get; set; }

        // سازنده‌ها
        public FileImpotException() {
        }
        public FileImpotException(string message)
            : base(message) {
        }
        public FileImpotException(string message, Exception e)
            : base(message,e) {
        }
    }
}
  • خط ۴: کلاس FileImpotException از کلاس Exception ارث می‌برد. این کلاس برای ثبت هرگونه خطایی که هنگام پردازش فایل داده متنی رخ می‌دهد، استفاده خواهد شد.
  • خط ۷: یک شمارشگر (enumeration) که نمایانگر کدهای خطا است:
    • Acces: خطا در دسترسی به فایل داده متنی
    • Ligne: ردیف شامل سه فیلد مورد انتظار نیست
    • Champ1: فیلد شماره ۱ نادرست است
    • Champ2: فیلد شمارهٔ ۲ نادرست است
    • Champ3: فیلد شماره ۳ نادرست است

برخی از این خطاها ممکن است به صورت ترکیبی رخ دهند (Champ1, Champ2, Champ3). در نتیجه، شماره‌گذاری CodeErreurs با ویژگی [Flags] نشانه‌گذاری شده است، که نشان می‌دهد مقادیر مختلف این شماره‌گذاری باید توان‌های ۲ باشند. بنابراین، خطا در فیلدهای ۱ و ۲ منجر به کد خطای Champ1 | Champ2 خواهد شد.

  • خط ۱۰: ویژگی خودکار Code کد خطا را ذخیره خواهد کرد.
  • خط ۱۵: یک سازنده که امکان ایجاد یک شیء FileImpotException را با ارسال یک پیام خطا به‌عنوان پارامتر فراهم می‌کند.
  • خط ۱۸: یک سازنده که امکان ایجاد یک شیء FileImpotException را با ارسال یک پیام خطا و استثنایی که باعث ایجاد خطا شده است به‌عنوان پارامتر فراهم می‌کند.

کلاسی که آرایه tranchesImpot از کلاس AbstractImpot را مقداردهی اولیه می‌کند اکنون به شرح زیر است:


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

namespace Chap3 {
    class FileImpot : AbstractImpot {

        public FileImpot(string fileName) {
            // داده‌ها
            List<TrancheImpot> listTranchesImpot = new List<TrancheImpot>();
            int numLigne = 1;
            // استثناء
            FileImpotException fe = null;
            // خواندن محتویات فایل fileName، خط به خط
            Regex pattern = new Regex(@"s*:\s*");
            // در ابتدا هیچ خطایی وجود ندارد
            FileImpotException.CodeErreurs code = 0;
            try {
                using (StreamReader input = new StreamReader(fileName)) {
                    while (!input.EndOfStream && code == 0) {
                        // خط فعلی
                        string ligne = input.ReadLine().Trim();
                        // خطوط خالی نادیده گرفته می‌شوند
                        if (ligne == "") continue;
                        // خط به سه فیلد تقسیم شده که با جداکننده زیر از هم جدا شده‌اند:
                        string[] champsLigne = pattern.Split(ligne);
                        //آیا سه فیلد وجود دارد؟
                        if (champsLigne.Length != 3) {
                            code = FileImpotException.CodeErreurs.Ligne;
                        }
                        //تبدیل‌های سه فیلد
                        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; ;
                        }
                        // خطا؟
                        if (code != 0) {
                            //خطا ثبت شده است
                            fe = new FileImpotException(String.Format("Ligne n° {0} incorrecte", numLigne)) { Code = code };
                        } else {
                            // محدوده مالیاتی جدید ثبت می‌شود
                            listTranchesImpot.Add(new TrancheImpot() { Limite = limite, CoeffR = coeffR, CoeffN = coeffN });
                            // خط بعدی
                            numLigne++;
                        }
                    }
                }
                //انتقال لیست listImpot به آرایه tranchesImpot
                if (code == 0) {
                    //انتقال لیست listImpot به آرایه tranchesImpot
                    tranchesImpot = listTranchesImpot.ToArray();
                }
            } catch (Exception e) {
                //خطا ثبت شد
                fe= new FileImpotException(String.Format("Erreur lors de la lecture du fichier {0}", fileName), e) { Code = FileImpotException.CodeErreurs.Acces };
            }
            // آیا باید خطا گزارش شود؟
            if (fe != null) throw fe;
        }
    }
}
  • خط ۷: کلاس FileImpot از کلاس AbstractImpot ارث می‌برد، درست همان‌طور که کلاس HardwiredImpot در نسخه ۲ این کار را انجام داد.
  • خط ۹: سازنده کلاس FileImpot مسئول مقداردهی اولیه فیلد trancheImpot در کلاس پایه خود AbstractImpot است. این سازنده نام فایل متنی حاوی داده‌ها را به‌عنوان پارامتر می‌پذیرد.
  • خط ۱۱: فیلد tranchesImpot از کلاس پایه AbstractImpot یک آرایه است که باید با داده‌های فایل filename که به‌عنوان پارامتر ارسال شده، پر شود. خواندن یک فایل متنی یک فرآیند ترتیبی است. تعداد خطوط تنها پس از خواندن کامل فایل مشخص می‌شود. بنابراین، آرایه tranchesImpot را نمی‌توان از پیش تخصیص داد. داده‌ها به‌طور موقت در لیست عمومی listTranchesImpot ذخیره خواهند شد.

توجه داشته باشید که نوع TrancheImpot یک ساختار است:


namespace Chap3 {
    //یک رده مالیاتی
    struct TrancheImpot {
        public decimal Limite { get; set; }
        public decimal CoeffR { get; set; }
        public decimal CoeffN { get; set; }
    }
}
  • خط ۱۴: fe، از نوع FileImpotException، برای محصور کردن هرگونه خطایی که ممکن است هنگام پردازش فایل متنی رخ دهد، استفاده می‌شود.
  • خط 16: عبارت منظم برای جداکنندهٔ فیلد در یک خط champ1:champ2:champ3 از فایل متنی. فیلدها با کاراکتر : از هم جدا می‌شوند، که می‌تواند قبل و بعد از آن هر تعداد فاصله باشد.
  • خط ۱۸: کد خطا در صورت بروز خطا
  • خط ۲۰: پردازش فایل متنی با استفاده از StreamReader
  • خط ۲۱: حلقه تا زمانی که یک خط برای خواندن باقی مانده باشد و هیچ خطایی رخ نداده باشد، ادامه می‌یابد.
  • خط ۲۷: خط خوانده‌شده با استفاده از عبارت منظم موجود در خط ۱۶ به فیلدها تقسیم می‌شود
  • خطوط ۲۹–۳۱: بررسی اینکه آیا خط واقعاً دارای سه فیلد است – ثبت هرگونه خطا
  • خطوط ۳۳–۳۸: سه رشته به سه عدد اعشاری تبدیل می‌شوند – هرگونه خطا ثبت می‌شود
  • خطوط ۴۰–۴۳: اگر خطایی رخ داده باشد، یک استثنا از نوع FileImpotException ایجاد می‌شود.
  • خطوط 44–47: اگر خطایی رخ نداده باشد، پس از ذخیره داده‌های خط فعلی، به خواندن خط بعدی فایل متنی ادامه می‌دهیم.
  • خطوط ۵۲–۵۵: با خروج از حلقه while، داده‌های لیست عمومی listTranchesImpot در آرایه tranchesImpot از کلاس پایه AbstractImpot کپی می‌شود. شایان ذکر است که این همان هدف مورد نظر سازنده بود.
  • خطوط ۵۶–۵۹: رسیدگی به یک استثناء احتمالی. این در یک شیء از نوع FileImpotException محصور شده است.
  • خط ۶۱: اگر استثنای fe از خط ۱۸ مقداردهی اولیه شده باشد، آنگاه پرتاب می‌شود.

پروژه کامل C# به شرح زیر است:

  • در [1]: کل پروژه
  • در [2,3]: ویژگی‌های فایل [DataImpot.txt] [2]. ویژگی [Copy to Output Directory] [3] روی 'always' تنظیم شده است. این بدان معناست که فایل [DataImpot.txt] هر بار که پروژه اجرا می‌شود، به پوشه bin/Release (حالت انتشار) یا bin/Debug (حالت اشکال‌زدایی) کپی خواهد شد. این همان جایی است که فایل اجرایی آن را جستجو می‌کند.
  • در [4]: همین امر در مورد فایل [DataImpotInvalide.txt] نیز صدق می‌کند.

محتویات [DataImpot.txt] به شرح زیر است:

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] به شرح زیر است:

a:b:c

برنامه آزمون [Program.cs] بدون تغییر باقی مانده است: این همان برنامه نسخه ۲، بند ۴.۱۰ است، با تفاوت زیر:


using System;

namespace Chap3 {
    class Program {
        static void Main() {
...
             //ایجاد یک شیء IImpot
            IImpot impot = null;
            try {
                // ایجاد یک شیء IImpot
                impot = new FileImpot("DataImpot.txt");
            } catch (FileImpotException e) {
                //نمایش خطا
                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);
                //پایان برنامه
                Environment.Exit(1);
            }

            //حلقه بی‌نهایت
            while (true) {
...
            }//در حالی که
        }
    }
}
  • خط ۸: شیء impot از نوع تعریف‌شده توسط رابط IImpot
  • خط ۱۱: نمونه‌سازی شی impot با یک شی از نوع FileImpot. این ممکن است یک استثنا ایجاد کند که توسط بلوک‌های try/catch در خطوط ۹، ۱۲ و ۱۸ مدیریت می‌شود.

در اینجا چند مثال از اجرا آورده شده است:

با فایل [DataImpot.txt]

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] که وجود ندارد

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]

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