CodeGym /Kurslar /C# SELF /Əsas kodlaşdırma növləri: ...

Əsas kodlaşdırma növləri: UTF-8, UTF-16, ASCII

C# SELF
Səviyyə, Dərs
Mövcuddur

1. Giriş

Artıq başa düşdük ki, kompüterlər nə qədər ağıllı olsalar da, özləri üçün "A" hərfi və ya "⌘" simvolu nə olduğunu anlamırlar. Onlar sıfır və birləri başa düşürlər, insan üçün məna daşıyan simvolları dekod etmək üçün isə onlara tərcüməçi — kodlaşdırma lazımdır.

Kodlaşdırmaların tarixi kompromislər və təkamül tarixidir. Əvvəl hər şey sadə idi, sonra çətinləşdi və nəhayət nisbətən universal standart meydana çıxdı. Gəlin bu xronologiyanı qısa şəkildə nəzərdən keçirək.

Tarixin başlanğıcı

Ən əvvəldən başlayaq. Öncəki mühazirədə mətni kodlayan əcdad — ASCII (tələffüz olunur "aski") haqqında danışmışdıq. Xatırladaq ki, bu ad American Standard Code for Information Interchange deməkdir, yəni məlumat mübadiləsi üçün Amerika standart kodu. Adından da göründüyü kimi, kim üçün hazırlanmışdı, aydındır.

ASCII 1960-cı illərdə hazırlanıb və simvolların kodlaşdırılmasının ilk geniş istifadə olunan standartı oldu. O, 128 simvoldan ibarət dəstdir:

  • Latın hərfləri (böyük və kiçik): A-Z, a-z
  • Rəqəmlər: 0-9
  • İntizamsız işarələr: .,!?"' və s.
  • Bəzi idarəedici simvollar: yeni sətir, tab və s.

Bu 128 simvolun hər biri bir baytla kodlanırdı və 8 baytın 7 biti istifadə olunurdu (ən yüksək bit adətən boş qalırdı və ya səhv yoxlaması üçün istifadə olunurdu). İngilis dili üçün bu çox kompakt və effektiv idi.


Nümunə:
'A' simvolu ASCII-də 0x41 baytı ilə kodlanır (ikilikdə 01000001)
'!' simvolu ASCII-də 0x21 baytı ilə kodlanır (ikilikdə 00100001)

Məhdudiyyətlər:
Əsas məhdudiyyət aydındır: ASCII ingilis dilinə uyğunlaşdırılıb. Əgər siz rusca ("Привет"), alman ("Grüße") və ya çin dilində yazmaq istəsəniz, ASCII sizə kömək etməyəcək. Onun cədvəlində bu simvollar yoxdur. Bu isə çoxsaylı kod səhifələrinin (Code Pages) yaranmasına səbəb oldu — onlar ASCII-nin 128 simvolunu 256-a qədər genişləndirməyə çalışırdılar, 8-ci biti istifadə edərək. Məsələn, rus dili üçün CP1251 (Windows Cyrillic), KOI8-R və s. var idi. Amma problem ondadır ki, bu kod səhifələri bir-biri ilə uyğun gəlmirdi: eyni bayt müxtəlif kod səhifələrində tamamilə fərqli simvol gətirə bilərdi! Həqiqi Babil qülləsi situasiyası.

Bu gün praktik istifadəsi:
Təmiz ASCII formatı bu gün ümumi mətn fayllarında nadir hallarda istifadə olunur, yalnız çox spesifik ehtiyaclar üçün və ya köhnə sistemlərdə. Bununla birlikdə, onun mirası yaşayır: bir çox müasir kodlaşdırmalar, görəcəyimiz kimi, ASCII-ə geriyə uyğun gəlir.

Gəlin ASCII-də nəsə yazıb oxumağa çalışaq, sonra isə rus hərfləri əlavə edib nə olacağını görək.

JetBrains Rider-da yeni konsol layihəsi yaradaq və adını məsələn FileEncodingExplorer qoyaq.

using System;
using System.IO;
using System.Text;

string file = "ascii.txt";
string asciiText = "Salam, dünya!";
string cyrillicText = "Salam, dünya!";

// ASCII-ə yazma
using var writer = new StreamWriter(file, false, Encoding.ASCII);
writer.WriteLine(asciiText);
writer.WriteLine(cyrillicText);

