Bagikan melalui


Pengodean karakter in .NET

Artikel ini menyediakan pengantar sistem pengodean karakter yang digunakan oleh .NET. Artikel ini menjelaskan cara kerja jenis String, Char, Rune, dan StringInfo dengan Unicode, UTF-16, dan UTF-8.

Istilah karakter digunakan di sini dalam arti umum apa yang dirasakan pembaca sebagai elemen tampilan tunggal. Contoh umumnya adalah huruf "a", simbol "@", dan emoji "🐂". Terkadang apa yang terlihat seperti satu karakter sebenarnya terdiri dari beberapa elemen tampilan independen, seperti yang dijelaskan bagian pada kluster grapheme.

Jenis string dan char

Instans kelas string mewakili beberapa teks. string secara logis merupakan urutan nilai 16-bit, yang masing-masing merupakan instans char struktur. Properti string.Length mengembalikan jumlah instans char dalam instans string.

Contoh fungsi berikut mencetak nilai dalam notasi heksadesimal dari semua instans char dalam string:

void PrintChars(string s)
{
    Console.WriteLine($"\"{s}\".Length = {s.Length}");
    for (int i = 0; i < s.Length; i++)
    {
        Console.WriteLine($"s[{i}] = '{s[i]}' ('\\u{(int)s[i]:x4}')");
    }
    Console.WriteLine();
}

Teruskan string "Halo" ke fungsi ini, dan Anda mendapatkan output berikut:

PrintChars("Hello");
"Hello".Length = 5
s[0] = 'H' ('\u0048')
s[1] = 'e' ('\u0065')
s[2] = 'l' ('\u006c')
s[3] = 'l' ('\u006c')
s[4] = 'o' ('\u006f')

Setiap karakter diwakili oleh satu char nilai. Pola itu berlaku untuk sebagian besar bahasa dunia. Misalnya, berikut adalah output untuk dua karakter Cina yang terdengar seperti nǐ hǎo dan rata-rata Halo:

PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')

Namun, untuk beberapa bahasa dan untuk beberapa simbol dan emoji, dibutuhkan dua char instans untuk mewakili satu karakter. Misalnya, bandingkan karakter dan char instans dalam kata yang berarti Osage dalam bahasa Osage:

PrintChars("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟");
"𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟".Length = 17
s[0] = '�' ('\ud801')
s[1] = '�' ('\udccf')
s[2] = '�' ('\ud801')
s[3] = '�' ('\udcd8')
s[4] = '�' ('\ud801')
s[5] = '�' ('\udcfb')
s[6] = '�' ('\ud801')
s[7] = '�' ('\udcd8')
s[8] = '�' ('\ud801')
s[9] = '�' ('\udcfb')
s[10] = '�' ('\ud801')
s[11] = '�' ('\udcdf')
s[12] = ' ' ('\u0020')
s[13] = '�' ('\ud801')
s[14] = '�' ('\udcbb')
s[15] = '�' ('\ud801')
s[16] = '�' ('\udcdf')

Dalam contoh sebelumnya, setiap karakter kecuali spasi diwakili oleh dua char instans.

Emoji Unicode tunggal juga diwakili oleh dua chars, seperti yang terlihat dalam contoh berikut yang menunjukkan emoji lembu:

"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')

Contoh-contoh ini menunjukkan bahwa nilai string.Length, yang menunjukkan jumlah char instans, tidak selalu menunjukkan jumlah karakter yang ditampilkan. Satu char instans dengan sendirinya tidak selalu mewakili karakter.

char Pasangan yang memetakan ke satu karakter disebut pasangan pengganti. Untuk memahami cara kerjanya, Anda perlu memahami pengodean Unicode dan UTF-16.

Titik kode Unicode

Unicode adalah standar pengodean internasional untuk digunakan pada berbagai platform dan dengan berbagai bahasa dan skrip.

