14-Bob: Char, String va Matn bilan Ishlash
Belgilar, System.String turi, StringBuilder bilan samarali string qurish, ToString va Parse metodlari, kodlashlar va xavfsiz stringlar
Ushbu bobda men Microsoft .NET Framework da individual belgilar va stringlar bilan ishlash mexanikasini tushuntiraman. Avval System.Char strukturasi va belgilarni turli usullarda boshqarish haqida gaplashaman. Keyin o'zgarmas (immutable) stringlar bilan ishlash imkonini beradigan yanada foydaliroq System.String klasini ko'rib chiqaman. (Yaratilganidan keyin stringlarni hech qanday usulda o'zgartirish mumkin emas.) Stringlarni tekshirgandan so'ng, System.Text.StringBuilder klassi orqali stringni dinamik ravishda samarali qurish usullarini ko'rsataman. String asoslari bilan tugatgandan keyin, obyektlarni stringlarga formatlash va stringlarni turli kodlashlar yordamida samarali saqlash yoki uzatishni tasvirlayman. Va nihoyat, parollar va kredit karta ma'lumotlari kabi nozik matn ma'lumotlarini himoya qilish uchun foydalanish mumkin bo'lgan System.Security.SecureString klassini muhokama qilaman.
Belgilar (Characters)
.NET Framework da belgilar doimo 16-bitli Unicode kod qiymatlari sifatida ifodalanadi, bu global ilovalarni ishlab chiqishni osonlashtiradi. Belgi System.Char strukturasining (value tur) instansiyasi bilan ifodalanadi. System.Char turi juda oddiy. U ikkita faqat o'qish uchun mo'ljallangan konstanta maydonni taklif qiladi: MinValue ('\0' deb aniqlangan) va MaxValue ('\uffff' deb aniqlangan).
Char instansiyasida statik GetUnicodeCategory metodini chaqirib, System.Globalization.UnicodeCategory sanab o'tilgan turining qiymatini olishingiz mumkin. Bu qiymat belgi boshqaruv belgisimi, valyuta belgisimi, kichik harfmi, katta harfmi, tinish belgisimi, matematika belgisimi yoki boshqa belgimi (Unicode standarti bo'yicha aniqlangan) ekanligini ko'rsatadi.
Ishlab chiqishni osonlashtirish uchun Char turi bir nechta statik yordamchi metodlarni ham taklif qiladi: IsDigit, IsLetter, IsWhiteSpace, IsUpper, IsLower, IsPunctuation, IsLetterOrDigit, IsControl, IsNumber, IsSeparator, IsSurrogate, IsLowSurrogate, IsHighSurrogate va IsSymbol. Bu metodlarning aksariyati ichki tarzda GetUnicodeCategory ni chaqiradi va mos ravishda true yoki false qaytaradi. E'tibor bering, bu metodlarning barchasi parametr sifatida yagona belgi yoki String ichidagi belgi indeksini qabul qiladi.
Bundan tashqari, bitta belgini kichik yoki katta harf ekvivalentiga madaniyatdan mustaqil tarzda ToLowerInvariant yoki ToUpperInvariant statik metodini chaqirish orqali aylantirish mumkin. Muqobil ravishda, ToLower va ToUpper metodlari chaqiruvchi ipning madaniyat ma'lumotidan foydalanib belgini aylantiradi. Harf kattalashtirish madaniyatga bog'liq operatsiya ekanligini yodda tutish kerak. Masalan, turk tili U+0069 (LOTIN KICHIK HARF I) ning katta harfini U+0130 (YUQORIDA NUQTALI LOTIN KATTA HARF I) deb hisoblaydi, boshqa madaniyatlar esa natijani U+0049 (LOTIN KATTA HARF I) deb hisoblaydi.
Bu statik metodlardan tashqari, Char turi bir nechta o'zining instansiya metodlarini ham taklif qiladi. Equals metodi ikki Char instansiyasi bir xil 16-bitli Unicode kod nuqtasini ifodalasa true qaytaradi. CompareTo metodlari (IComparable/IComparable<Char> interfeyslar tomonidan aniqlangan) ikki Char instansiyalarining madaniyatdan mustaqil solishtirishini qaytaradi. ConvertFromUtf32 metodi bitta yoki ikkita UTF-16 belgilardan iborat stringni bitta UTF-32 belgidan hosil qiladi. ConvertToUtf32 past/yuqori surrogatlar juftligidan yoki stringdan UTF-32 belgini hosil qiladi. ToString metodi bitta belgidan iborat String ni qaytaradi. ToString ning teskarisi Parse/TryParse bo'lib, u bitta belgili String ni qabul qilib uning UTF-16 kod nuqtasini qaytaradi.
Oxirgi metod GetNumericValue belgining raqamli ekvivalentini qaytaradi. Quyidagi kod bu metoddan foydalanishni ko'rsatadi.
using System;
public static class Program {
public static void Main() {
Double d; // '\u0033' — "3 raqami"
d = Char.GetNumericValue('\u0033'); // '3' ham ishlaydi
Console.WriteLine(d.ToString()); // "3" ko'rsatadi
// '\u00bc' — "chorak kasr ('\u00bc')"
d = Char.GetNumericValue('\u00bc');
Console.WriteLine(d.ToString()); // "0.25" ko'rsatadi
// 'A' — "Lotin katta A harfi"
d = Char.GetNumericValue('A');
Console.WriteLine(d.ToString()); // "-1" ko'rsatadi
}
}
Char va Raqamli Turlar O'rtasida Konvertatsiya
Uchta texnika turli raqamli turlar va Char instansiyalari o'rtasida konvertatsiya qilish imkonini beradi. Texnikalar afzallik tartibida keltirilgan:
- Casting (tip o'tkazish) —
CharniInt32kabi raqamli qiymatga aylantirishning eng oson usuli oddiy casting. Uchta texnikaning eng samaralisidi, chunki kompilyator konvertatsiyani bajarish uchun Intermediate Language (IL) ko'rsatmalarini chiqaradi va hech qanday metod chaqirilishi shart emas. Bundan tashqari, ba'zi tillar (masalan C#) konvertatsiya checked yoki unchecked kod yordamida bajarilishi kerakligini ko'rsatish imkonini beradi. - Convert turidan foydalanish —
System.ConvertturiCharni raqamli turga va aksincha aylantirish imkoniyatiga ega bir nechta statik metodlarni taklif qiladi. Bu metodlarning barchasi konvertatsiyani checked operatsiya sifatida bajaradi, konvertatsiya natijasida ma'lumot yo'qolsaOverflowExceptiontashlanadi. - IConvertible interfeysidan foydalanish —
Charturi va .NET Framework Class Library (FCL) dagi barcha raqamli turlarIConvertibleinterfeysini amalga oshiradi. Bu texnika uchta texnikaning eng kam samaralisi, chunki value turda interfeys metodini chaqirish instansiyaning boxinglanishini talab qiladi.
Quyidagi kod ushbu uchta texnikadan qanday foydalanishni ko'rsatadi.
using System;
public static class Program {
public static void Main() {
Char c;
Int32 n;
// C# casting yordamida raqam <-> belgi konvertatsiyasi
c = (Char) 65;
Console.WriteLine(c); // "A" ko'rsatadi
n = (Int32) c;
Console.WriteLine(n); // "65" ko'rsatadi
c = unchecked((Char) (65536 + 65));
Console.WriteLine(c); // "A" ko'rsatadi
// Convert yordamida raqam <-> belgi konvertatsiyasi
c = Convert.ToChar(65);
Console.WriteLine(c); // "A" ko'rsatadi
n = Convert.ToInt32(c);
Console.WriteLine(n); // "65" ko'rsatadi
// Convert diapazon tekshiruvini namoyish etadi
try {
c = Convert.ToChar(70000); // 16 bit uchun juda katta
Console.WriteLine(c); // Bajarilmaydi
}
catch (OverflowException) {
Console.WriteLine("70000 ni Char ga aylantirish mumkin emas.");
}
// IConvertible yordamida raqam <-> belgi konvertatsiyasi
c = ((IConvertible) 65).ToChar(null);
Console.WriteLine(c); // "A" ko'rsatadi
n = ((IConvertible) c).ToInt32(null);
Console.WriteLine(n); // "65" ko'rsatadi
}
}
System.String Turi
Har qanday ilovadagi eng ko'p ishlatiladigan turlardan biri System.String dir. String belgilarning o'zgarmas (immutable) ketma-ketligini ifodalaydi. String turi bevosita Object dan hosila bo'ladi, bu uni reference turga aylantiradi. Demak, String obyektlari (belgilar massivi) doimo heapda joylashadi, hech qachon oqim stekida emas. String turi shuningdek bir nechta interfeyslarni amalga oshiradi: IComparable, IComparable<String>, ICloneable, IConvertible, IEnumerable/IEnumerable<Char> va IEquatable<String>.
Stringlar Yaratish
Ko'plab dasturlash tillari (jumladan C#) String ni primitiv tur deb hisoblaydi — ya'ni kompilyator literal stringlarni to'g'ridan-to'g'ri kodingizga yozish imkonini beradi. Kompilyator bu literal stringlarni modulning metama'lumotlariga joylashtiradi va ular ish vaqtida yuklanib murojaat qilinadi.
C# da literal stringdan String obyektini yaratish uchun new operatoridan foydalanish mumkin emas.
using System;
public static class Program {
public static void Main() {
String s = new String("Hi there."); // <-- Xato
Console.WriteLine(s);
}
}
Buning o'rniga quyidagi soddalashtirilgan sintaksisdan foydalanishingiz kerak.
using System;
public static class Program {
public static void Main() {
String s = "Hi there.";
Console.WriteLine(s);
}
}
Agar ushbu kodni kompilyatsiya qilib IL ni tekshirsangiz (ILDasm.exe yordamida), quyidagini ko'rasiz.
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 13 (0xd)
.maxstack 1
.locals init ([0] string s)
IL_0000: ldstr "Hi there."
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call void [mscorlib]System.Console::WriteLine(string)
IL_000c: ret
} // end of method Program::Main
newobj IL ko'rsatmasi obyektning yangi instansiyasini yaratadi. Lekin IL kodda hech qanday newobj ko'rsatmasi yo'q. Uning o'rniga maxsus ldstr (load string) IL ko'rsatmasini ko'rasiz, bu ko'rsatma metama'lumotlardan olingan literal string yordamida String obyektini yaratadi. Bu sizga CLR ning aslida literal String obyektlarini yaratishning maxsus usuli borligini ko'rsatadi.
C# manba kodingizga literal stringlarni kiritish uchun ba'zi maxsus sintaksislar taklif qiladi. Yangi satr va qaytish kabi maxsus belgilar uchun C/C++ dasturchilarga tanish bo'lgan escape mexanizmidan foydalaniladi.
// Qaytish va yangi satr belgilarini o'z ichiga olgan string
String s = "Hi\r\nthere.";
Oldingi misol qaytish va yangi satr belgilarini stringga qattiq kodlagan bo'lsa-da, men bu amaliyotni tavsiya etmayman. System.Environment turi ilovangiz Windows da ishlaganda bu belgilardan iborat stringni qaytaradigan faqat o'qish uchun mo'ljallangan NewLine xususiyatini aniqlaydi. NewLine xususiyati platformaga bog'liq. Masalan, Common Language Infrastructure (CLI) UNIX tizimiga ko'chirilsa, NewLine faqat bitta \n belgisidan iborat stringni qaytaradi. Oldingi stringni har qanday platformada to'g'ri ishlashi uchun to'g'ri aniqlash usuli:
String s = "Hi" + Environment.NewLine + "there.";
C# ning + operatori yordamida bir nechta stringni birlashtirib bitta stringni hosil qilishingiz mumkin.
// Uchta literal string birlashtirilgan bitta literal string
String s = "Hi" + " " + "there.";
Ushbu kodda barcha stringlar literal bo'lganligi sababli, C# kompilyatori ularni kompilyatsiya vaqtida birlashtiradi va modulning metama'lumotlarida faqat bitta string "Hi there." joylashtiradi. Literal bo'lmagan stringlarda + operatoridan foydalanish birlashtirishning ish vaqtida bajarilishiga olib keladi. Ish vaqtida bir nechta stringlarni birlashtirish uchun + operatoridan foydalanmang, chunki u garbage-collected heapda bir nechta string obyektlarini yaratadi. Buning o'rniga System.Text.StringBuilder turidan foydalaning (bobning keyingi qismida tushuntiraman).
Nihoyat, C# tirnoqlar orasidagi barcha belgilar stringning bir qismi hisoblangan so'zma-so'z (verbatim) stringni e'lon qilish uchun maxsus usulni taklif qiladi. Bu maxsus e'lonlar odatda fayl yoki katalog yo'lini ko'rsatishda yoki regular ifodalar bilan ishlashda foydalaniladi.
// Ilova yo'lini ko'rsatish
String file = "C:\\Windows\\System32\\Notepad.exe";
// Verbatim string yordamida ilova yo'li
String file = @"C:\Windows\System32\Notepad.exe";
Ikki qator ham bir xil natijani beradi, chunki ular assemblyning metama'lumotlarida identik stringlarni hosil qiladi. Ikkinchi satrda @ belgisi kompilyatorga bu verbatim string ekanligini bildiradi va teskari egri chiziq belgilarini escape belgilari emas, oddiy belgilar sifatida qayd etadi.
Stringlar O'zgarmasdir (Immutable)
String obyekti haqida bilish kerak bo'lgan eng muhim narsa shundaki, u o'zgarmas (immutable) dir. Ya'ni, bir marta yaratilgandan keyin string uzunroq yoki qisqaroq bo'la olmaydi va uning belgilarini o'zgartirish mumkin emas. O'zgarmas stringlar bir nechta afzalliklarga ega. Birinchidan, bu sizga aslida stringni o'zgartirmasdan stringda operatsiyalar bajarishga imkon beradi.
if (s.ToUpperInvariant().Substring(10, 21).EndsWith("EXE")) {
...
}
Bu yerda ToUpperInvariant yangi stringni qaytaradi; u s stringidagi belgilarni o'zgartirmaydi. Substring ToUpperInvariant qaytargan string ustida ishlaydi va u ham yangi stringni qaytaradi, uni keyin EndsWith tekshiradi. ToUpperInvariant va Substring tomonidan yaratilgan ikkita vaqtinchalik string ilova kodi tomonidan uzoq vaqt murojaat qilinmaydi va garbage collector keyingi yig'ishda ularning xotirasini qaytarib oladi.
O'zgarmas stringlar shuningdek stringni manipulyatsiya qilish yoki unga kirish paytida ip sinxronizatsiyasi muammolari bo'lmasligini anglatadi. Bundan tashqari, CLR uchun bir nechta identik String kontentini bitta String obyekti orqali almashish mumkin, bu esa tizimdagi stringlar sonini kamaytirib xotirani tejaydi — va bu string interning degan narsa (keyinroq muhokama qilinadi).
Ishlash uchun String turi CLR bilan zich bog'langan. Xususan, CLR String turi ichida aniqlangan maydonlarning aniq joylashuvini biladi va bu maydonlarga bevosita kiradi. Bu ishlash va to'g'ridan-to'g'ri kirish kichik rivojlanish qiymati bilan keladi: String klassi sealed, ya'ni siz uni asosiy tur sifatida foydalanib o'z turlaringizni aniqlay olmaysiz.
Stringlarni Solishtirish
Solishtirish stringlar ustida bajariladigan eng keng tarqalgan operatsiya. Stringlarni solishtirish uchun ikkita sabab bor: tenglikni aniqlash yoki ularni saralash (odatda foydalanuvchiga ko'rsatish uchun).
String tengligini aniqlashda yoki stringlarni saralash uchun solishtirishda String klassi tomonidan aniqlangan quyidagi metodlardan birini chaqirish tavsiya etiladi.
Boolean Equals(String value, StringComparison comparisonType)
static Boolean Equals(String a, String b, StringComparison comparisonType)
static Int32 Compare(String strA, String strB, StringComparison comparisonType)
static Int32 Compare(string strA, string strB, Boolean ignoreCase, CultureInfo culture)
static Int32 Compare(String strA, String strB, CultureInfo culture, CompareOptions options)
static Int32 Compare(String strA, Int32 indexA, String strB, Int32 indexB, Int32 length,
StringComparison comparisonType)
Boolean StartsWith(String value, StringComparison comparisonType)
Boolean EndsWith(String value, StringComparison comparisonType)
Saralashda doimo katta-kichik harfga sezgir (case-sensitive) solishtirishlarni bajaring. Sababi shuki, agar faqat harf kattaligi bilan farq qiladigan ikkita string teng deb hisoblansa, ular har safar tartiblashda boshqacha tartiblanishi mumkin va bu foydalanuvchini chalkashtirib yuboradi.
comparisonType argumenti (oldingi metodlarning aksariyatida) StringComparison sanab o'tilgan turining qiymatlaridan biri.
public enum StringComparison {
CurrentCulture = 0,
CurrentCultureIgnoreCase = 1,
InvariantCulture = 2,
InvariantCultureIgnoreCase = 3,
Ordinal = 4,
OrdinalIgnoreCase = 5
}
Ko'pgina dasturlar ichki dasturiy maqsadlarda stringlardan foydalanadi: yo'l nomlari, URL lar, registr kalitlari va qiymatlari, muhit o'zgaruvchilari, reflection, XML teglar, XML atributlari va hokazo. Bu stringlar odatda foydalanuvchiga ko'rsatilmaydi va dastur ichida ishlatiladi. Dasturiy stringlarni solishtirganda doimo StringComparison.Ordinal yoki StringComparison.OrdinalIgnoreCase dan foydalanishingiz kerak. Bu eng tez solishtirish usuli, chunki hech qanday madaniyat ma'lumoti hisobga olinmaydi.
Boshqa tomondan, foydalanuvchiga ko'rsatish uchun stringlarni lingvistik jihatdan to'g'ri solishtirmoqchi bo'lsangiz, StringComparison.CurrentCulture yoki StringComparison.CurrentCultureIgnoreCase dan foydalanishingiz kerak.
Agar ordinal solishtirish oldidan stringning katta-kichik harfini o'zgartirmoqchi bo'lsangiz, String ning ToUpperInvariant yoki ToLowerInvariant metodlaridan foydalanishingiz kerak. Stringlarni normallashtirganda ToLowerInvariant o'rniga ToUpperInvariant dan foydalanish qattiq tavsiya etiladi, chunki Microsoft katta harf solishtirishni bajarish uchun kodni optimallashtirgan.
Quyidagi kod ordinal va madaniyatga oid string solishtirish o'rtasidagi farqni ko'rsatadi.
using System;
using System.Globalization;
public static class Program {
public static void Main() {
String s1 = "Strasse";
String s2 = "Stra\u00dfe";
Boolean eq;
// CompareOrdinal nolga teng bo'lmagan natija qaytaradi.
eq = String.Compare(s1, s2, StringComparison.Ordinal) == 0;
Console.WriteLine("Ordinal solishtirish: '{0}' {2} '{1}'", s1, s2,
eq ? "==" : "!=");
// Germaniya (de) uchun stringlarni to'g'ri solishtirish
CultureInfo ci = new CultureInfo("de-DE");
// Compare nolni qaytaradi.
eq = String.Compare(s1, s2, true, ci) == 0;
Console.WriteLine("Madaniy solishtirish: '{0}' {2} '{1}'", s1, s2,
eq ? "==" : "!=");
}
}
Bu kodni kompilyatsiya qilib ishga tushirsangiz, quyidagi natija chiqadi.
Ordinal solishtirish: 'Strasse' != 'Straße'
Madaniy solishtirish: 'Strasse' == 'Straße'
Compare metodi ordinal solishtirish bajarmayotganida belgi kengaytmalarini (character expansions) amalga oshiradi. Masalan, nemis Eszet belgisi 'ss' ga kengaytiriladi. Xuddi shunday, ligatura belgisi har doim kengaytiriladi. Shu sababli oldingi kod misolida ikkinchi Compare chaqiruvi men qaysi madaniyatni o'tkazsam ham doim 0 ni qaytaradi.
String turi ushbu kitobda ko'rsatilgan versiyalarga qo'shimcha ravishda Equals, StartsWith, EndsWith va Compare metodlarining bir nechta boshqa yuklamalarini (overload) aniqlaydi. Microsoft bu boshqa versiyalardan qochishni tavsiya qiladi. Shuningdek, String ning boshqa solishtirish metodlari — CompareTo (IComparable interfeysi uchun zarur), CompareOrdinal va == hamda != operatorlari ham oldini olinishi kerak. Sababi shuki, chaqiruvchi stringni qanday solishtirish kerakligini aniq ko'rsatmaydi.
String Interning
Stringlarni tenglik uchun tekshirish ko'plab ilovalar uchun keng tarqalgan operatsiya bo'lib, bu vazifa ishlashga sezilarli ta'sir qilishi mumkin. Ordinal tenglik tekshiruvini bajarayotganda CLR avval ikkala stringning belgilar soni bir xilmi yoki yo'qligini tezda tekshiradi. Agar ular teng bo'lmasa, stringlar aniq teng emas. Agar teng bo'lsa, CLR har bir individual belgini solishtirishi kerak.
Bundan tashqari, agar xotirada bir xil stringning bir nechta nusxasi mavjud bo'lsa, bu xotirani isrof qilishdir. Agar bitta instansiya bo'lsa va unga murojaat qiladigan barcha o'zgaruvchilar shu bitta string obyektiga ishora qilsa, xotiradan ancha samarali foydalanasiz.
Agar ilovangiz string tengligini tez-tez case-sensitive, ordinal solishtirishlar yordamida solishtirsa yoki bir xil qiymatga ega ko'plab string obyektlariga ega bo'lishni kutayotgan bo'lsangiz, CLR dagi string interning mexanizmidan foydalanib ishlashni sezilarli yaxshilashingiz mumkin. CLR initsializatsiya qilinganida, u kalitlari stringlar va qiymatlari managed heapdagi String obyektlariga havolalar bo'lgan ichki hash jadval yaratadi. String klassi bu ichki hash jadvalga kirish imkonini beradigan ikkita metodni taklif qiladi.
public static String Intern(String str);
public static String IsInterned(String str);
Intern metodi String ni qabul qiladi, uning hash kodini oladi va ichki hash jadvalda mos keluvchini tekshiradi. Agar identik string allaqachon mavjud bo'lsa, allaqachon mavjud String obyektiga havola qaytariladi. Agar identik string mavjud bo'lmasa, stringning nusxasi yaratiladi, nusxa ichki hash jadvalga qo'shiladi va bu nusxaga havola qaytariladi.
Quyidagi kod string interningni ko'rsatadi.
String s1 = "Hello";
String s2 = "Hello";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // 'False' bo'lishi kerak
s1 = String.Intern(s1);
s2 = String.Intern(s2);
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // 'True'
String interning yordamida ishlashni qanday yaxshilash mumkinligini ko'rish uchun quyidagi misolni ko'rib chiqing. NumTimesWordAppearsEquals metodi so'z va har bir massiv elementi bitta so'zga ishora qiladigan stringlar massivini qabul qiladi. Bu metod berilgan so'zning so'zlar ro'yxatida necha marta uchrashini aniqlaydi.
private static Int32 NumTimesWordAppearsEquals(String word, String[] wordlist) {
Int32 count = 0;
for (Int32 wordnum = 0; wordnum < wordlist.Length; wordnum++) {
if (word.Equals(wordlist[wordnum], StringComparison.Ordinal))
count++;
}
return count;
}
Endi interningdan foydalanib yozilgan versiyasini ko'ring.
private static Int32 NumTimesWordAppearsIntern(String word, String[] wordlist) {
// Bu metod wordlist dagi barcha elementlar interned stringlarga ishora qilishini faraz qiladi.
word = String.Intern(word);
Int32 count = 0;
for (Int32 wordnum = 0; wordnum < wordlist.Length; wordnum++) {
if (Object.ReferenceEquals(word, wordlist[wordnum]))
count++;
}
return count;
}
Bu metod so'zni intern qiladi va so'zlar ro'yxati interned stringlarga havolalarni o'z ichiga oladi deb faraz qiladi. Birinchidan, bu versiya xotirani tejashi mumkin, chunki so'z ro'yxatda bir necha marta uchralsa, endi bir nechta havola xuddi shu bitta String obyektiga ishora qiladi. Ikkinchidan, bu versiya tezroq, chunki berilgan so'z massivda bor-yo'qligini aniqlash faqat ko'rsatkichlarni solishtirish masalasiga aylanadi.
String interning ehtiyotkorlik bilan va ogohlanish bilan ishlatilishi kerak. Shu sababli C# kompilyatori assemblyni kompilyatsiya qilganingizda doimo string interning yoqilmasligini ko'rsatadigan atribut/bayroqni belgilaydi.
String Pooling
Manba kodini kompilyatsiya qilganda kompilyator har bir literal stringni qayta ishlash va stringni boshqariladigan modulning metama'lumotlariga joylashtirish kerak. Agar bir xil literal string manba kodingizda bir necha marta uchralsa, bu stringlarning barchasini metama'lumotlarga joylashtirish natija faylining hajmini oshiradi.
Bu shishishni yo'q qilish uchun ko'pgina kompilyatorlar (jumladan C# kompilyatori) literal stringni modulning metama'lumotlariga faqat bir marta yozadi. Kompilyatorning bitta stringning bir nechta nusxalarini bitta stringga birlashtirish qobiliyati modulning hajmini sezilarli kamaytirishi mumkin. Bu jarayon string pooling deb ataladi. String pooling stringlar ishlashini yaxshilashning yana bir usuli va repertuaringizda bo'lishi kerak bo'lgan bilim.
Stringning Belgilarini va Matn Elementlarini Tekshirish
Garchi stringlarni solishtirish foydali bo'lsa-da, ba'zan string ichidagi belgilarni tekshirish kerak. String turi bunga yordam beradigan bir nechta xususiyatlar va metodlarni taklif qiladi, jumladan Length, Chars (C# da indekser), GetEnumerator, ToCharArray, Contains, IndexOf, LastIndexOf, IndexOfAny va LastIndexOfAny.
Aslida, System.Char har doim bitta mavhum Unicode belgisiga teng bo'lmasligi mumkin bo'lgan bitta 16-bitli Unicode kod qiymatini ifodalaydi. Masalan, ba'zi mavhum Unicode belgilari ikki kod qiymatining kombinatsiyasi. Ba'zi Unicode matn elementlari bir nechta 16-bitli kod qiymatlarini talab qiladi. Birinchi kod qiymati yuqori surrogat, ikkinchisi past surrogat deb ataladi. Yuqori surrogatlar U+D800 dan U+DBFF gacha qiymatga ega, past surrogatlar esa U+DC00 dan U+DFFF gacha.
Matn elementlari bilan to'g'ri ishlash uchun System.Globalization.StringInfo turidan foydalanishingiz kerak. Bu turdan foydalanishning eng oson usuli uning konstruktoriga string uzatib instansiya yaratish, keyin LengthInTextElements xususiyatini so'rash va SubstringByTextElements metodidan foydalanish.
using System;
using System.Text;
using System.Globalization;
using System.Windows.Forms;
public sealed class Program {
public static void Main() {
// Quyidagi string birlashtiruvchi belgilarni o'z ichiga oladi
String s = "a\u0304\u0308bc\u0327";
SubstringByTextElements(s);
EnumTextElements(s);
EnumTextElementIndexes(s);
}
private static void SubstringByTextElements(String s) {
String output = String.Empty;
StringInfo si = new StringInfo(s);
for (Int32 element = 0; element < si.LengthInTextElements; element++) {
output += String.Format(
"Matn elementi {0} — '{1}'{2}",
element, si.SubstringByTextElements(element, 1),
Environment.NewLine);
}
MessageBox.Show(output, "SubstringByTextElements natijasi");
}
private static void EnumTextElements(String s) {
String output = String.Empty;
TextElementEnumerator charEnum =
StringInfo.GetTextElementEnumerator(s);
while (charEnum.MoveNext()) {
output += String.Format(
"Indeks {0} dagi belgi — '{1}'{2}",
charEnum.ElementIndex, charEnum.GetTextElement(),
Environment.NewLine);
}
MessageBox.Show(output, "GetTextElementEnumerator natijasi");
}
private static void EnumTextElementIndexes(String s) {
String output = String.Empty;
Int32[] textElemIndex = StringInfo.ParseCombiningCharacters(s);
for (Int32 i = 0; i < textElemIndex.Length; i++) {
output += String.Format(
"Belgi {0} indeks {1} da boshlanadi{2}",
i, textElemIndex[i], Environment.NewLine);
}
MessageBox.Show(output, "ParseCombiningCharacters natijasi");
}
}
Boshqa String Operatsiyalari
String turi stringni yoki uning qismlarini nusxalash imkonini beradigan metodlarni ham taklif qiladi. 14-1 jadval nusxalash metodlarini jamlaydi.
| A'zo | A'zo Turi | Tavsifi |
|---|---|---|
Clone | Instansiya | Xuddi shu obyektga havolani qaytaradi (this). String obyektlari o'zgarmas bo'lganligi uchun bu muammo emas. String ning ICloneable interfeysini amalga oshiradi. |
Copy | Statik | Ko'rsatilgan stringning yangi dublikat stringini qaytaradi. Kamdan-kam ishlatiladi; stringlarni tokenlar sifatida ko'radigan ilovalar uchun mavjud. |
CopyTo | Instansiya | Stringning belgilarining bir qismini belgilar massiviga nusxalaydi. |
Substring | Instansiya | Asl stringning bir qismini ifodalovchi yangi stringni qaytaradi. |
ToString | Instansiya | Xuddi shu obyektga havolani qaytaradi (this). |
Bu metodlardan tashqari, String stringni manipulyatsiya qiladigan ko'plab statik va instansiya metodlarini taklif qiladi: Insert, Remove, PadLeft, Replace, Split, Join, ToLower, ToUpper, Trim, Concat, Format va hokazo. Bu metodlarning barchasi haqida eslab qolish kerak bo'lgan muhim narsa shuki, ular yangi string obyektlarini qaytaradi; chunki stringlar o'zgarmas bo'lganligi sababli, yaratilganidan keyin ularni o'zgartirish mumkin emas (xavfsiz kod ishlatilgan holda).
Stringni Samarali Qurish: StringBuilder
String turi o'zgarmas stringni ifodalaganligi sababli, FCL System.Text.StringBuilder turini taqdim etadi, bu sizga stringlar va belgilar bilan dinamik operatsiyalarni samarali bajarishga va String yaratishga imkon beradi. StringBuilder ni String yaratish uchun chiroyli konstruktor deb o'ylang. Umuman olganda, metodlaringizni StringBuilder parametrlari emas, String parametrlari bilan loyihalashingiz kerak.
Mantiqiy jihatdan, StringBuilder obyekti Char strukturalari massiviga ishora qiladigan maydonga ega. StringBuilder ning a'zolari bu belgilar massivini manipulyatsiya qilish imkonini beradi, samarali ravishda stringni qisqartirish yoki undagi belgilarni o'zgartirish. Agar siz stringni belgilar massivining ajratilgan o'lchamidan oshirsangiz, StringBuilder avtomatik ravishda yangi, kattaroq massivni ajratadi, belgilarni nusxalaydi va eski massivdan foydalanishni to'xtatadi. Oldingi massiv garbage collect qilinadi.
StringBuilder obyektidan foydalanib stringingizni qurish tugagach, StringBuilder ning ToString metodini chaqirib belgilar massivini String ga "aylantiring". Bu StringBuilder dagi stringni o'z ichiga olgan heapda yangi String obyektini yaratadi.
StringBuilder Obyektini Yaratish
String klassidan farqli o'laroq, CLR StringBuilder klassi haqida maxsus hech narsa bilmaydi. Bundan tashqari, aksariyat tillar (jumladan C#) StringBuilder klassini primitiv tur deb hisoblamaydi. StringBuilder obyektini boshqa har qanday non-primitiv tur kabi yaratasiz.
StringBuilder sb = new StringBuilder();
StringBuilder turi ko'plab konstruktorlarni taklif qiladi. Har bir konstruktorning vazifasi har bir StringBuilder obyekti tomonidan saqlanadigan holatni ajratish va initsializatsiya qilish:
- Maksimal sig'im (Maximum capacity) — Stringga joylashtirilishi mumkin bo'lgan maksimal belgilar sonini belgilaydigan
Int32qiymati. Standart qiymatiInt32.MaxValue(taxminan 2 milliard). Yaratilgandan keyinStringBuilderning maksimal sig'im qiymatini o'zgartirish mumkin emas. - Sig'im (Capacity) —
StringBuildertomonidan saqlanadigan belgilar massivining hajmini ko'rsatadiganInt32qiymati. Standart qiymati 16. Belgilar massiviga belgilar qo'shganda,StringBuildermassivning sig'imidan oshishga urinayotganini aniqlaydi. Agar shunday bo'lsa,StringBuilderavtomatik ravishda sig'imni ikki baravar oshiradi, yangi massiv ajratadi va belgilarni eski massivdan yangisiga nusxalaydi. Eski massiv garbage collect qilinadi. Massivni dinamik o'stirish ishlashga salbiy ta'sir qiladi; yaxshi boshlang'ich sig'im belgilash orqali bundan qoching. - Belgilar massivi (Character array) — String dagi belgilar to'plamini saqlaydigan
Charstrukturalari massivi.StringBuilderningLengthxususiyatidan massivdagi foydalanilgan belgilar sonini olishingiz mumkin.StringBuilderyaratishdaStringuzatib belgilar massivini initsializatsiya qilishingiz mumkin.
StringBuilder A'zolari
String dan farqli o'laroq, StringBuilder o'zgaruvchan (mutable) stringni ifodalaydi. Bu shuni anglatadiki, StringBuilder ning aksariyat a'zolari belgilar massividagi kontentni o'zgartiradi va boshqariladigan heapda yangi obyektlar yaratilishiga olib kelmaydi. 14-2 jadval StringBuilder ning a'zolarini jamlaydi.
| A'zo | A'zo Turi | Tavsifi |
|---|---|---|
MaxCapacity | Faqat o'qish xususiyati | Stringga joylashtirilishi mumkin bo'lgan eng katta belgilar soni. |
Capacity | O'qish/yozish xususiyati | Belgilar massivining hajmi. Sig'imni stringning uzunligidan kichikroq yoki MaxCapacity dan kattaroq qilishga urinish ArgumentOutOfRangeException tashlaydi. |
EnsureCapacity | Metod | Belgilar massivi kamida ko'rsatilgan hajmda ekanligini kafolatlaydi. |
Length | O'qish/yozish xususiyati | "String" dagi belgilar soni. 0 ga o'rnatish StringBuilder ning kontentini bo'sh stringga tiklaydi. |
ToString | Metod | StringBuilder ning belgilar massivini ifodalovchi String ni qaytaradi. |
Chars | O'qish/yozish indekser | Belgilar massividagi ko'rsatilgan indeksdagi belgini oladi yoki o'rnatadi. C# da bu massiv sintaksisi ([]) yordamida kirish mumkin bo'lgan indekser. |
Clear | Metod | StringBuilder ning kontentini tozalaydi, Length ni 0 ga o'rnatish bilan bir xil. |
Append | Metod | Belgilar massivining oxiriga bitta obyektni qo'shadi. Obyekt umumiy format va chaqiruvchi ipning madaniyatidan foydalanib stringga aylantiriladi. |
Insert | Metod | Belgilar massivining ko'rsatilgan pozitsiyasiga bitta obyektni qo'yadi. |
AppendFormat | Metod | Ko'rsatilgan obyektlarni belgilar massivining oxiriga qo'shadi. Obyektlar chaqiruvchi tomonidan taqdim etilgan formatlash va madaniyat ma'lumotlaridan foydalanib stringlarga aylantiriladi. StringBuilder obyektlari bilan ishlatiladigan eng keng tarqalgan metodlardan biri. |
AppendLine | Metod | Belgilar massivining oxiriga bo'sh satr yoki stringni yangi satr bilan qo'shadi. |
Replace | Metod | Belgilar massivi ichida bir belgini boshqasiga yoki bir stringni boshqasiga almashtiradi. |
Remove | Metod | Belgilar massividan belgilar diapazonini olib tashlaydi. |
Equals | Metod | Faqat ikkala StringBuilder obyekti bir xil maksimal sig'im, sig'im va belgilarga ega bo'lgandagina true qaytaradi. |
CopyTo | Metod | StringBuilder ning belgilarining bir qismini Char massiviga nusxalaydi. |
StringBuilder ning metodlari haqida e'tiborga olish kerak bo'lgan muhim narsa shundaki, ularning aksariyati xuddi shu StringBuilder obyektiga havolani qaytaradi. Bu bir nechta operatsiyalarni birlashtirish uchun qulay sintaksisni beradi.
StringBuilder sb = new StringBuilder();
String s = sb.AppendFormat("{0} {1}", "Jeffrey", "Richter").
Replace(' ', '-').Remove(4, 3).ToString();
Console.WriteLine(s); // "Jeff-Richter"
String va StringBuilder klasslari to'liq metod paritetiga ega emasligini ko'rasiz; ya'ni String da ToLower, ToUpper, EndsWith, PadLeft, PadRight, Trim va hokazo bor. StringBuilder klassi bu metodlarning hech birini taklif qilmaydi. Boshqa tomondan, StringBuilder klassi stringning bir qismida belgilar yoki stringlarni almashtirish imkonini beradigan boyroq Replace metodini taklif qiladi. Bu ikki klass o'rtasida to'liq paritetning yo'qligi achinarli, chunki endi muayyan vazifalarni bajarish uchun String va StringBuilder o'rtasida konvertatsiya qilishingiz kerak.
// String manipulyatsiyalarini bajarish uchun StringBuilder yaratish.
StringBuilder sb = new StringBuilder();
// StringBuilder yordamida ba'zi string manipulyatsiyalarini bajarish.
sb.AppendFormat("{0} {1}", "Jeffrey", "Richter").Replace(" ", "-");
// StringBuilder ni String ga aylantirish,
// barcha belgilarni katta harfga o'tkazish uchun.
String s = sb.ToString().ToUpper();
// StringBuilder ni tozalash (yangi Char massivi ajratadi).
sb.Length = 0;
// Katta harfli String ni StringBuilder ga yuklash,
// va ko'proq manipulyatsiyalar bajarish.
sb.Append(s).Insert(8, "Marc-");
// StringBuilder ni yana String ga aylantirish.
s = sb.ToString();
// String ni foydalanuvchiga ko'rsatish.
Console.WriteLine(s); // "JEFFREY-Marc-RICHTER"
Obyektning String Ifodasini Olish: ToString
Siz tez-tez obyektning string ifodasini olishingiz kerak bo'ladi. Odatda bu raqamli turni (masalan Byte, Int32 va Single) yoki DateTime obyektini foydalanuvchiga ko'rsatish kerak bo'lganda zarur. .NET Framework obyektga yo'naltirilgan platforma bo'lganligi sababli, har bir tur instansiyaning qiymatini string ekvivalentiga aylantirish uchun kod taqdim etish mas'uliyatini o'z zimmasiga oladi.
Siz har qanday obyektning string ifodasini ToString metodini chaqirish orqali olishingiz mumkin. System.Object tomonidan aniqlangan public, virtual, parametrsiz ToString metodi har qanday turning instansiyasida chaqirilishi mumkin. Semantik jihatdan, ToString obyektning joriy qiymatini ifodalovchi stringni qaytaradi va bu string chaqiruvchi ipning joriy madaniyatiga muvofiq formatlanishi kerak.
System.Object ning ToString implementatsiyasi shunchaki obyektning turning to'liq nomini qaytaradi. Bu qiymat ayniqsa foydali emas, lekin ko'plab turlar uchun oqilona stringni taklif qila olmaydigan ko'plab turlar uchun oqilona standart qiymat. Joriy qiymatning string ifodasini olish uchun oqilona usulni taklif qilmoqchi bo'lgan barcha turlar ToString metodini qayta yozishi kerak.
Maxsus Formatlar va Madaniyatlar
Parametrsiz ToString metodida ikkita muammo bor. Birinchidan, chaqiruvchi raqamni valyuta stringi, o'nli kasrli string, foiz stringi yoki o'n oltilik stringga formatlashni xohlashi mumkin. Ikkinchidan, chaqiruvchi maxsus madaniyat yordamida stringni formatlashni oson tanlashga qodir emas. Ko'proq nazorat qilish uchun formatlash va madaniyat ma'lumotlarini aniq ko'rsatish imkonini beradigan ToString versiyasi kerak.
Chaqiruvchiga formatlash va madaniyatda tanlov taklif qiladigan turlar System.IFormattable interfeysini amalga oshiradi.
public interface IFormattable {
String ToString(String format, IFormatProvider formatProvider);
}
FCL da barcha asosiy turlar (Byte, SByte, Int16/UInt16, Int32/UInt32, Int64/UInt64, Single, Double, Decimal va DateTime) ushbu interfeysni amalga oshiradi. Bundan tashqari, Guid kabi boshqa turlar ham, har bir sanab o'tilgan tur ta'rifi ushbu interfeysni avtomatik amalga oshiradi.
IFormattable ning ToString metodi ikkita parametr qabul qiladi. Birinchisi, format — bu metodni obyekt qanday formatlanishi kerakligini aytadigan string. Ikkinchi parametr, formatProvider — System.IFormatProvider interfeysini amalga oshiradigan tur instansiyasi. Bu tur ToString metodiga aniq madaniyat ma'lumotini yetkazadi.
Microsoft FCL da aniqlagan ko'plab turlar bir nechta formatlarni taniydi. Masalan, DateTime turi qisqa sana uchun "d", uzun sana uchun "D", umumiy uchun "g", saralanuvchi uchun "s" va hokazolarni qo'llab-quvvatlaydi. Barcha o'rnatilgan raqamli turlar valyuta uchun "C", o'nli kasrlar uchun "D", eksponensial uchun "E", belgilangan nuqta uchun "F", umumiy uchun "G", raqam uchun "N", foiz uchun "P", qayta aylantiruv uchun "R" va o'n oltilik uchun "X" ni qo'llab-quvvatlaydi.
Raqamni formatlashda, ToString metodi formatProvider parametrini ko'radi. Agar null uzatilsa, ToString chaqiruvchi ipning madaniyatini System.Globalization.CultureInfo.CurrentCulture xususiyatidan o'qish orqali aniqlaydi.
System.Globalization.CultureInfo turi FCL dagi IFormatProvider interfeysini amalga oshiradigan juda kam turlardan biri. Agar siz, masalan, Vetnam uchun stringni formatlashni istasangiz, CultureInfo obyektini yaratib, uni ToString ning formatProvider parametriga o'tkazasiz.
Decimal price = 123.54M;
String s = price.ToString("C", new CultureInfo("vi-VN"));
MessageBox.Show(s);
Agar hech qanday maxsus madaniyat uchun formatlanmagan obyektning stringini olishni istasangiz, System.Globalization.CultureInfo ning statik InvariantCulture xususiyatini chaqirib, qaytarilgan obyektni ToString ning formatProvider parametri sifatida o'tkazishingiz kerak.
Decimal price = 123.54M;
String s = price.ToString("C", CultureInfo.InvariantCulture);
MessageBox.Show(s);
Bir Nechta Obyektni Bitta Stringga Formatlash
Ba'zida ko'plab formatlangan obyektlardan iborat stringlarni yaratishni xohlaysiz. Masalan, quyidagi stringda sana, ism va yosh bor.
String s = String.Format("On {0}, {1} is {2} years old.",
new DateTime(2012, 4, 22, 14, 35, 5), "Aidan", 9);
Console.WriteLine(s);
Agar ushbu kodni "en-US" joriy madaniyat bilan qurib ishga tushirsangiz, quyidagi natijani ko'rasiz.
On 4/22/2012 2:35:05 PM, Aidan is 9 years old.
String ning statik Format metodi figurali qavslar ichida raqamlar bilan almashtiriluvchi parametrlarni aniqlaydigan format stringini qabul qiladi. Ichki tarzda Format metodi har bir obyektning ToString metodini chaqirib stringini oladi. Keyin qaytarilgan stringlar birlashtirilib yakuniy string hosil qilinadi.
Figurali qavslar ichida format ma'lumotini ko'rsatish orqali ob'yektni formatlashda ko'proq nazoratga ega bo'lishingiz mumkin.
String s = String.Format("On {0:D}, {1} is {2:E} years old.",
new DateTime(2012, 4, 22, 14, 35, 5), "Aidan", 9);
Console.WriteLine(s);
Natija (agar "en-US" joriy madaniyat bo'lsa):
On Sunday, April 22, 2012, Aidan is 9.000000E+000 years old.
Format metodi format stringini tahlil qilganida, almashtiriluvchi parametr 0 uchun o'zining IFormattable interfeysining ToString metodi ikki parametr bilan — "D" va null — chaqirilishi kerakligini ko'radi. Xuddi shunday, parametr 2 uchun "E" va null bilan chaqiriladi.
O'zingizning Maxsus Formatlashtirgichingiz
.NET Framework dagi formatlash imkoniyatlari katta moslashuvchanlik va nazoratni taklif qilish uchun mo'ljallangan. StringBuilder ning AppendFormat metodi har qanday obyekt stringga formatlanayotganida chaqiradigan funktsiyani aniqlash mumkin. Ya'ni ToString ni chaqirish o'rniga AppendFormat siz aniqlagan funktsiyani chaqirishi mumkin, bu sizga har qanday yoki barcha obyektlarni xohlaganingizcha formatlash imkonini beradi.
Buni HTML matnini formatlaydigan misol yordamida tushuntiraman. Brauzerda ko'rsatiladigan HTML matnini formatlayotgan bo'lsangiz va barcha Int32 qiymatlari qalin ko'rinishda bo'lishini istayotgan bo'lsangiz. Buning uchun IFormatProvider va ICustomFormatter interfeyslarini amalga oshiruvchi klass yaratasiz.
using System;
using System.Text;
using System.Threading;
public static class Program {
public static void Main() {
StringBuilder sb = new StringBuilder();
sb.AppendFormat(new BoldInt32s(), "{0} {1} {2:M}", "Jeff", 123, DateTime.Now);
Console.WriteLine(sb);
}
}
internal sealed class BoldInt32s : IFormatProvider, ICustomFormatter {
public Object GetFormat(Type formatType) {
if (formatType == typeof(ICustomFormatter)) return this;
return Thread.CurrentThread.CurrentCulture.GetFormat(formatType);
}
public String Format(String format, Object arg, IFormatProvider formatProvider) {
String s;
IFormattable formattable = arg as IFormattable;
if (formattable == null) s = arg.ToString();
else s = formattable.ToString(format, formatProvider);
if (arg.GetType() == typeof(Int32))
return "<B>" + s + "</B>";
return s;
}
}
Bu kodni kompilyatsiya qilib ishga tushirsangiz (agar "en-US" joriy madaniyat bo'lsa), natija shunga o'xshash bo'ladi:
Jeff <B>123</B> September 1
AppendFormat ichki tarzda qanday ishlashining psevdokodi: Agar IFormatProvider uzatilgan bo'lsa, u ICustomFormatter turini so'rab GetFormat ni chaqiradi. Agar maxsus formatlashtirgich mavjud bo'lsa, AppendFormat har bir almashtiriluvchi parametrni formatlash uchun ICustomFormatter ning Format metodini chaqiradi. Agar maxsus formatlashtirgich argumentni formatlamasa, AppendFormat turning IFormattable interfeysi yoki standart ToString metodiga murojaat qiladi.
Stringni Tahlil Qilib Obyekt Olish: Parse
Oldingi bo'limda men obyektni qanday olib, uning string ifodasini olishni tushuntirdim. Ushbu bo'limda men teskari haqida gaplashaman: stringni qanday olib, undan obyekt ifodasini olish.
Stringni tahlil qila oladigan har qanday tur Parse nomli public, statik metodni taklif qiladi. Bu metod String ni qabul qiladi va turning instansiyasini qaytaradi; bir ma'noda Parse fabrika (factory) vazifasini bajaradi. FCL da Parse metodi barcha raqamli turlar, shuningdek DateTime, TimeSpan va boshqa turlar uchun mavjud.
Stringni raqamli turga qanday tahlil qilishni ko'rib chiqaylik. Deyarli barcha raqamli turlar kamida bitta Parse metodini taklif qiladi. Int32 turi tomonidan aniqlangan Parse metodi:
public static Int32 Parse(String s, NumberStyles style,
IFormatProvider provider);
Ushbu Parse metodi uchta parametrni qabul qiladi. String parametr s tahlil qilinmoqchi bo'lgan raqamning string ifodasini aniqlaydi. System.Globalization.NumberStyles parametr style Parse stringda kutishi kerak bo'lgan belgilarni aniqlaydigan bit bayroqlari to'plami. IFormatProvider parametr provider madaniyatga xos ma'lumotni olish uchun foydalanishi mumkin bo'lgan obyektni aniqlaydi.
Masalan, quyidagi kod Parse ning System.FormatException tashlanishiga sabab bo'ladi, chunki tahlil qilinayotgan stringda bosh bo'sh joy bor.
Int32 x = Int32.Parse(" 123", NumberStyles.None, null);
Bosh bo'sh joyni o'tkazishga ruxsat berish uchun style parametrini o'zgartiring.
Int32 x = Int32.Parse(" 123", NumberStyles.AllowLeadingWhite, null);
O'n oltilik sonni tahlil qilish uchun kod qismi:
Int32 x = Int32.Parse("1A", NumberStyles.HexNumber, null);
Console.WriteLine(x); // "26" ko'rsatadi
Qulaylik uchun Int32 turi Parse metodining to'rtta yuklamasini taklif qiladi.
// style uchun NumberStyles.Integer va ip madaniyatini uzatadi.
public static Int32 Parse(String s);
// Ip madaniyatining provider ma'lumotini uzatadi.
public static Int32 Parse(String s, NumberStyles style);
// style uchun NumberStyles.Integer ni uzatadi.
public static Int32 Parse(String s, IFormatProvider provider);
// Bu bobda gaplashilayotgan metod.
public static Int32 Parse(String s, NumberStyles style,
IFormatProvider provider);
DateTime turi ham Parse metodini taklif qiladi.
public static DateTime Parse(String s,
IFormatProvider provider, DateTimeStyles styles);
Ba'zi dasturchilar Microsoft ga quyidagini xabar qilishgan: ularning ilovasi Parse ni tez-tez chaqirganida va Parse istisnolarni qayta-qayta tashlaganida (foydalanuvchining noto'g'ri kiritishi sababli) ilovaning ishlashi yomonlashadi. Shuning uchun Microsoft barcha raqamli turlar, DateTime, DateTimeOffset, TimeSpan va hatto IPAddress ga TryParse metodlarini qo'shdi.
public static Boolean TryParse(String s, NumberStyles style,
IFormatProvider provider, out Int32 result);
Ko'rib turganingizdek, bu metod ko'rsatilgan stringni Int32 ga tahlil qilish mumkinligini ko'rsatuvchi true yoki false qaytaradi. Agar metod true qaytarsa, havola orqali uzatilgan o'zgaruvchi tahlil qilingan raqamli qiymatni o'z ichiga oladi.
Kodlashlar: Belgilar va Baytlar O'rtasidagi Konvertatsiya
CLR da barcha belgilar 16-bitli Unicode kod qiymatlari sifatida ifodalanadi va barcha stringlar 16-bitli Unicode kod qiymatlaridan iborat. Bu belgilar va stringlar bilan ishlashni osonlashtiradi.
Ba'zida siz stringlarni faylga saqlash yoki tarmoq orqali uzatishni xohlaysiz. Agar stringlar asosan ingliz tilida so'zlashuvchilar tomonidan o'qiladigan belgilardan iborat bo'lsa, 16-bitli qiymatlar to'plamini saqlash yoki uzatish unchalik samarali emas, chunki yozilgan baytlarning yarmi nolni o'z ichiga oladi. 16-bitli qiymatlarni siqilgan baytlar massiviga kodlash va keyin baytlar massivini 16-bitli qiymatlar massiviga qayta dekodlash samaraliroq bo'ladi.
FCL belgilar kodlash va dekodlashni osonlashtirish uchun bir nechta turlarni taklif qiladi. Eng ko'p ishlatiladigan ikkita kodlash UTF-16 va UTF-8:
- UTF-16 — Har bir 16-bitli belgini 2 bayt sifatida kodlaydi. Belgilarni umuman ta'sirmaydi va hech qanday siqish sodir bo'lmaydi — ishlashi a'lo. UTF-16 kodlash shuningdek Unicode kodlash deb ham ataladi. UTF-16 little-endian dan big-endian ga va aksincha aylantirish uchun ishlatilishi mumkin.
- UTF-8 — Ba'zi belgilarni 1 bayt, ba'zilarini 2 bayt, ba'zilarini 3 bayt va ba'zilarini 4 bayt sifatida kodlaydi. 0x0080 dan past qiymatli belgilar 1 baytga siqiladi, bu AQSh da ishlatiladigan belgilar uchun juda yaxshi ishlaydi. 0x0080 va 0x07FF orasidagi belgilar 2 baytga aylantiriladi, bu Yevropa va Yaqin Sharq tillari uchun yaxshi ishlaydi. 0x0800 va undan yuqori belgilar 3 baytga aylantiriladi, bu Sharqiy Osiyo tillari uchun yaxshi ishlaydi. Surrogatlar juftligi 4 bayt sifatida yoziladi. UTF-8 juda mashhur kodlash.
UTF-16 va UTF-8 kodlashlar eng keng tarqalgan bo'lsa-da, FCL kamroq ishlatiladigan ba'zi kodlashlarni ham qo'llab-quvvatlaydi:
- UTF-32 — Barcha belgilarni 4 bayt sifatida kodlaydi. Belgilarni bosib o'tish uchun oddiy algoritm yozmoqchi bo'lganingizda foydali. UTF-32 xotira jihatdan samarali emas va shuning uchun faylga yoki tarmoqqa saqlash yoki uzatish uchun kamdan-kam foydalaniladi.
- UTF-7 — Odatda 7-bitli qiymatlar bilan ishlashi mumkin bo'lgan eski tizimlar bilan ishlatiladi. Bu kodlashdan qochishingiz kerak, chunki u odatda ma'lumotlarni siqish emas, kengaytirishga olib keladi. Unicode konsorsiumi bu kodlashni eskirgan deb belgilagan.
- ASCII — 16-bitli belgilarni ASCII belgilariga kodlaydi; ya'ni 0x0080 dan past qiymatli har qanday 16-bitli belgi bitta baytga aylantiriladi. 0x007F dan katta qiymatli har qanday belgi aylantirib bo'lmaydi va uning qiymati yo'qoladi.
Belgilar to'plamini kodlash yoki dekodlash kerak bo'lganda, System.Text.Encoding dan hosila bo'lgan klassning instansiyasini olishingiz kerak. Encoding bir nechta statik faqat o'qish xususiyatlarini taklif qiladigan abstrakt asosiy klass bo'lib, har biri Encoding-hosila klassning instansiyasini qaytaradi.
Quyida UTF-8 yordamida belgilarni kodlash va dekodlash misoli.
using System;
using System.Text;
public static class Program {
public static void Main() {
// Kodlamoqchi bo'lgan string
String s = "Hi there.";
// UTF8 yordamida kodlash/dekodlashni biladigan
// Encoding-hosila obyektni olish
Encoding encodingUTF8 = Encoding.UTF8;
// Stringni baytlar massiviga kodlash.
Byte[] encodedBytes = encodingUTF8.GetBytes(s);
// Kodlangan bayt qiymatlarini ko'rsatish.
Console.WriteLine("Kodlangan baytlar: " +
BitConverter.ToString(encodedBytes));
// Baytlar massivini yana stringga dekodlash.
String decodedString = encodingUTF8.GetString(encodedBytes);
// Dekodlangan stringni ko'rsatish.
Console.WriteLine("Dekodlangan string: " + decodedString);
}
}
Bu kod quyidagi natijani beradi.
Kodlangan baytlar: 48-69-20-74-68-65-72-65-2E
Dekodlangan string: Hi there.
UTF8 statik xususiyatiga qo'shimcha ravishda, Encoding klassi quyidagi statik xususiyatlarni ham taklif qiladi: Unicode, BigEndianUnicode, UTF32, UTF7, ASCII va Default. Encoding shuningdek statik GetEncoding metodini taklif qiladi, u sizga kod sahifasini (butun son yoki string bo'yicha) ko'rsatish va ko'rsatilgan kod sahifasi yordamida kodlash/dekodlash mumkin bo'lgan obyektni qaytarish imkonini beradi.
Barcha Encoding-hosila turlar kodlashni amalga oshirmasdan belgilar to'plamini kodlash uchun zarur bo'lgan baytlar sonini oladigan GetByteCount metodini taklif qiladi. Shuningdek dekodlash natijasida hosil bo'ladigan belgilar sonini qaytaradigan GetCharCount metodi ham mavjud.
| Metod | Tavsifi |
|---|---|
GetPreamble | Har qanday kodlangan baytlarni oqimga yozishdan oldin yozilishi kerak bo'lgan baytlar massivini qaytaradi. Bu baytlar ko'pincha BOM (byte order mark) baytlari deb ataladi. |
Convert | Manba kodlashdagi baytlar massivini maqsad kodlashdagi baytlar massiviga aylantiradi. |
Equals | Ikkala Encoding-hosila obyekti bir xil kod sahifasi va preambulni ifodalasa true qaytaradi. |
GetHashCode | Kodlash obyektining kod sahifasini qaytaradi. |
Belgilar va Baytlar Oqimlarini Kodlash va Dekodlash
Tasavvur qiling, siz System.Net.Sockets.NetworkStream obyekti orqali UTF-16 kodlangan stringni o'qiyapsiz. Baytlar oqimga bo'laklarda kelishi ehtimoli katta. Boshqacha aytganda, avval 5 bayt, keyin 7 bayt o'qishingiz mumkin. UTF-16 da har bir belgi 2 baytdan iborat. 5 baytli birinchi massivni Encoding ning GetString metodiga uzatganda faqat ikkita belgidan iborat string qaytariladi. Agar keyin 7 baytli massivni uzatsangiz, uchta belgidan iborat string qaytariladi, lekin barcha kod nuqtalari noto'g'ri qiymatlarga ega bo'ladi!
Bu ma'lumotlar buzilish muammosi Encoding klasslari chaqiruvlar orasidagi holatni saqlamasligi sababli yuzaga keladi. Agar siz belgilar/baytlarni bo'laklarda kodlash yoki dekodlash bilan shug'ullanadigan bo'lsangiz, ma'lumot yo'qolishini oldini olish uchun chaqiruvlar o'rtasida holatni saqlash bo'yicha qo'shimcha ish qilishingiz kerak.
Baytlar bo'laklarini dekodlash uchun Encoding-hosila obyektning GetDecoder metodini chaqirib System.Text.Decoder klassidan hosila bo'lgan obyektga havola olishingiz kerak. Barcha Decoder-hosila klasslar ikkita muhim metodni taklif qiladi: GetChars va GetCharCount. Bu metodlarni chaqirganingizda, bayt massivida belgini to'ldirish uchun yetarli bayt bo'lmasa, qoldiq baytlar dekoder obyekti ichida saqlanadi. Keyingi chaqiruvda dekoder obyekti qoldiq baytlarni yangi bayt massiviga qo'shadi — bu ma'lumotlar bo'laklarining to'g'ri dekodlanishini ta'minlaydi.
Stringlarni bo'laklarda kodlashni istasangiz, GetDecoder o'rniga GetEncoder ni chaqiring. GetEncoder System.Text.Encoder abstrakt asosiy klassidan hosila bo'lgan obyektni qaytaradi. Barcha Encoder-hosila klasslar ikkita muhim metodni taklif qiladi: GetBytes va GetByteCount. Har bir chaqiruvda Encoder-hosila obyekti har qanday qoldiq holat ma'lumotini saqlaydi, shuning uchun siz ma'lumotlarni bo'laklarda kodlashingiz mumkin.
Base-64 String Kodlash va Dekodlash
UTF-16 va UTF-8 kodlashlar juda mashhur. Shuningdek, baytlar ketma-ketligini base-64 stringga kodlash ham juda mashhur. FCL base-64 kodlash va dekodlash uchun metodlar taklif qiladi. Biroq base-64 kodlash va dekodlash Encoding-hosila tur orqali emas, System.Convert turining statik metodlari orqali amalga oshiriladi.
Baytlar massivini base-64 string sifatida kodlash uchun Convert ning statik FromBase64String yoki FromBase64CharArray metodini chaqirasiz. Aksincha, baytlar massivini base-64 string sifatida dekodlash uchun Convert ning statik ToBase64String yoki ToBase64CharArray metodini chaqirasiz. Quyidagi kod bu metodlarning ba'zilaridan qanday foydalanishni ko'rsatadi.
using System;
public static class Program {
public static void Main() {
// 10 ta tasodifiy baytlar to'plamini olish
Byte[] bytes = new Byte[10];
new Random().NextBytes(bytes);
// Baytlarni ko'rsatish
Console.WriteLine(BitConverter.ToString(bytes));
// Baytlarni base-64 stringga dekodlash va stringni ko'rsatish
String s = Convert.ToBase64String(bytes);
Console.WriteLine(s);
// Base-64 stringni yana baytlarga kodlash va baytlarni ko'rsatish
bytes = Convert.FromBase64String(s);
Console.WriteLine(BitConverter.ToString(bytes));
}
}
Bu kodni kompilyatsiya qilib ishga tushirish quyidagi natijani beradi (sizning natijangiz tasodifiy yaratilgan baytlar sababli farq qilishi mumkin).
3B-B9-27-40-59-35-86-54-5F-F1
O7knQFk1hlRf8Q==
3B-B9-27-40-59-35-86-54-5F-F1
Xavfsiz Stringlar (Secure Strings)
String obyektlari ko'pincha foydalanuvchining paroli yoki kredit karta ma'lumotlari kabi nozik ma'lumotlarni o'z ichiga olish uchun ishlatiladi. Afsuski, String obyektlari xotirada belgilar massivini o'z ichiga oladi va agar biron bir xavfsiz bo'lmagan yoki boshqarilmaydigan kod jarayonning manzil makonida josuslik qilishi mumkin bo'lsa, nozik ma'lumotni o'z ichiga olgan stringni topishi va bu ma'lumotdan ruxsatsiz tarzda foydalanishi mumkin. String obyekti qisqa vaqt uchun ishlatilsa va keyin garbage collect qilinalsa ham, CLR String obyektining xotirasini darhol qayta ishlatmasligi mumkin (ayniqsa String obyekti eski avlodda bo'lsa), String belgilarini jarayon xotirasida qoldiradi.
Ba'zi davlat idoralari juda qat'iy xavfsizlik kafolatlarini talab qiladigan xavfsizlik talablariga ega. Ushbu talablarga javob berish uchun Microsoft FCL ga yanada xavfsizroq string klassini qo'shdi: System.Security.SecureString. SecureString obyektini yaratganingizda, u ichki tarzda belgilar massivini o'z ichiga olgan boshqarilmaydigan xotira blokini ajratadi. Boshqarilmaydigan xotira ishlatiladi, shuning uchun garbage collector undan xabardor emas.
Bu stringning belgilari shifrlangan, nozik ma'lumotni har qanday zararli xavfsiz bo'lmagan/boshqarilmaydigan koddan himoya qiladi. Siz xavfsiz stringdagi belgini qo'shish, kiritish, olib tashlash yoki o'rnatish uchun quyidagi metodlardan foydalanishingiz mumkin: AppendChar, InsertAt, RemoveAt va SetAt. Bu metodlarning har birini chaqirganingizda, ichki tarzda metod belgilarni shifrdan chiqaradi, operatsiyani joyida bajaradi va keyin belgilarni qayta shifrlaydi. Bu belgilar juda qisqa vaqt davomida shifrlanmagan holatda bo'lishini anglatadi.
SecureString klassi IDisposable interfeysini amalga oshiradi va string ma'lumotlarini deterministik tarzda yo'q qilish uchun qulay usulni taqdim etadi. Ilovangiz nozik string ma'lumotiga endi muhtoj bo'lmaganda, siz SecureString ning Dispose metodini chaqirasiz yoki SecureString instansiyasini using konstruktsiyasida ishlatishingiz mumkin. Ichki tarzda Dispose xotira buferining kontentini nolga o'rnatadi va keyin buferni ozod qiladi. String obyektidan farqli o'laroq, SecureString obyekti finalizatsiya qilinganida, shifrlangan stringning belgilari boshqa xotirada bo'lmaydi.
Quyida SecureString ni qanday initsializatsiya qilish va foydalanish ko'rsatilgan (kompilyatsiya qilishda C# kompilyatoriga /unsafe kalitini ko'rsatishingiz kerak bo'ladi).
using System;
using System.Security;
using System.Runtime.InteropServices;
public static class Program {
public static void Main() {
using (SecureString ss = new SecureString()) {
Console.Write("Iltimos parolni kiriting: ");
while (true) {
ConsoleKeyInfo cki = Console.ReadKey(true);
if (cki.Key == ConsoleKey.Enter) break;
// Parol belgilarini SecureString ga qo'shish
ss.AppendChar(cki.KeyChar);
Console.Write("*");
}
Console.WriteLine();
// Namoyish maqsadida parolni ko'rsatish
DisplaySecureString(ss);
}
// 'using' dan keyin SecureString Dispose qilingan;
// xotirada nozik ma'lumot yo'q
}
// Bu metod boshqarilmaydigan xotiraga kirganligi uchun unsafe
private unsafe static void DisplaySecureString(SecureString ss) {
Char* pc = null;
try {
// SecureString ni boshqarilmaydigan xotira buferiga shifrdan chiqarish
pc = (Char*) Marshal.SecureStringToCoTaskMemUnicode(ss);
// Shifrdan chiqarilgan SecureString ni
// o'z ichiga olgan boshqarilmaydigan xotira buferiga kirish
for (Int32 index = 0; pc[index] != 0; index++)
Console.Write(pc[index]);
}
finally {
// Shifrdan chiqarilgan SecureString belgilarini
// o'z ichiga olgan boshqarilmaydigan xotira buferini nolga o'rnatish va ozod qilish
if (pc != null)
Marshal.ZeroFreeCoTaskMemUnicode((IntPtr) pc);
}
}
}
System.Runtime.InteropServices.Marshal klassi SecureString ning belgilarini boshqarilmaydigan xotira buferiga shifrdan chiqarish va buferingizni nolga o'rnatish va ozod qilish uchun chaqirishingiz kerak bo'lgan mos metodlar bilan beshta metodni taklif qiladi.
| SecureString ni buferga shifrdan chiqarish metodi | Buferingizni nolga o'rnatish va ozod qilish metodi |
|---|---|
SecureStringToBSTR | ZeroFreeBSTR |
SecureStringToCoTaskMemAnsi | ZeroFreeCoTaskMemAnsi |
SecureStringToCoTaskMemUnicode | ZeroFreeCoTaskMemUnicode |
SecureStringToGlobalAllocAnsi | ZeroFreeGlobalAllocAnsi |
SecureStringToGlobalAllocUnicode | ZeroFreeGlobalAllocUnicode |
Hech qachon SecureString ning kontentini String ga joylashtirmang: agar shunday qilsangiz, String heapda shifrlanmagan holda yashaydi va garbage collection dan keyin xotira qayta foydalanilgunga qadar uning belgilari nolga o'rnatilmaydi. SecureString klassi ToString metodini ataylab qayta yozmaydi, bu orqali nozik ma'lumotni oshkor qilishdan saqlaydi (uni String ga aylantirish aynan shunday qilgan bo'lardi).
Ushbu bobda siz .NET Framework da belgilar va stringlar bilan ishlashning barcha jihatlarini o'rgandingiz: Char turi va uning metodlari, String turning o'zgarmasligi va solishtirish usullari, string interning va pooling mexanizmlari, StringBuilder bilan samarali string qurish, ToString va IFormattable orqali formatlash, Parse/TryParse orqali tahlil qilish, turli kodlashlar (UTF-8, UTF-16, ASCII) va nihoyat SecureString bilan nozik ma'lumotlarni himoya qilish. Bu bilimlar .NET da matn bilan ishlashning mustahkam poydevorini tashkil etadi.