// ASCII-dən oxuma
using var reader = new StreamReader(file, Encoding.ASCII);
string content = reader.ReadToEnd();
Console.WriteLine("Faylın məzmunu:");
Console.WriteLine(content);

Console.WriteLine("\nKiril hərfləri '?' ilə əvəz olundu, çünki ASCII kiril əlifbasını dəstəkləmir!");

Bu kodu işə saldıqda görəcəksiniz ki, ingilis bölməsi normal oxunur, amma rus bölməsi sual işarələrinə (?) və ya digər "naməlum" simvollara çevrilir. Çünki Encoding.ASCII kiril simvollarını baytlara çevirməyi bilmir və onları sadəcə "təhlükəsiz" bir şeylə əvəz edir (adətən ?), və ya digər kodlaşdırmada olan baytlar ASCII tərəfindən başqa simvollar kimi şərh olunur. Bizim halda StreamWriter mövcud olmayan simvolları yazarkən ? ilə əvəz edir. Bu, düzgün kodlaşdırmanın nə qədər vacib olduğunu göstərir!

2. UTF-8: İnternetin və çevikliyin şahı

Və budur biz bu günün ən vacib və bəlkə də ən populyar kodlaşdırmasından — UTF-8-dən danışırıq. Bu kodlaşdırma internetin böyük hissəsində, Linux sistemlərində və müasir tətbiqlərin əksəriyyətində işləyir.

Bu nədir?
UTF-8 (Unicode Transformation Format - 8-bit) — Unicode-un başqa bir kodlaşdırmasıdır və UTF-16-nin ingilis mətnləri üçün qeyri-effektivliyi problemini həll edir. UTF-8 — dəyişkən uzunluqlu kodlaşdırmadır, amma çox ağıllı yanaşması var:

  • Adətən ASCII-simvollar (kodlar 0 ilə 127 arasında) bir bayt ilə kodlanır. Və ən gözəl tərəfi odur ki, bu baytlar ASCII-dəki təmsilləri ilə tam eynidir! Bu o deməkdir ki, UTF-8 ASCII-ə geriyə uyğun gəlir.
  • Qalan simvollar 2-dən 4-ə qədər baytlarla kodlanır:
    • Kiril — adətən 2 bayt.
    • Bir çox Avropa diakritik simvolları, ərəb, ivrit, yunan — 2 bayt.
    • Çin/Yapon/Koreya loqogramları — tez-tez 3 bayt.
    • Nadir simvollar və bəzi emoji-lər — 4 bayt.

UTF-8-də bayt nümayəndəliyi nümunələri:

  • 'A' simvolu (ASCII): 01000001 (1 bayt)
  • 'я' simvolu (rusca): 11010001 10111111 (2 bayt)
  • '€' simvolu (Euro): 11100010 10000010 10101100 (3 bayt)
  • '😂' (sevimli emoji): 11110000 10011111 10011000 10000010 (4 bayt)

Niyə UTF-8 şahdır?

  1. Səmərəlilik: ASCII-simvollar çox olduqda (ingilis dili, kod, konfiqurasiya faylları üçün tipik) çox kompaktdır.
  2. ASCII ilə geriyə uyğunluq: Əgər UTF-8 faylı yalnız ASCII simvolları ehtiva edirsə, onu ASCII kimi oxumaq olar və hər şey işləyər!
  3. BOM-un olmaması (adətən): UTF-16-dən fərqli olaraq, UTF-8 adətən BOM istifadə etmir. Əgər rast gəlinirsə (məsələn, EF BB BF), bu könüllü "xüsusiyyətdir" və bəzən problemlər yarada bilər (məsələn, bəzi formatların parsing-i və ya Linux skriptləri ilə).

Mənfi cəhətlər:

  • Dəyişkən uzunluq bəzi əməliyyatları çətinləşdirə bilər (məsələn, N-ci simvola keçmək üçün tam skan lazım gəlir), amma C#-da bu problem yoxdur: string faylın kodlaşdırmasından asılı olmayaraq Unicode simvolları ilə işləyir.

Praktiki istifadə:

  • Veb-səhifələr (HTML, CSS, JavaScript),
  • API-lər (JSON, XML),
  • Konfiqurasiya faylları,
  • İnkişaf dillərinin mənbə kodları,
  • Linux/Unix əməliyyat sistemləri.