Standar Unicode mendefinisikan lebih dari 1,1 juta titik kode. Titik kode adalah nilai bilangan bulat yang dapat berkisar dari 0 hingga U+10FFFF (desimal 1.114.111). Beberapa titik kode ditetapkan ke huruf, simbol, atau emoji. Yang lain ditetapkan ke tindakan yang mengontrol bagaimana teks atau karakter ditampilkan, seperti lanjutkan ke baris baru. Banyak titik kode yang belum ditetapkan.

Berikut adalah beberapa contoh penetapan titik kode, dengan tautan ke bagan Unicode di mana mereka muncul:

Desimal Hex Contoh Deskripsi
10 U+000A T/A UMPAN BARIS
97 U+0061 sebuah HURUF LATIN KECIL A
562 U+0232 Ȳ HURUF LATIN KAPITAL Y DENGAN MACRON
68.675 U+10C43 𐱃 SURAT TURKI LAMA ORKHON DI
127.801 U+1F339 🌹 Emoji MAWAR

Titik kode biasanya disebut dengan menggunakan sintaks U+xxxx, di mana xxxx adalah nilai bilangan bulat yang dikodekan hex.

Dalam rentang lengkap titik kode ada dua sub-rentang:

  • Bidang Multibahasa Dasar (BMP) dalam rentang U+0000..U+FFFF. Rentang 16-bit ini menyediakan 65.536 poin kode, cukup untuk mencakup sebagian besar sistem penulisan dunia.
  • Titik kode tambahan dalam rentang U+10000..U+10FFFF. Rentang 21-bit ini menyediakan lebih dari satu juta titik kode tambahan yang dapat digunakan untuk bahasa yang kurang terkenal dan tujuan lain seperti emoji.

Diagram berikut mengilustrasikan hubungan antara BMP dan titik kode tambahan.

BMP dan titik kode tambahan

Unit kode UTF-16

Format Transformasi Unicode 16-bit (UTF-16) adalah sistem pengodean karakter yang menggunakan unit kode 16-bit untuk mewakili titik kode Unicode. .NET menggunakan UTF-16 untuk mengodekan teks dalam string. Instans char mewakili unit kode 16-bit.

Satu unit kode 16-bit dapat mewakili titik kode apa pun dalam rentang 16-bit dari Bidang Multibahasa Dasar. Tetapi untuk titik kode dalam rentang tambahan, dua instans char diperlukan.

Pasangan pengganti

Terjemahan dua nilai 16-bit ke nilai 21-bit tunggal difasilitasi oleh rentang khusus yang disebut titik kode pengganti, dari U+D800 hingga U+DFFF (desimal 55.296 hingga 57.343), inklusif.

Diagram berikut mengilustrasikan hubungan antara BMP dan titik kode tambahan.

Titik kode BMP dan pengganti

Ketika titik kode pengganti tinggi (U+D800..U+DBFF) segera diikuti oleh titik kode pengganti rendah (U+DC00..U+DFFF), pasangan ditafsirkan sebagai titik kode tambahan dengan menggunakan rumus berikut:

code point = 0x10000 +
  ((high surrogate code point - 0xD800) * 0x0400) +
  (low surrogate code point - 0xDC00)

Berikut rumus yang sama menggunakan notasi desimal:

code point = 65,536 +
  ((high surrogate code point - 55,296) * 1,024) +
  (low surrogate code point - 56,320)

Titik kode pengganti yang tinggi tidak memiliki nilai angka yang lebih tinggi daripada titik kode pengganti rendah. Titik kode pengganti tinggi disebut "tinggi" karena digunakan untuk menghitung 10 bit dengan urutan lebih tinggi dari rentang titik kode 20-bit. Titik kode pengganti rendah digunakan untuk menghitung urutan lebih rendah 10 bit.

Misalnya, titik kode aktual yang sesuai dengan pasangan pengganti 0xD83C dan 0xDF39 dihitung sebagai berikut:

actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
       = 0x10000 + (          0x003C  * 0x0400) +           0x0339
       = 0x10000 +                      0xF000  +           0x0339
       = 0x1F339

Berikut perhitungan yang sama menggunakan notasi desimal:

actual =  65,536 + ((55,356 - 55,296) * 1,024) + (57,145 - 56320)
       =  65,536 + (              60  * 1,024) +             825
       =  65,536 +                     61,440  +             825
       = 127,801

Contoh sebelumnya menunjukkan bahwa "\ud83c\udf39" adalah pengodean UTF-16 dari titik kode yang U+1F339 ROSE ('🌹') disebutkan sebelumnya.

Nilai skalar Unicode

Istilah nilai skalar Unicode mengacu pada semua poin kode selain titik kode pengganti. Dengan kata lain, nilai skalar adalah titik kode apa pun yang ditetapkan karakter atau dapat ditetapkan karakter di masa mendatang. "Karakter" di sini mengacu pada apa pun yang dapat ditetapkan ke titik kode, yang mencakup hal-hal seperti tindakan yang mengontrol bagaimana teks atau karakter ditampilkan.

Diagram berikut mengilustrasikan poin kode nilai skalar.

Nilai skalar

Jenis Rune sebagai nilai skalar

Penting

Rune Jenis tidak tersedia di .NET Framework.

Di .NET, System.Text.Rune jenis mewakili nilai skalar Unicode.

Konstruktor Rune memvalidasi bahwa instans yang dihasilkan adalah nilai skalar Unicode yang valid, jika tidak, mereka melemparkan pengecualian. Contoh berikut menunjukkan kode yang berhasil membuat instans Rune karena input mewakili nilai skalar yang valid:

Rune a = new Rune('a');
Rune b = new Rune(0x0061);
Rune c = new Rune('\u0061');
Rune d = new Rune(0x10421);
Rune e = new Rune('\ud801', '\udc21');

Contoh berikut memberikan pengecualian karena titik kode berada dalam rentang pengganti dan bukan bagian dari pasangan pengganti:

Rune f = new Rune('\ud801');

Contoh berikut memberikan pengecualian karena titik kode berada di luar rentang tambahan:

Rune g = new Rune(0x12345678);

Rune contoh penggunaan: mengubah huruf besar/kecil

API yang mengambil char dan mengasumsikan bahwa API bekerja dengan titik kode yang merupakan nilai skalar tidak berfungsi dengan benar jika char berasal dari pasangan pengganti. Misalnya, pertimbangkan metode berikut yang memanggil Char.ToUpperInvariant masing-masing char dalam string:

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string ConvertToUpperBadExample(string input)
{
    StringBuilder builder = new StringBuilder(input.Length);
    for (int i = 0; i < input.Length; i++) /* or 'foreach' */
    {
        builder.Append(char.ToUpperInvariant(input[i]));
    }
    return builder.ToString();
}

Jika inputstring berisi huruf Deseret huruf kecil er (𐑉), kode ini tidak akan mengonversinya menjadi huruf besar (𐐡). Kode memanggil char.ToUpperInvariant secara terpisah pada setiap titik kode pengganti, U+D801 dan U+DC49. Tetapi U+D801 tidak memiliki informasi yang cukup dengan sendirinya untuk mengidentifikasinya sebagai huruf kecil, jadi char.ToUpperInvariant biarkan saja. Dan itu menangani U+DC49 dengan cara yang sama. Hasilnya adalah bahwa huruf kecil '𐑉' di inputstring tidak dikonversi menjadi huruf besar '𐑉'.

Berikut adalah dua opsi untuk mengonversi menjadi string huruf besar dengan benar:

  • Panggil String.ToUpperInvariant pada input string daripada iterasi char-by-char. Metode ini string.ToUpperInvariant memiliki akses ke kedua bagian dari setiap pasangan pengganti, sehingga dapat menangani semua titik kode Unicode dengan benar.

  • Ulangi melalui nilai skalar Unicode sebagai instans Rune alih-alih instans char, seperti yang ditunjukkan dalam contoh berikut. Karena instans Rune adalah nilai skalar Unicode yang valid, instans dapat diteruskan ke API yang mengharapkan untuk beroperasi pada nilai skalar. Misalnya, memanggil Rune.ToUpperInvariant seperti yang ditunjukkan dalam contoh berikut memberikan hasil yang benar:

    static string ConvertToUpper(string input)
    {
        StringBuilder builder = new StringBuilder(input.Length);
        foreach (Rune rune in input.EnumerateRunes())
        {
            builder.Append(Rune.ToUpperInvariant(rune));
        }
        return builder.ToString();
    }
    

API Rune lainnya

Jenis Rune memaparkan analog dari banyak API char. Misalnya, metode berikut mencerminkan API statis pada jenis char:

Untuk mendapatkan nilai skalar mentah dari instans Rune, gunakan properti Rune.Value.

Untuk mengonversi instans Rune kembali ke urutan char, gunakan metode Rune.ToString atau Rune.EncodeToUtf16.

Karena nilai skalar Unicode apa pun dapat diwakili oleh satu char atau oleh pasangan pengganti, setiap instans Rune dapat diwakili oleh paling banyak 2 instans char. Gunakan Rune.Utf16SequenceLength untuk melihat berapa banyak instans char yang diperlukan untuk mewakili instans Rune.

Untuk informasi selengkapnya tentang jenis .NET Rune , lihat Rune referensi API.

Kluster Grapheme

Apa yang terlihat seperti satu karakter mungkin dihasilkan dari kombinasi beberapa titik kode, sehingga istilah yang lebih deskriptif yang sering digunakan sebagai pengganti "karakter" adalah kluster grapheme. Istilah yang setara dalam .NET adalah elemen teks.

Pertimbangkan instans string "a", "á", "á", dan "👩🏽‍🚒". Jika sistem operasi Anda menanganinya seperti yang ditentukan oleh standar Unicode, masing-masing instans string ini muncul sebagai elemen teks tunggal atau kluster grafik. Tetapi dua terakhir diwakili oleh lebih dari satu titik kode nilai skalar.

  • string "a" diwakili oleh satu nilai skalar dan berisi satu instans char.

    • U+0061 LATIN SMALL LETTER A
  • string "á" diwakili oleh satu nilai skalar dan berisi satu instans char.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • string "á" terlihat sama dengan "á" tetapi diwakili oleh dua nilai skalar dan berisi dua instans char.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • Akhirnya, string "👩🏽‍🚒" diwakili oleh empat nilai skalar dan berisi tujuh instans char.

    • U+1F469 WOMAN (rentang tambahan, memerlukan pasangan pengganti)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (rentang tambahan, memerlukan pasangan pengganti)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (rentang tambahan, memerlukan pasangan pengganti)

Dalam beberapa contoh sebelumnya - seperti pengubah aksen gabungan atau pengubah warna kulit - titik kode tidak ditampilkan sebagai elemen mandiri di layar. Sebaliknya, ini berfungsi untuk memodifikasi tampilan elemen teks yang datang sebelum itu. Contoh-contoh ini menunjukkan bahwa mungkin perlu beberapa nilai skalar untuk membentuk apa yang kita anggap sebagai satu "karakter," atau "kluster grapheme."

Untuk menghitung kluster grapheme dari string, gunakan kelas StringInfo seperti yang ditunjukkan dalam contoh berikut. Jika Anda terbiasa dengan Swift, jenis StringInfo .NET secara konseptual mirip dengan jenis character Swift.

Contoh: menghitung char, Rune, dan instans elemen teks

Di API .NET, kluster grapheme disebut elemen teks. Metode berikut menunjukkan perbedaan antara char, Rune, dan instans elemen teks dalam string:

static void PrintTextElementCount(string s)
{
    Console.WriteLine(s);
    Console.WriteLine($"Number of chars: {s.Length}");
    Console.WriteLine($"Number of runes: {s.EnumerateRunes().Count()}");

    TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(s);

    int textElementCount = 0;
    while (enumerator.MoveNext())
    {
        textElementCount++;
    }

    Console.WriteLine($"Number of text elements: {textElementCount}");
}
PrintTextElementCount("a");
// Number of chars: 1
// Number of runes: 1
// Number of text elements: 1

PrintTextElementCount("á");
// Number of chars: 2
// Number of runes: 2
// Number of text elements: 1

PrintTextElementCount("👩🏽‍🚒");
// Number of chars: 7
// Number of runes: 4
// Number of text elements: 1

Contoh: memisahkan instans string

Saat memisahkan instans string, hindari memisahkan pasangan pengganti dan kluster grafik. Pertimbangkan contoh kode yang salah berikut, yang ingin menyisipkan pemisah baris setiap 10 karakter dalam :string

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string InsertNewlinesEveryTencharsBadExample(string input)
{
    StringBuilder builder = new StringBuilder();

    // First, append chunks in multiples of 10 chars
    // followed by a newline.
    int i = 0;
    for (; i < input.Length - 10; i += 10)
    {
        builder.Append(input, i, 10);
        builder.AppendLine(); // newline
    }

    // Then append any leftover data followed by
    // a final newline.
    builder.Append(input, i, input.Length - i);
    builder.AppendLine(); // newline

    return builder.ToString();
}

Karena kode ini menghitung instans char, pasangan pengganti yang kebetulan mengalihkan batas 10-char akan dibagi dan garis baru disuntikkan di antara mereka. Penyisipan ini memperkenalkan kerusakan data, karena titik kode pengganti hanya bermakna sebagai pasangan.

Potensi kerusakan data tidak dihilangkan jika Anda menghitung instans Rune (nilai skalar) alih-alih instans char. Sekumpulan instans Rune mungkin membentuk kluster grapheme yang mengaitkan batas 10-char. Jika kumpulan kluster grapheme dipisahkan, itu tidak dapat ditafsirkan dengan benar.

Pendekatan yang lebih baik adalah memutus string dengan menghitung kluster grapheme, atau elemen teks, seperti dalam contoh berikut:

static string InsertNewlinesEveryTenTextElements(string input)
{
    StringBuilder builder = new StringBuilder();

    // Append chunks in multiples of 10 chars

    TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(input);

    int textElementCount = 1;
    while (enumerator.MoveNext())
    {
        builder.Append(enumerator.Current);
        if (textElementCount % 10 == 0)
        {
            builder.AppendLine(); // newline
        }
        textElementCount++;
    }

    // Add a final newline.
    builder.AppendLine(); // newline
    return builder.ToString();

}

Seperti disebutkan sebelumnya, sebelum .NET 5, StringInfo kelas memiliki bug yang menyebabkan beberapa kluster grapheme salah ditangani.

UTF-8 dan UTF-32

Bagian sebelumnya berfokus pada UTF-16 karena itulah yang digunakan .NET untuk mengodekan instans string. Ada sistem pengodean lain untuk Unicode - UTF-8 dan UTF-32. Pengodean ini masing-masing menggunakan unit kode 8-bit dan unit kode 32-bit.

Seperti UTF-16, UTF-8 memerlukan beberapa unit kode untuk mewakili beberapa nilai skalar Unicode. UTF-32 dapat mewakili nilai skalar apa pun dalam satu unit kode 32-bit.

Berikut adalah beberapa contoh yang menunjukkan bagaimana titik kode Unicode yang sama diwakili dalam masing-masing dari ketiga sistem pengodean Unicode ini:

Scalar: U+0061 LATIN SMALL LETTER A ('a')
UTF-8 : [ 61 ]           (1x  8-bit code unit  = 8 bits total)
UTF-16: [ 0061 ]         (1x 16-bit code unit  = 16 bits total)
UTF-32: [ 00000061 ]     (1x 32-bit code unit  = 32 bits total)

Scalar: U+0429 CYRILLIC CAPITAL LETTER SHCHA ('Щ')
UTF-8 : [ D0 A9 ]        (2x  8-bit code units = 16 bits total)
UTF-16: [ 0429 ]         (1x 16-bit code unit  = 16 bits total)
UTF-32: [ 00000429 ]     (1x 32-bit code unit  = 32 bits total)

Scalar: U+A992 JAVANESE LETTER GA ('ꦒ')
UTF-8 : [ EA A6 92 ]     (3x  8-bit code units = 24 bits total)
UTF-16: [ A992 ]         (1x 16-bit code unit  = 16 bits total)
UTF-32: [ 0000A992 ]     (1x 32-bit code unit  = 32 bits total)

Scalar: U+104CC OSAGE CAPITAL LETTER TSHA ('𐓌')
UTF-8 : [ F0 90 93 8C ]  (4x  8-bit code units = 32 bits total)
UTF-16: [ D801 DCCC ]    (2x 16-bit code units = 32 bits total)
UTF-32: [ 000104CC ]     (1x 32-bit code unit  = 32 bits total)

Seperti disebutkan sebelumnya, satu unit kode UTF-16 dari pasangan pengganti tidak berarti dengan sendirinya. Dengan cara yang sama, satu unit kode UTF-8 tidak berarti dengan sendirinya jika berada dalam urutan dua, tiga, atau empat yang digunakan untuk menghitung nilai skalar.

Catatan

Dimulai dengan C# 11, Anda dapat mewakili literal UTF-8 string menggunakan akhiran "u8" pada literal string. Untuk informasi selengkapnya tentang literal UTF-8 string , lihat bagian "string literal" dari artikel tentang jenis referensi bawaan di Panduan C#.

Endianness (urutan byte)

Dalam .NET, unit string kode UTF-16 disimpan dalam memori yang berdekatan sebagai urutan bilangan bulat 16-bit (instans char). Bit unit kode individu ditata sesuai dengan endianness arsitektur saat ini.

Pada arsitektur little-endian, string yang terdiri dari titik [ D801 DCCC ] kode UTF-16 akan ditata dalam memori sebagai byte [ 0x01, 0xD8, 0xCC, 0xDC ]. Pada arsitektur big-endian yang sama string akan ditata dalam memori sebagai byte [ 0xD8, 0x01, 0xDC, 0xCC ].

Sistem komputer yang berkomunikasi satu sama lain harus menyetujui representasi data yang melintasi kawat. Sebagian besar protokol jaringan menggunakan UTF-8 sebagai standar saat mengirimkan teks, sebagian untuk menghindari masalah yang mungkin diakibatkan oleh komputer big-endian yang berkomunikasi dengan mesin little-endian. string Yang terdiri dari titik kode UTF-8 [ F0 90 93 8C ] akan selalu direpresentasikan sebagai byte [ 0xF0, 0x90, 0x93, 0x8C ] terlepas dari endianness.

Untuk menggunakan UTF-8 untuk mengirimkan teks, aplikasi .NET sering menggunakan kode seperti contoh berikut:

string stringToWrite = GetString();
byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0, stringAsUtf8Bytes.Length);

Dalam contoh sebelumnya, metode Encoding.UTF8.GetBytes mendekode UTF-16 string kembali ke dalam serangkaian nilai skalar Unicode, lalu mengodekan kembali nilai skalar tersebut ke dalam UTF-8 dan menempatkan urutan yang dihasilkan ke dalam byte array. Metode Encoding.UTF8.GetString melakukan transformasi yang berlawanan, mengonversi array UTF-8 byte menjadi UTF-16 string.

Peringatan

Karena UTF-8 adalah hal yang biasa di internet, mungkin menggoda untuk membaca byte mentah dari kawat dan untuk memperlakukan data seolah-olah itu UTF-8. Namun, Anda harus memvalidasi bahwa itu memang terbentuk dengan baik. Klien berbahaya mungkin mengirimkan UTF-8 yang tidak terbentuk ke layanan Anda. Jika Anda mengoperasikan data tersebut seolah-olah terbentuk dengan baik, itu dapat menyebabkan kesalahan atau lubang keamanan di aplikasi Anda. Untuk memvalidasi data UTF-8, Anda dapat menggunakan metode seperti Encoding.UTF8.GetString, yang akan melakukan validasi saat mengonversi data masuk menjadi string.

Pengodean yang terbentuk dengan baik

Pengodean Unicode yang terbentuk dengan baik adalah string unit kode yang dapat didekodekan secara tidak ambigu dan tanpa kesalahan ke dalam urutan nilai skalar Unicode. Data yang terbentuk dengan baik dapat ditranskode secara bebas bolak-balik antara UTF-8, UTF-16, dan UTF-32.

Pertanyaan tentang apakah urutan pengodean terbentuk dengan baik atau tidak, tidak terkait dengan endianness arsitektur mesin. Urutan UTF-8 yang tidak terbentuk sakit dibentuk dengan cara yang sama pada mesin big-endian dan little-endian.

Berikut adalah beberapa contoh pengodean yang tidak terbentuk:

  • Dalam UTF-8, urutannya [ 6C C2 61 ] tidak terbentuk karena C2 tidak dapat diikuti oleh 61.

  • Dalam UTF-16, urutan [ DC00 DD00 ] (atau, dalam C#,string"\udc00\udd00" ) tidak terbentuk karena pengganti rendah DC00 tidak dapat diikuti oleh pengganti rendah lainnya DD00.

  • Dalam UTF-32, urutannya [ 0011ABCD ] tidak terbentuk karena 0011ABCD berada di luar rentang nilai skalar Unicode.

Dalam .NET, instans string hampir selalu berisi data UTF-16 yang terbentuk dengan baik, tetapi itu tidak dijamin. Contoh berikut menunjukkan kode C# yang valid yang membuat data UTF-16 yang tidak terbentuk dalam instans string.

  • Harfiah yang tidak terbentuk:

    const string s = "\ud800";
    
  • Substring yang memisahkan pasangan pengganti:

    string x = "\ud83e\udd70"; // "🥰"
    string y = x.Substring(1, 1); // "\udd70" standalone low surrogate
    

API seperti Encoding.UTF8.GetString tidak pernah mengembalikan instans string yang tidak terbentuk. Encoding.GetString dan Encoding.GetBytes metode mendeteksi urutan yang tidak terbentuk dalam input dan melakukan penggantian karakter saat menghasilkan output. Misalnya, jika Encoding.ASCII.GetString(byte[]) melihat byte non-ASCII dalam input (di luar rentang U+0000..U+007F), itu menyisipkan '?' ke dalam instans string yang dikembalikan. Encoding.UTF8.GetString(byte[]) menggantikan urutan UTF-8 yang tidak terbentuk dengan U+FFFD REPLACEMENT CHARACTER ('�') dalam instans string yang dikembalikan. Untuk informasi selengkapnya, lihat Standar Unicode, Bagian 5.22 dan 3.9.

Kelas bawaan juga dapat dikonfigurasi Encoding untuk melemparkan pengecualian daripada melakukan penggantian karakter ketika urutan yang terbentuk sakit terlihat. Pendekatan ini sering digunakan dalam aplikasi sensitif keamanan di mana penggantian karakter mungkin tidak dapat diterima.

byte[] utf8Bytes = ReadFromNetwork();
UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
string asString = encoding.GetString(utf8Bytes); // will throw if 'utf8Bytes' is ill-formed

Untuk informasi tentang cara menggunakan kelas bawaan Encoding , lihat Cara menggunakan kelas pengodean karakter di .NET.

Lihat juga