Kodowanie znaków na platformie .NETCharacter encoding in .NET

Ten artykuł zawiera wprowadzenie do char systemów kodowania acter, które są używane przez platformę .NET.This article provides an introduction to character encoding systems that are used by .NET. W tym artykule wyjaśniono String , jak,, Char Rune i StringInfo typy działają z Unicode, UTF-16 i UTF-8.The article explains how the String, Char, Rune, and StringInfo types work with Unicode, UTF-16, and UTF-8.

Termin char acter jest używany w tym miejscu w ogólnym sensie, co czytnik jest postrzegany jako pojedynczy element wyświetlania.The term character is used here in the general sense of what a reader perceives as a single display element. Typowe przykłady to litera "a", symbol "@" i Emoji " 🐂 ".Common examples are the letter "a", the symbol "@", and the emoji "🐂". Czasami wygląda na to char , że jeden acter jest w rzeczywistości składający się z wielu niezależnych elementów wyświetlanych, ponieważ sekcja w klastrach Grapheme objaśnia.Sometimes what looks like one character is actually composed of multiple independent display elements, as the section on grapheme clusters explains.

stringTypy i charThe string and char types

Wystąpienie string klasy reprezentuje jakiś tekst.An instance of the string class represents some text. A string jest logicznie sekwencją wartości 16-bitowych, z których każdy jest wystąpieniem char struktury.A string is logically a sequence of 16-bit values, each of which is an instance of the char struct. string . Właściwość length zwraca liczbę char wystąpień w string wystąpieniu.The string.Length property returns the number of char instances in the string instance.

Poniższa funkcja Przykładowa drukuje wartości w notacji szesnastkowej wszystkich char wystąpień w string :The following sample function prints out the values in hexadecimal notation of all the char instances in a string:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/PrintStringChars. cs" ID = "SnippetPrintChars":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/PrintStringChars.cs" id="SnippetPrintChars":::

Przekaż string "Hello" do tej funkcji i uzyskasz następujące dane wyjściowe:Pass the string "Hello" to this function, and you get the following output:

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')

Każdy char acter jest reprezentowany przez pojedynczą char wartość.Each character is represented by a single char value. Ten wzorzec ma wartość true w przypadku większości języków świata.That pattern holds true for most of the world's languages. Na przykład poniżej przedstawiono dane wyjściowe dla dwóch chińskich char acters, takie jak nǐ hǎo i średnia Hello :For example, here's the output for two Chinese characters that sound like nǐ hǎo and mean Hello :

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

Jednakże w przypadku niektórych języków i dla niektórych symboli i znaków emoji char do reprezentowania pojedynczego acter są dwa wystąpienia char .However, for some languages and for some symbols and emoji, it takes two char instances to represent a single character. Na przykład Porównaj char acters i char wystąpienia w wyrazie oznaczające Osage w języku Osage:For example, compare the characters and char instances in the word that means Osage in the Osage language:

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')

W poprzednim przykładzie każdy acter, char z wyjątkiem miejsca, jest reprezentowany przez dwa char wystąpienia.In the preceding example, each character except the space is represented by two char instances.

Pojedynczy emoji Unicode jest również reprezentowany przez dwa char s, jak pokazano w poniższym przykładzie pokazujący OX-emoji:A single Unicode emoji is also represented by two chars, as seen in the following example showing an ox emoji:

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

Te przykłady pokazują, że wartość string.Length , która wskazuje liczbę char wystąpień, nie zawsze wskazuje liczbę wyświetlanych char acters.These examples show that the value of string.Length, which indicates the number of char instances, doesn't necessarily indicate the number of displayed characters. Pojedyncze char wystąpienie przez siebie samo nie musi reprezentować char acter.A single char instance by itself doesn't necessarily represent a character.

charPary, które są mapowane na pojedyncze char acter są nazywane parami wieloskładnikowym.The char pairs that map to a single character are called surrogate pairs. Aby zrozumieć, jak działają, należy zrozumieć kodowanie Unicode i UTF-16.To understand how they work, you need to understand Unicode and UTF-16 encoding.

Punkty kodu UnicodeUnicode code points

Unicode jest międzynarodowym standardem kodowania do użycia na różnych platformach i w różnych językach i skryptach.Unicode is an international encoding standard for use on various platforms and with various languages and scripts.

Standard Unicode definiuje ponad 1 100 000 punktów kodów.The Unicode Standard defines over 1.1 million code points. Punkt kodu jest wartością całkowitą, która może być z zakresu od 0 do U+10FFFF (dziesiętne 1 114 111).A code point is an integer value that can range from 0 to U+10FFFF (decimal 1,114,111). Niektóre punkty kodu są przypisywane do liter, symboli lub emoji.Some code points are assigned to letters, symbols, or emoji. Inne są przypisane do akcji kontrolujących sposób wyświetlania tekstu lub char acters, na przykład z wyprzedzeniem do nowego wiersza.Others are assigned to actions that control how text or characters are displayed, such as advance to a new line. Wiele punktów kodowych nie jest jeszcze przypisanych.Many code points are not yet assigned.

Poniżej przedstawiono kilka przykładów przypisań punktów kodu z linkami do kodu Unicode char TS, w którym są wyświetlane:Here are some examples of code point assignments, with links to Unicode charts in which they appear:

Liczba dziesiętnaDecimal HexHex PrzykładExample OpisDescription
1010 U+000A BrakN/A KANAŁ INFORMACYJNY WIERSZALINE FEED
9797 U+0061 aa MAŁA LITERA ALATIN SMALL LETTER A
562562 U+0232 ȲȲ WIELKA LITERA Y Z MACRONLATIN CAPITAL LETTER Y WITH MACRON
68 67568,675 U+10C43 𐱃𐱃 STARY TURKIC LETTER ORKHON NAOLD TURKIC LETTER ORKHON AT
127 801127,801 U+1F339 🌹🌹 RÓŻAny emojiROSE emoji

Punkty kodu są zwykle określane przy użyciu składni U+xxxx , gdzie xxxx jest wartością całkowitą zakodowaną szesnastkowo.Code points are customarily referred to by using the syntax U+xxxx, where xxxx is the hex-encoded integer value.

W całym zakresie punktów kodu istnieją dwa podzakresy:Within the full range of code points there are two subranges:

  • Podstawowa płaszczyzna wielojęzyczna (BMP) w zakresie U+0000..U+FFFF .The Basic Multilingual Plane (BMP) in the range U+0000..U+FFFF. Ten 16-bitowy zakres zapewnia 65 536 punktów kodów, wystarczających do pokrycia większości systemów pisania na świecie.This 16-bit range provides 65,536 code points, enough to cover the majority of the world's writing systems.
  • Dodatkowe punkty kodu z zakresu U+10000..U+10FFFF .Supplementary code points in the range U+10000..U+10FFFF. Ten 21-bitowy zakres oferuje ponad milion dodatkowych punktów kodu, które mogą być używane dla mniej dobrze znanych języków i innych celów, takich jak emoji.This 21-bit range provides more than a million additional code points that can be used for less well-known languages and other purposes such as emojis.

Na poniższym diagramie przedstawiono relację między BMP i dodatkowymi punktami kodu.The following diagram illustrates the relationship between the BMP and the supplementary code points.

 BMP i dodatkowe punkty kodów

Jednostki kodu UTF-16UTF-16 code units

16-bitowy format transformacji Unicode ( UTF-16) to char system kodowania acter, który używa 16-bitowych jednostek kodu do reprezentowania punktów kodu Unicode.16-bit Unicode Transformation Format ( UTF-16) is a character encoding system that uses 16-bit code units to represent Unicode code points. .NET używa kodowania UTF-16, aby zakodować tekst w string ..NET uses UTF-16 to encode the text in a string. charWystąpienie reprezentuje jednostkę kodu 16-bitowego.A char instance represents a 16-bit code unit.

Pojedyncza jednostka kodowa 16-bitowa może reprezentować dowolny punkt kodu w 16-bitowym zakresie podstawowej płaszczyzny wielojęzycznej.A single 16-bit code unit can represent any code point in the 16-bit range of the Basic Multilingual Plane. Jednak w przypadku punktu kodu w dodatkowych zakresach char są potrzeby dwa wystąpienia.But for a code point in the supplementary range, two char instances are needed.

Pary zastępczeSurrogate pairs

Tłumaczenie wartości 2 16-bitowych na pojedynczą wartość 21-bitową jest obsługiwane przez specjalny zakres nazywany surogatami punktów kodu od U+D800 do U+DFFF (dziesiętne 55 296 do 57 343) włącznie.The translation of two 16-bit values to a single 21-bit value is facilitated by a special range called the surrogate code points , from U+D800 to U+DFFF (decimal 55,296 to 57,343), inclusive.

Na poniższym diagramie przedstawiono relację między kodem BMP i surogatem.The following diagram illustrates the relationship between the BMP and the surrogate code points.

 BMP i Surogat punktów kodowych

Gdy do górnego punktu kodowego ( U+D800..U+DBFF ) bezpośrednio następuje dolny punkt kodowy ( U+DC00..U+DFFF ), para jest interpretowana jako dodatkowy punkt kodu przy użyciu następującej formuły:When a high surrogate code point (U+D800..U+DBFF) is immediately followed by a low surrogate code point (U+DC00..U+DFFF), the pair is interpreted as a supplementary code point by using the following formula:

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

W tej samej formule jest używana notacja dziesiętna:Here's the same formula using decimal notation:

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

Górny punkt kodu wieloskładnikowego nie ma wyższej wartości liczbowej niż dolny punkt kodowy.A high surrogate code point doesn't have a higher number value than a low surrogate code point. Górny punkt kodowy jest nazywany "wysoki", ponieważ jest używany do obliczania wyższej liczby 11 bitów pełnego zakresu kodów 21-bitowego.The high surrogate code point is called "high" because it's used to calculate the higher-order 11 bits of the full 21-bit code point range. Dolny punkt kodowy jest używany do obliczania 10 bitów o mniejszej kolejności.The low surrogate code point is used to calculate the lower-order 10 bits.

Na przykład rzeczywisty punkt kodu, który odnosi się do pary zastępczej 0xD83C i 0xDF39 jest obliczany w następujący sposób:For example, the actual code point that corresponds to the surrogate pair 0xD83C and 0xDF39 is computed as follows:

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

To samo obliczenie przy użyciu notacji dziesiętnej:Here's the same calculation using decimal notation:

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

Powyższy przykład pokazuje, że "\ud83c\udf39" jest kodowanie UTF-16 U+1F339 ROSE ('🌹') powyżej wymienionego powyżej.The preceding example demonstrates that "\ud83c\udf39" is the UTF-16 encoding of the U+1F339 ROSE ('🌹') code point mentioned earlier.

Wartości skalarne UnicodeUnicode scalar values

Wyrażenie " wartość skalarna Unicode " odnosi się do wszystkich punktów kodu innych niż punkty zastępcze kodu.The term Unicode scalar value refers to all code points other than the surrogate code points. Innymi słowy, wartość skalarna jest dowolnym punktem kodu przypisanym do char acter lub może być przypisywany char acter w przyszłości.In other words, a scalar value is any code point that is assigned a character or can be assigned a character in the future. "Znak" odnosi się do wszystkich elementów, które można przypisać do punktu kodu, co obejmuje takie czynności jak akcje kontrolujące sposób wyświetlania tekstu lub char acters."Character" here refers to anything that can be assigned to a code point, which includes such things as actions that control how text or characters are displayed.

Na poniższym diagramie przedstawiono punkty kodów wartości skalarnych.The following diagram illustrates the scalar value code points.

 wartości skalarnych

RuneTyp jako wartość skalarnąThe Rune type as a scalar value

Począwszy od platformy .NET Core 3,0, System.Text.Rune Typ reprezentuje wartość skalarną Unicode.Beginning with .NET Core 3.0, the System.Text.Rune type represents a Unicode scalar value. Rune Program nie jest dostępny w programie .NET Core 2. x lub .NET Framework 4. x.Rune is not available in .NET Core 2.x or .NET Framework 4.x.

RuneKonstruktory weryfikują, że wystąpienie wyniku jest prawidłową wartością skalarną Unicode, w przeciwnym razie zgłasza wyjątek.The Rune constructors validate that the resulting instance is a valid Unicode scalar value, otherwise they throw an exception. Poniższy przykład pokazuje kod, który pomyślnie tworzy wystąpienia Rune wystąpień, ponieważ dane wejściowe reprezentują prawidłowe wartości skalarne:The following example shows code that successfully instantiates Rune instances because the input represents valid scalar values:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/'utwórz Rune s.cs" ID = "SnippetValid":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/InstantiateRunes.cs" id="SnippetValid":::

Poniższy przykład zgłasza wyjątek, ponieważ punkt kodu znajduje się w zakresie surogatu i nie jest częścią pary dwuskładnikowej:The following example throws an exception because the code point is in the surrogate range and isn't part of a surrogate pair:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/'utwórz Rune s.cs" ID = "SnippetInvalidSurrogate":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/InstantiateRunes.cs" id="SnippetInvalidSurrogate":::

Poniższy przykład zgłasza wyjątek, ponieważ punkt kodu znajduje się poza dodatkowym zakresem:The following example throws an exception because the code point is beyond the supplementary range:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/'utwórz Rune s.cs" ID = "SnippetInvalidHigh":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/InstantiateRunes.cs" id="SnippetInvalidHigh":::

Rune przykład użycia: zmiana wielkości literRune usage example: changing letter case

Interfejs API, który przyjmuje char i zakłada, że pracuje z punktem kodu, który jest wartością skalarną, nie działa poprawnie, jeśli char pochodzi z pary zastępczej.An API that takes a char and assumes it is working with a code point that is a scalar value doesn't work correctly if the char is from a surrogate pair. Rozważmy na przykład następującą metodę, która wywołuje Char.ToUpperInvariant na każdym z char string :For example, consider the following method that calls Char.ToUpperInvariant on each char in a string:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/ConvertToUpper. cs" ID = "SnippetBadExample":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/ConvertToUpper.cs" id="SnippetBadExample":::

Jeśli input string zawiera małą literę Deseret er ( 𐑉 ), ten kod nie przekonwertuje go na wielkie litery ( 𐐡 ).If the input string contains the lowercase Deseret letter er (𐑉), this code won't convert it to uppercase (𐐡). Kod jest wywoływany char.ToUpperInvariant oddzielnie w każdym punkcie kodu zastępczego U+D801 i U+DC49 .The code calls char.ToUpperInvariant separately on each surrogate code point, U+D801 and U+DC49. Ale U+D801 nie ma wystarczających informacji, aby zidentyfikować je jako małą literę, więc char.ToUpperInvariant pozostawia ją samodzielnie.But U+D801 doesn't have enough information by itself to identify it as a lowercase letter, so char.ToUpperInvariant leaves it alone. Obsługuje to ten U+DC49 sam sposób.And it handles U+DC49 the same way. Wynikiem jest to, że małe litery "𐑉" w input string nie są konwertowane na wielkie litery "𐑉".The result is that lowercase '𐑉' in the input string doesn't get converted to uppercase '𐐡'.

Poniżej przedstawiono dwie opcje prawidłowej konwersji string na wielkie litery:Here are two options for correctly converting a string to uppercase:

  • Wywołania String.ToUpperInvariant na wejściu string zamiast Iterowanie char przez- char .Call String.ToUpperInvariant on the input string rather than iterating char-by-char. string.ToUpperInvariantMetoda ma dostęp do obu części każdej pary surogatów, więc może prawidłowo obsługiwać wszystkie punkty kodu Unicode.The string.ToUpperInvariant method has access to both parts of each surrogate pair, so it can handle all Unicode code points correctly.

  • Wykonaj iterację przez wartości skalarne Unicode jako Rune wystąpienia char , a nie wystąpienia, jak pokazano w poniższym przykładzie.Iterate through the Unicode scalar values as Rune instances instead of char instances, as shown in the following example. Ponieważ Rune wystąpienie jest prawidłową wartością skalarną Unicode, można je przesłać do interfejsów API, które oczekują na wartość skalarną.Since a Rune instance is a valid Unicode scalar value, it can be passed to APIs that expect to operate on a scalar value. Na przykład wywoływanie, Rune.ToUpperInvariant jak pokazano w poniższym przykładzie, daje poprawne wyniki:For example, calling Rune.ToUpperInvariant as shown in the following example gives correct results:

    ::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/ConvertToUpper. cs" ID = "SnippetGoodExample":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/ConvertToUpper.cs" id="SnippetGoodExample":::

Inne Rune interfejsy APIOther Rune APIs

RuneTyp uwidacznia analogowe wiele char interfejsów API.The Rune type exposes analogs of many of the char APIs. Na przykład następujące metody dublowania statycznych interfejsów API w char typie:For example, the following methods mirror static APIs on the char type:

Aby pobrać nieprzetworzoną wartość skalarną z Rune wystąpienia, użyj Rune.Value właściwości.To get the raw scalar value from a Rune instance, use the Rune.Value property.

Aby skonwertować Rune wystąpienie z powrotem do sekwencji char s, użyj Rune.ToString Rune.EncodeToUtf16 metody lub.To convert a Rune instance back to a sequence of chars, use Rune.ToString or the Rune.EncodeToUtf16 method.

Ponieważ każda wartość skalarna Unicode jest możliwa do zaprezentowania przez pojedynczą char lub dwuskładnikową parę, każde Rune wystąpienie może być reprezentowane przez co najwyżej 2 char wystąpienia.Since any Unicode scalar value is representable by a single char or by a surrogate pair, any Rune instance can be represented by at most 2 char instances. Użyj Rune.Utf16SequenceLength , aby zobaczyć, ile char wystąpień jest wymaganych do reprezentowania Rune wystąpienia.Use Rune.Utf16SequenceLength to see how many char instances are required to represent a Rune instance.

Aby uzyskać więcej informacji na temat Rune typu .NET, zobacz Rune Dokumentacja interfejsu API.For more information about the .NET Rune type, see the Rune API reference.

Klastry GraphemeGrapheme clusters

Wygląda na to char , że jeden acter może wynikać z kombinacji wielu punktów kodowych, więc bardziej opisowy termin, który jest często używany zamiast " char acter", to klaster Grapheme.What looks like one character might result from a combination of multiple code points, so a more descriptive term that is often used in place of "character" is grapheme cluster. Odpowiedni termin w programie .NET to element tekstowy.The equivalent term in .NET is text element.

Rozważ string wystąpienia "a", "o", "o" i " 👩🏽‍🚒 ".Consider the string instances "a", "á", "á", and "👩🏽‍🚒". Jeśli system operacyjny obsługuje je zgodnie z definicją standardu Unicode, każde z tych string wystąpień jest wyświetlane jako pojedynczy element tekstowy lub klaster Grapheme.If your operating system handles them as specified by the Unicode standard, each of these string instances appears as a single text element or grapheme cluster. Jednak ostatnie dwa są reprezentowane przez więcej niż jeden punkt kodu wartości skalarnej.But the last two are represented by more than one scalar value code point.

  • string"A" jest reprezentowane przez jedną wartość skalarną i zawiera jedno char wystąpienie.The string "a" is represented by one scalar value and contains one char instance.

    • U+0061 LATIN SMALL LETTER A
  • "I" string jest reprezentowane przez jedną wartość skalarną i zawiera jedno char wystąpienie.The string "á" is represented by one scalar value and contains one char instance.

    • U+00E1 LATIN SMALL LETTER A WITH ACUTE
  • string"A" wygląda tak samo jak "", ale jest reprezentowane przez dwie wartości skalarne i zawiera dwa char wystąpienia.The string "á" looks the same as "á" but is represented by two scalar values and contains two char instances.

    • U+0061 LATIN SMALL LETTER A
    • U+0301 COMBINING ACUTE ACCENT
  • Na koniec string " 👩🏽‍🚒 " jest reprezentowane przez cztery wartości skalarne i zawiera siedem char wystąpień.Finally, the string "👩🏽‍🚒" is represented by four scalar values and contains seven char instances.

    • U+1F469 WOMAN (zakres dodatkowy, wymaga pary dwuskładnikowej)U+1F469 WOMAN (supplementary range, requires a surrogate pair)
    • U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (zakres dodatkowy, wymaga pary dwuskładnikowej)U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (supplementary range, requires a surrogate pair)
    • U+200D ZERO WIDTH JOINER
    • U+1F692 FIRE ENGINE (zakres dodatkowy, wymaga pary dwuskładnikowej)U+1F692 FIRE ENGINE (supplementary range, requires a surrogate pair)

W niektórych z powyższych przykładów — takich jak modyfikator łączenia akcentu lub modyfikator odcienia skórki — punkt kodu nie jest wyświetlany jako element autonomiczny na ekranie.In some of the preceding examples - such as the combining accent modifier or the skin tone modifier - the code point does not display as a standalone element on the screen. Zamiast tego służy do modyfikowania wyglądu elementu tekstowego, który został wcześniej dołączony.Rather, it serves to modify the appearance of a text element that came before it. Te przykłady pokazują, że może przyjmować wiele wartości skalarnych, aby określić, co myślisz jako pojedynczy char klaster "acter" lub "Grapheme".These examples show that it might take multiple scalar values to make up what we think of as a single "character," or "grapheme cluster."

Aby wyliczyć klastry Grapheme z string , należy użyć StringInfo klasy, jak pokazano w poniższym przykładzie.To enumerate the grapheme clusters of a string, use the StringInfo class as shown in the following example. Jeśli znasz język SWIFT, StringInfo Typ .NET jest koncepcyjnie podobny do character typu SWIFT.If you're familiar with Swift, the .NET StringInfo type is conceptually similar to Swift's character type.

Przykład: liczba char , Rune i wystąpienia elementu tekstowegoExample: count char, Rune, and text element instances

W interfejsach API platformy .NET klaster Grapheme jest nazywany elementem tekstowym.In .NET APIs, a grapheme cluster is called a text element. Poniższa metoda pokazuje różnice między elementami char , Rune i wystąpieniami elementów tekstu w string :The following method demonstrates the differences between char, Rune, and text element instances in a string:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/CountTextElements. cs" ID = "SnippetCountMethod":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/CountTextElements.cs" id="SnippetCountMethod":::

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/CountTextElements. cs" ID = "SnippetCallCountMethod":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/CountTextElements.cs" id="SnippetCallCountMethod":::

W przypadku uruchomienia tego kodu w .NET Framework lub .NET Core 3,1 lub starszym liczba elementów tekstowych dla znaku emoji zostanie wyświetlona 4 .If you run this code in .NET Framework or .NET Core 3.1 or earlier, the text element count for the emoji shows 4. Jest to spowodowane usterką w StringInfo klasie, która została naprawiona w programie .NET 5.That is due to a bug in the StringInfo class that is fixed in .NET 5.

Przykład: dzielenie string wystąpieńExample: splitting string instances

Podczas dzielenia string wystąpień należy unikać dzielenia pary zastępczych i klastrów Grapheme.When splitting string instances, avoid splitting surrogate pairs and grapheme clusters. Rozważmy następujący przykład nieprawidłowego kodu, który zamierza wstawiać podziały wierszy co 10 char acters w string :Consider the following example of incorrect code, which intends to insert line breaks every 10 characters in a string:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/InsertNewlines. cs" ID = "SnippetBadExample":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/InsertNewlines.cs" id="SnippetBadExample":::

Ponieważ ten kod wylicza char wystąpienia, para dwuskładnikowa, która ma miejsce na obramowanie międzystrefowe, char zostanie podzielona i zostanie dodany nowy wiersz.Because this code enumerates char instances, a surrogate pair that happens to straddle a 10-char boundary will be split and a newline injected between them. W tym wstawieniu wprowadzono uszkodzenia danych, ponieważ punkty kodu surogatu są istotne tylko jako pary.This insertion introduces data corruption, because surrogate code points are meaningful only as pairs.

Potencjalne uszkodzenie danych nie jest eliminowane w przypadku wyliczania Rune wystąpień (wartości skalarnych) zamiast char wystąpień.The potential for data corruption isn't eliminated if you enumerate Rune instances (scalar values) instead of char instances. Zestaw Rune wystąpień może utworzyć klaster Grapheme, który ma międzystrefę 10 char granic.A set of Rune instances might make up a grapheme cluster that straddles a 10-char boundary. Jeśli zestaw klastrów Grapheme jest podzielony, nie można go poprawnie zinterpretować.If the grapheme cluster set is split up, it can't be interpreted correctly.

Lepszym rozwiązaniem jest przerwanie string przez zliczanie klastrów Grapheme lub elementów tekstowych, jak w poniższym przykładzie:A better approach is to break the string by counting grapheme clusters, or text elements, as in the following example:

::: Code Language = "CSharp" source = "urywki/ char acter-Encoding-Introduction/CSharp/InsertNewlines. cs" ID = "SnippetGoodExample":::::code language="csharp" source="snippets/character-encoding-introduction/csharp/InsertNewlines.cs" id="SnippetGoodExample":::

Jak wspomniano wcześniej, jednak w implementacjach platformy .NET innej niż .NET 5 StringInfo Klasa może nieprawidłowo obsłużyć niektóre klastry Grapheme.As noted earlier, however, in implementations of .NET other than .NET 5, the StringInfo class might handle some grapheme clusters incorrectly.

UTF-8 i UTF-32UTF-8 and UTF-32

Poprzednie sekcje skupiające się na kodowaniu UTF-16, ponieważ są używane przez platformę .NET do kodowania string wystąpień.The preceding sections focused on UTF-16 because that's what .NET uses to encode string instances. Istnieją inne systemy kodowania dla standardu Unicode- UTF-8 i UTF-32.There are other encoding systems for Unicode - UTF-8 and UTF-32. Te kodowania wykorzystują odpowiednio 8-bitowe jednostki kodu i 32-bitowe jednostki kodu.These encodings use 8-bit code units and 32-bit code units, respectively.

Podobnie jak w przypadku UTF-16, UTF-8 wymaga wielu jednostek kodu do reprezentowania niektórych wartości skalarnych Unicode.Like UTF-16, UTF-8 requires multiple code units to represent some Unicode scalar values. Kodowanie UTF-32 może reprezentować dowolną wartość skalarną w jednej jednostce kodu 32-bitowej.UTF-32 can represent any scalar value in a single 32-bit code unit.

Poniżej przedstawiono kilka przykładów przedstawiających sposób reprezentowania tego samego punktu kodu Unicode w każdym z tych trzech systemów kodowania Unicode:Here are some examples showing how the same Unicode code point is represented in each of these three Unicode encoding systems:

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)

Jak wspomniano wcześniej, pojedyncza jednostka kodu UTF-16 z pary zastępczej jest bezproblemowa.As noted earlier, a single UTF-16 code unit from a surrogate pair is meaningless by itself. W ten sam sposób pojedyncza jednostka kodu UTF-8 jest bezużyteczne, jeśli jest w sekwencji dwóch, trzech lub czterech używanych do obliczania wartości skalarnej.In the same way, a single UTF-8 code unit is meaningless by itself if it's in a sequence of two, three, or four used to calculate a scalar value.

EndianEndianness

W programie .NET jednostki kodu UTF-16 string są przechowywane w ciągłej pamięci jako sekwencja 16-bitowych liczb całkowitych ( char wystąpień).In .NET, the UTF-16 code units of a string are stored in contiguous memory as a sequence of 16-bit integers (char instances). Bity poszczególnych jednostek kodu są ustalane w zależności od liczby bajtów bieżącej architektury.The bits of individual code units are laid out according to the endianness of the current architecture.

W przypadku architektury little-endian string złożone z punktów kodu UTF-16 [ D801 DCCC ] zostałyby ustalone w pamięci jako bajty [ 0x01, 0xD8, 0xCC, 0xDC ] .On a little-endian architecture, the string consisting of the UTF-16 code points [ D801 DCCC ] would be laid out in memory as the bytes [ 0x01, 0xD8, 0xCC, 0xDC ]. W przypadku architektury big-endian string można ją określić w pamięci jako bajty [ 0xD8, 0x01, 0xDC, 0xCC ] .On a big-endian architecture that same string would be laid out in memory as the bytes [ 0xD8, 0x01, 0xDC, 0xCC ].

Systemy komputerowe, które komunikują się ze sobą, muszą wyrazić zgodę na reprezentację danych przekraczających sieć.Computer systems that communicate with each other must agree on the representation of data crossing the wire. Większość protokołów sieciowych używa kodowania UTF-8 jako standard podczas przesyłania tekstu, częściowo, aby uniknąć problemów, które mogą wynikać z komputera z dużą liczbą bajtów, komunikując się z komputerem o małej przepustowości.Most network protocols use UTF-8 as a standard when transmitting text, partly to avoid issues that might result from a big-endian machine communicating with a little-endian machine. stringSkładowe składające się z punktów kodu UTF-8 [ F0 90 93 8C ] będą zawsze reprezentowane jako bajty, [ 0xF0, 0x90, 0x93, 0x8C ] niezależnie od liczby bajtów.The string consisting of the UTF-8 code points [ F0 90 93 8C ] will always be represented as the bytes [ 0xF0, 0x90, 0x93, 0x8C ] regardless of endianness.

Aby użyć UTF-8 do przesyłania tekstu, aplikacje .NET często używają kodu, takiego jak Poniższy przykład:To use UTF-8 for transmitting text, .NET applications often use code like the following example:

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

W poprzednim przykładzie metoda Encoding. UTF8. GetBytes dekoduje kod UTF-16 string do serii wartości skalarnych Unicode, a następnie ponownie koduje te wartości skalarne na UTF-8 i umieszcza wyniki sekwencji w byte tablicy.In the preceding example, the method Encoding.UTF8.GetBytes decodes the UTF-16 string back into a series of Unicode scalar values, then it re-encodes those scalar values into UTF-8 and places the resulting sequence into a byte array. Metoda Encoding. UTF8. GetString wykonuje odwrotną transformację, konwertując tablicę UTF-8 byte na UTF-16 string .The method Encoding.UTF8.GetString performs the opposite transformation, converting a UTF-8 byte array to a UTF-16 string.

Ostrzeżenie

Ponieważ UTF-8 jest commonplace w Internecie, może być skłonny do odczytu nieprzetworzonych bajtów z sieci i do traktowania danych, tak jakby była to UTF-8.Since UTF-8 is commonplace on the internet, it may be tempting to read raw bytes from the wire and to treat the data as if it were UTF-8. Należy jednak sprawdzić, czy jest on rzeczywiście poprawnie sformułowany.However, you should validate that it is indeed well-formed. Złośliwy klient może przesłać do usługi źle sformułowany format UTF-8.A malicious client might submit ill-formed UTF-8 to your service. Jeśli te dane są używane w taki sposób, jakby były poprawnie sformułowane, może to spowodować błędy lub luki w zabezpieczeniach aplikacji.If you operate on that data as if it were well-formed, it could cause errors or security holes in your application. Aby sprawdzić poprawność danych UTF-8, można użyć metody takiej jak Encoding.UTF8.GetString , która przeprowadza walidację podczas konwertowania danych przychodzących na string .To validate UTF-8 data, you can use a method like Encoding.UTF8.GetString, which will perform validation while converting the incoming data to a string.

Poprawnie sformułowane kodowanieWell-formed encoding

Poprawnie sformułowane kodowanie Unicode to string jednostki kodu, które można zdekodować w sposób niejednoznaczny i bez błędu do sekwencji wartości skalarnych Unicode.A well-formed Unicode encoding is a string of code units that can be decoded unambiguously and without error into a sequence of Unicode scalar values. Poprawnie sformułowane dane można transkodować bez ograniczeń i z powrotem między UTF-8, UTF-16 i UTF-32.Well-formed data can be transcoded freely back and forth between UTF-8, UTF-16, and UTF-32.

Pytanie, czy sekwencja kodowania jest poprawnie sformułowana, czy nie jest niezwiązana z przydziałami architektury maszyny.The question of whether an encoding sequence is well-formed or not is unrelated to the endianness of a machine's architecture. Nieprawidłowo sformułowana sekwencja UTF-8 jest źle sformułowana w taki sam sposób na maszynach big-endian i little-endian.An ill-formed UTF-8 sequence is ill-formed in the same way on both big-endian and little-endian machines.

Poniżej przedstawiono kilka przykładów niewłaściwie sformułowanych kodowań:Here are some examples of ill-formed encodings:

  • W UTF-8 sekwencja [ 6C C2 61 ] jest źle sformułowana, ponieważ C2 nie może następować po niej 61 .In UTF-8, the sequence [ 6C C2 61 ] is ill-formed because C2 cannot be followed by 61.

  • W UTF-16, sekwencja [ DC00 DD00 ] (lub, w języku C#, string "\udc00\udd00" ) jest źle sformułowana, ponieważ dolny Surogat DC00 nie może następować po drugim dolnym surogatie DD00 .In UTF-16, the sequence [ DC00 DD00 ] (or, in C#, the string "\udc00\udd00") is ill-formed because the low surrogate DC00 cannot be followed by another low surrogate DD00.

  • W UTF-32 sekwencja [ 0011ABCD ] jest źle sformułowana, ponieważ 0011ABCD znajduje się poza zakresem wartości skalarnych Unicode.In UTF-32, the sequence [ 0011ABCD ] is ill-formed because 0011ABCD is outside the range of Unicode scalar values.

W programie .NET string wystąpienia prawie zawsze zawierają poprawnie sformułowane dane UTF-16, ale nie są one gwarantowane.In .NET, string instances almost always contain well-formed UTF-16 data, but that isn't guaranteed. W poniższych przykładach pokazano prawidłowy kod w języku C#, który tworzy nieprawidłowo sformułowane dane UTF-16 w string wystąpieniach.The following examples show valid C# code that creates ill-formed UTF-16 data in string instances.

  • Nieprawidłowo sformułowany literał:An ill-formed literal:

    const string s = "\ud800";
    
  • Sub string dzieląca parę zastępczą:A substring that splits up a surrogate pair:

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

Interfejsy API, takie jak Encoding.UTF8.GetString nigdy nie zwracają źle sformułowane string wystąpienia.APIs like Encoding.UTF8.GetString never return ill-formed string instances. Encoding.GetString``Encoding.GetBytesmetody i wykrywają źle sformułowane sekwencje w danych wejściowych i wykonują char podstawienie acter podczas generowania danych wyjściowych.Encoding.GetString and Encoding.GetBytes methods detect ill-formed sequences in the input and perform character substitution when generating the output. Jeśli na przykład Encoding.ASCII.GetString(byte[]) w danych wejściowych zostanie wyświetlony bajt inny niż ASCII (poza zakresem u + 0000.. u + 007F), wstawiany jest znak "?" w zwracanym string wystąpieniu.For example, if Encoding.ASCII.GetString(byte[]) sees a non-ASCII byte in the input (outside the range U+0000..U+007F), it inserts a '?' into the returned string instance. Encoding.UTF8.GetString(byte[]) zamienia źle sformułowane sekwencje UTF-8 z U+FFFD REPLACEMENT CHARACTER ('�') w zwracanym string wystąpieniu.Encoding.UTF8.GetString(byte[]) replaces ill-formed UTF-8 sequences with U+FFFD REPLACEMENT CHARACTER ('�') in the returned string instance. Aby uzyskać więcej informacji, zobacz standard Unicode, sekcje 5,22 i 3,9.For more information, see the Unicode Standard, Sections 5.22 and 3.9.

Wbudowane Encoding klasy można również skonfigurować tak, aby zgłaszać wyjątek, a nie char przestawiać acter, gdy widoczne są źle sformułowane sekwencje.The built-in Encoding classes can also be configured to throw an exception rather than perform character substitution when ill-formed sequences are seen. Takie podejście jest często stosowane w aplikacjach z uwzględnieniem zabezpieczeń, w których char podstawianie acter może być niedopuszczalne.This approach is often used in security-sensitive applications where character substitution might not be acceptable.

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

Aby uzyskać informacje o sposobach korzystania z wbudowanych Encoding klas, zobacz How to use char acter Encoding Classes in .NET.For information about how to use the built-in Encoding classes, see How to use character encoding classes in .NET.

Zobacz takżeSee also