Gəlin UTF-8 faylı yazıb oxuyaq.

string file = "utf8.txt";
string text = "Salam, мир! 😀 €";

// UTF-8-ə yazma (default olaraq BOM-suz)
File.WriteAllText(file, text, Encoding.UTF8);

// UTF-8-dən oxuma
string readText = File.ReadAllText(file, Encoding.UTF8);
Console.WriteLine(readText); // Hər şey düzgün oxunur!

Bu kodu işə saldıqda və fayl ölçülərini müqayisə etdikdə görərsiniz ki, qarışıq mətn üçün utf8.txt adətən UTF-16 fayldan daha kiçik olur, əgər mətn yalnız ingiliscədirsə isə ölçüsü ASCII-yə yaxın olar.

3. UTF-16: Demək olar ki, bütün dünya üçün Unicode

Kod səhifələrinin "Babil qülləsi" problemi proqramçıların baş ağrısına çevrildi, xüsusilə proqramlar qlobal olduqda. Universal həll lazımdı. Və o peyda oldu — Unicode. Unicode özü kodlaşdırma deyil, hər bir məlum simvola unikal ədədi kod (code point) təyin edən böyük bir cədvəldir.

Bu nədir?
UTF-16 (Unicode Transformation Format - 16-bit) — əvvəllər bütün Unicode simvollarının iki bayt (16 bit) ilə kodlanacağı nəzərdə tutulan kodlaşdırmadır.

  • Çox simvollar (BMP, 65535-ə qədər) 2 baytla kodlanır.
  • BMP-dən kənar simvollar üçün surroqat cütlüklər istifadə olunur — toplam 4 bayt. Yəni UTF-16 də dəyişkən uzunluqludur, amma çox vaxt 1 simvol üçün 2 bayt kimi qəbul edilir.

Bayt sırası (Endianness) və BOM:

  • Big-Endian (BE): yuxarı bayt əvvəl gəlir.
  • Little-Endian (LE): aşağı bayt əvvəl gəlir.
  • Oxuyan proqramın sıralamanı başa düşməsi üçün faylın əvvəlinə tez-tez BOM (Byte Order Mark) qoyulur:
    • UTF-16 LE üçün: FF FE
    • UTF-16 BE üçün: FE FF
    C# və Windows-da default olaraq UTF-16 LE istifadə olunur.

Üstünlüklər:

  • Dünyadakı simvolların böyük hissəsini dəstəkləyir.
  • BMP daxilindəki simvollarla işləmək sadədir (sabit 2 bayt uzunluğu).

Mənfi cəhətlər:

  • İngilis mətnləri üçün səmərəli deyil: hər ASCII simvolu 2 bayt tutur.
  • BOM-un mövcudluğu bəzi gözlənilməz oxuyucularda problemlər yarada bilər.

Praktiki istifadə:
UTF-16 Windows daxilində və məsələn Java-da daxili string təmsili üçün geniş istifadə olunur. Windows Notepad ilə saxlanılan kiril mətnli fayllar tez-tez UTF-16 LE və BOM ilə saxlanılır.

string file = "utf16.txt";
string text = "Salam, мир! 👋";

// UTF-16-ya yazma (default Little-Endian, BOM ilə)
File.WriteAllText(file, text, Encoding.Unicode);

// UTF-16-dan oxuma
string readText = File.ReadAllText(file, Encoding.Unicode);
Console.WriteLine(readText); // Hər şey düzgün oxunur!

Console.WriteLine($"Faylın ölçüsü: {new FileInfo(file).Length} bayt");

İşlətdikdən sonra görəcəksiniz ki, bütün simvollar düzgün göstərilir. İngilis mətni olan fayllar UTF-16-də təxminən iki dəfə çox yer tutur, ASCII və ya UTF-8-ə nisbətən (ASCII-range).

4. Kodlaşdırmaların müqayisə cədvəli

Bilikləri sistemləşdirmək üçün əsas xüsusiyyətləri bir cədvəldə toplayaq.

Kodlaşdırma Simvol üçün minimal bayt sayı Simvol üçün maksimal bayt sayı ASCII ilə uyğunluq (birbaşa) .NET-də default olaraq BOM istifadə edirmi Tətbiq nümunələri
ASCII 1 1 Tam Xeyr Köhnə sistemlər, çox sadə mətn məlumatları, daxili protokollar
UTF-16 2 4 Xeyr Bəli (Encoding.Unicode) Windows daxilində string təmsili, Java; Windows mətn faylları
UTF-8 1 4 Tam Xeyr (Encoding.UTF8 .NET 5+); Bəli (Encoding.UTF8 .NET Framework) Web (HTML, JSON), konfiqurasiya faylları, mənbə kodu, Linux/Unix

Qısa qeydlər Encoding.UTF8 barədə .NET-də:
Tarixən .NET Framework-də Encoding.UTF8 default olaraq BOM əlavə edirdi. Müasir .NET (Core/5+) versiyalarında davranış dəyişib: default olaraq BOM əlavə olunmur. Əgər BOM lazımdırsa, istifadə edin: new UTF8Encoding(true).

5. C#-da kodlaşdırmanı necə göstərmək

Misallarda da gördüyünüz kimi, StreamReader və StreamWriter-ə hansı "sözlüyü" istifadə edəcəyini demək üçün onlara System.Text.Encoding-dən bir obyekt veririk.

System.Text.Encoding hazır seçimlər təqdim edir:

  • Encoding.ASCII: ASCII ilə işləmək üçün.
  • Encoding.Unicode: UTF-16 LE (BOM ilə).
  • Encoding.UTF8: UTF-8 (müasir .NET-də default olaraq BOM-suz).

Başqa kodlaşdırmalar Encoding.GetEncoding vasitəsilə əlçatandır (məsələn, "windows-1251", "koi8-r"), amma fokus indi — Unicode-dadır.

// UTF-8-ə yazma
using var writer = new StreamWriter("my_file.txt", false, Encoding.UTF8);
writer.WriteLine("Nəsə mətn.");

// UTF-16-dan oxuma
using var reader = new StreamReader("another_file.txt", Encoding.Unicode);
string content = reader.ReadToEnd();
Console.WriteLine(content);

Budur bütün sirr! StreamReader və StreamWriter seçilmiş kodlaşdırma qaydalarından istifadə edərək simvolları baytlara və əksinə çevirir.

6. Kodlaşdırma problemləri: "təhrif olunmuş simvollar"

ASCII-yə rus mətni yazanda gördüyümüz "təhrif olunmuş simvollar" artıq məlum oldu. Amma faylı bir kodlaşdırmada yazmısınızsa və başqa kodlaşdırmada oxumağa çalışırsınızsa, əsl əyləncə başlayır!

Təsəvvür edin: məktub rus dilində yazılıb və UTF-8-də saxlanılıb, amma müştəri onu CP1251 kimi oxumağa çalışır. Bayt ardıcıllıqları səhv şərh olunacaq və "Привет, мир!" yerinə siz "təhrif olunmuş simvollar" (ing. mojibake) görəcəksiniz.

Səbəb sadədir: yazma və oxuma mərhələlərində kodlaşdırmaların uyğunsuzluğu. Həmişə yazma və oxuma üçün eyni kodlaşdırmanı istifadə edin, yoxsa siz xüsusi şəkildə recoding etmirsinizsə, problem yaranacaq.

string file = "mismatch.txt";
string russianText = "Salam, dünya!";

// UTF-8-də yazma (düzgün!)
File.WriteAllText(file, russianText, Encoding.UTF8);

// Səhv oxuma: UTF-8 faylını ASCII kimi oxumağa çalışırıq
string readAsAscii = File.ReadAllText(file, Encoding.ASCII);

Console.WriteLine($"Original: {russianText}");
Console.WriteLine($"ASCII kimi oxunuş: {readAsAscii}"); // Burada "təhrif olunmuş simvollar" yaranacaq!

Proqramı işə salın və görəcəksiniz ki, kiril hərfləri sual işarələrinə və ya məntiqsiz simvollara çevrilir. Növbəti dərsdə kodlaşdırmalarla daha çevik işləməyi, belə problemlərdən necə qaçmağı və oxuduğunuz faylın kodlaşdırmasını necə müəyyən etməyi danışacağıq.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Fayl ölçülərinin müxtəlif kodlaşdırmalar üçün müqayisəsi
Fayl ölçülərinin müxtəlif kodlaşdırmalar üçün müqayisəsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION