Jak používat třídy kódování znaků v .NET

Tento článek vysvětluje, jak používat třídy, které .NET poskytuje pro kódování a dekódování textu pomocí různých schémat kódování. Pokyny předpokládají, že jste si přečetli úvod do kódování znaků v .NET.

Kodéry a dekodéry

.NET poskytuje třídy kódování, které kódují a dekódují text pomocí různých kódovacích systémů. Třída například UTF8Encoding popisuje pravidla kódování do a dekódování z UTF-8. .NET používá kódování UTF-16 (reprezentované UnicodeEncoding třídou) pro string instance. Kodéry a dekodéry jsou k dispozici pro jiná schémata kódování.

Kódování a dekódování může také zahrnovat ověření. Třída například kontroluje všechny char instance v rozsahu náhradních dat, UnicodeEncoding aby se ujistila, že jsou v platných náhradních dvojicích. Záložní strategie určuje, jak kodér zpracovává neplatné znaky nebo jak dekodér zpracovává neplatné bajty.

Upozorňující

Třídy kódování .NET poskytují způsob, jak ukládat a převádět data znaků. Neměly by se používat k ukládání binárních dat ve formě řetězce. V závislosti na použitém kódování může převod binárních dat na formát řetězce s třídami kódování způsobit neočekávané chování a způsobit nepřesná nebo poškozená data. Chcete-li převést binární data na řetězcový formulář, použijte metodu Convert.ToBase64String .

Všechny třídy kódování znaků v .NET dědí z System.Text.Encoding třídy, což je abstraktní třída, která definuje funkce společné pro všechna kódování znaků. Pokud chcete získat přístup k jednotlivým objektům kódování implementovaným v .NET, postupujte takto:

  • Použijte statické vlastnosti Encoding třídy, které vracejí objekty, které představují standardní kódování znaků dostupné v .NET (ASCII, UTF-7, UTF-8, UTF-16 a UTF-32). Encoding.Unicode Například vlastnost vrátí UnicodeEncoding objekt. Každý objekt používá náhradní náhradní náhradní objekt pro zpracování řetězců, které nemůže kódovat a bajty, které nemůže dekódovat. Další informace naleznete v tématu Náhradní náhradní.

  • Volání konstruktoru třídy kódování Objekty pro kódování ASCII, UTF-7, UTF-8, UTF-16 a UTF-32 lze tímto způsobem vytvořit instanci. Ve výchozím nastavení každý objekt používá náhradní náhradní náhradní objekt pro zpracování řetězců, které nemůže kódovat a bajty, které nemůže dekódovat, ale můžete určit, že by měla být vyvolána výjimka. Další informace naleznete v tématu Náhradní náhradní anáhradní výjimka.

  • Encoding(Int32) Zavolejte konstruktor a předejte ho celé číslo, které představuje kódování. Standardní kódovací objekty používají náhradní náhradní objekty a znakové stránky a dvoubabajtů znakové sady (DBCS) používají náhradní objekty pro zpracování řetězců, které nemohou kódovat a bajty, které nemohou dekódovat. Další informace najdete v tématu Nejvhodnější náhradní řešení.

  • Volejte metodu Encoding.GetEncoding , která vrátí jakékoli standardní kódování, znakovou stránku nebo kód DBCS dostupné v .NET. Přetížení umožňují určit záložní objekt pro kodér i dekodér.

Informace o všech kódováních dostupných v .NET můžete načíst voláním Encoding.GetEncodings metody. .NET podporuje schémata kódování znaků uvedená v následující tabulce.

Třída kódování Popis
ASCII Zakóduje omezený rozsah znaků pomocí nižších sedmi bitů bajtu. Vzhledem k tomu, že toto kódování podporuje pouze hodnoty znaků od U+0000 času U+007F, ve většině případů je nedostatečné pro mezinárodní aplikace.
UTF-7 Představuje znaky jako sekvence 7bitových znaků ASCII. Znaky unicode jiné než ASCII jsou reprezentovány řídicí sekvencí znaků ASCII. UTF-7 podporuje protokoly, jako je e-mail a diskusní skupina. UTF-7 ale není zvlášť zabezpečený ani robustní. V některých případech může změna jednoho bitu radikálně změnit interpretaci celého řetězce UTF-7. V jiných případech mohou různé řetězce UTF-7 kódovat stejný text. U sekvencí, které obsahují znaky jiné než ASCII, vyžaduje UTF-7 více místa než UTF-8 a kódování/dekódování je pomalejší. V důsledku toho byste místo UTF-7 měli použít UTF-8, pokud je to možné.
UTF-8 Představuje každý bod kódu Unicode jako posloupnost jednoho až čtyř bajtů. UTF-8 podporuje 8bitové velikosti dat a funguje dobře s mnoha existujícími operačními systémy. Pro rozsah znaků ASCII je UTF-8 identický s kódováním ASCII a umožňuje širší sadu znaků. U skriptů CJK (Chinese-Japanese-Korean) však může UTF-8 vyžadovat pro každý znak tři bajty a může způsobit větší velikosti dat než UTF-16. Někdy množství dat ASCII, jako jsou značky HTML, odůvodňuje zvýšenou velikost oblasti CJK.
UTF-16 Představuje každý bod kódu Unicode jako posloupnost jednoho nebo dvou 16bitových celých čísel. Nejběžnější znaky Unicode vyžadují pouze jeden bod kódu UTF-16, i když doplňkové znaky Unicode (U+10000 a vyšší) vyžadují dva náhradní body kódu UTF-16. Podporují se jak příkazy little-endian, tak big-endian bajt. Kódování UTF-16 používá modul CLR (Common Language Runtime) k reprezentaci a String hodnoty a operační systém Windows ho používá k reprezentaci CharWCHAR hodnot.
UTF-32 Představuje každý bod kódu Unicode jako 32bitové celé číslo. Podporují se jak příkazy little-endian, tak big-endian bajt. Kódování UTF-32 se používá, když aplikace chtějí zabránit chování náhradních bodů kódu kódování UTF-16 v operačních systémech, pro které je kódovaný prostor příliš důležitý. Jeden glyfy vykreslené na displeji lze stále zakódovat s více než jedním znakem UTF-32.
Kódování ANSI/ISO Poskytuje podporu pro různé kódové stránky. V operačních systémech Windows se k podpoře konkrétního jazyka nebo skupiny jazyků používají znakové stránky. Tabulku, která obsahuje stránky kódu podporované rozhraním .NET, najdete v Encoding této třídě. Kódovací objekt pro konkrétní znakovou stránku můžete načíst voláním Encoding.GetEncoding(Int32) metody. Znaková stránka obsahuje 256 bodů kódu a je založená na nule. Ve většině znakových stránek představují body kódu 0 až 127 znakovou sadu ASCII a body kódu 128 až 255 se výrazně liší mezi znakovými stránkami. Například znaková stránka 1252 poskytuje znaky pro systémy zápisu latinky, včetně angličtiny, němčiny a francouzštiny. Posledních 128 bodů kódu na znakové stránce 1252 obsahuje zvýrazňující znaky. Znaková stránka 1253 obsahuje kódy znaků, které jsou vyžadovány v řeckém systému zápisu. Posledních 128 bodů kódu na znakové stránce 1253 obsahuje řecké znaky. V důsledku toho aplikace, která spoléhá na znakové stránky ANSI, nemůže uložit řečtinu a němčinu do stejného textového streamu, pokud neobsahuje identifikátor, který označuje odkazovanou znakovou stránku.
Kódování dvoubabajtů znakové sady (DBCS) Podporuje jazyky, jako jsou čínština, japonština a korejština, které obsahují více než 256 znaků. Ve službě DBCS představuje dvojice bodů kódu (dvojitý bajt) každý znak. Vlastnost Encoding.IsSingleByte se vrátí false pro kódování DBCS. Objekt kódování pro konkrétní službu DBCS můžete načíst voláním Encoding.GetEncoding(Int32) metody. Když aplikace zpracovává data dbCS, první bajt znaku DBCS (hlavní bajt) se zpracuje v kombinaci s bajtem stopy, který za ním bezprostředně následuje. Vzhledem k tomu, že jeden pár dvoubatových bodů kódu může představovat různé znaky v závislosti na znakové stránce, toto schéma stále neumožňuje kombinaci dvou jazyků, jako je japonština a čínština, ve stejném datovém proudu.

Tato kódování umožňují pracovat s znaky Unicode a s kódováními, které se nejčastěji používají ve starších aplikacích. Kromě toho můžete vytvořit vlastní kódování definováním třídy, která je odvozena a Encoding přepsána jeho členy.

Podpora kódování .NET Core

Ve výchozím nastavení .NET Core neposkytuje žádné kódování znakové stránky jiné než kódová stránka 28591 a kódování Unicode, jako je UTF-8 a UTF-16. Kódování znakové stránky ale můžete přidat ve standardních aplikacích pro Windows, které cílí na .NET do vaší aplikace. Další informace najdete v CodePagesEncodingProvider tématu.

Výběr třídy kódování

Pokud máte možnost zvolit kódování, které má vaše aplikace používat, měli byste použít kódování Unicode, nejlépe buď UTF8Encoding nebo UnicodeEncoding. (.NET také podporuje třetí kódování Unicode, UTF32Encoding.)

Pokud plánujete použít kódování ASCII (ASCIIEncoding), zvolte UTF8Encoding místo toho. Dvě kódování jsou pro znakovou sadu ASCII stejné, ale UTF8Encoding mají následující výhody:

  • Může představovat každý znak Unicode, zatímco ASCIIEncoding podporuje pouze hodnoty znaků Unicode mezi U+0000 a U+007F.

  • Poskytuje detekci chyb a lepší zabezpečení.

  • Byl vyladěn tak, aby byl co nejrychleji a měl by být rychlejší než jakékoli jiné kódování. Dokonce i pro obsah, který je zcela ASCII, operace prováděné s UTF8Encoding jsou rychlejší než operace prováděné s ASCIIEncoding.

Měli byste zvážit použití ASCIIEncoding pouze pro starší verze aplikací. I u starších aplikací UTF8Encoding ale může být lepší volbou z následujících důvodů (za předpokladu výchozího nastavení):

  • Pokud má vaše aplikace obsah, který není výhradně ASCII a kóduje ho pomocí ASCIIEncoding, každý znak jiné než ASCII kóduje jako otazník (?). Pokud aplikace pak dekóduje tato data, informace se ztratí.

  • Pokud vaše aplikace obsahuje obsah, který není výhradně ASCII a kóduje ho UTF8Encoding, výsledek se zdá být nečitelný, pokud je interpretován jako ASCII. Pokud však aplikace k dekódování těchto dat používá dekódovač UTF-8, data úspěšně provedou odezvu.

Ve webové aplikaci by znaky odeslané klientovi v reakci na webový požadavek měly odrážet kódování použité v klientovi. Ve většině případů byste měli vlastnost nastavit HttpResponse.ContentEncoding na hodnotu vrácenou HttpRequest.ContentEncoding vlastností, aby zobrazovala text v kódování, které uživatel očekává.

Použití objektu kódování

Kodér převede řetězec znaků (nejčastěji znaky Unicode) na jeho číselný (bajtový) ekvivalent. Pomocí kodéru ASCII můžete například převést znaky Unicode na ASCII, aby se mohly zobrazit v konzole. Chcete-li provést převod, zavoláte metodu Encoding.GetBytes . Pokud chcete určit, kolik bajtů je potřeba k uložení zakódovaných znaků před provedením kódování, můžete metodu GetByteCount volat.

Následující příklad používá jedno bajtové pole ke kódování řetězců ve dvou samostatných operacích. Udržuje index, který označuje počáteční pozici v bajtovém poli pro další sadu BAJTŮ kódovaných ASCII. Volá metodu ASCIIEncoding.GetByteCount(String) , aby se zajistilo, že pole bajtů je dostatečně velké, aby vyhovovalo zakódovanému řetězci. Potom volá metodu ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) , která kóduje znaky v řetězci.

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      string[] strings= { "This is the first sentence. ",
                          "This is the second sentence. " };
      Encoding asciiEncoding = Encoding.ASCII;

      // Create array of adequate size.
      byte[] bytes = new byte[49];
      // Create index for current position of array.
      int index = 0;

      Console.WriteLine("Strings to encode:");
      foreach (var stringValue in strings) {
         Console.WriteLine("   {0}", stringValue);

         int count = asciiEncoding.GetByteCount(stringValue);
         if (count + index >=  bytes.Length)
            Array.Resize(ref bytes, bytes.Length + 50);

         int written = asciiEncoding.GetBytes(stringValue, 0,
                                              stringValue.Length,
                                              bytes, index);

         index = index + written;
      }
      Console.WriteLine("\nEncoded bytes:");
      Console.WriteLine("{0}", ShowByteValues(bytes, index));
      Console.WriteLine();

      // Decode Unicode byte array to a string.
      string newString = asciiEncoding.GetString(bytes, 0, index);
      Console.WriteLine("Decoded: {0}", newString);
   }

   private static string ShowByteValues(byte[] bytes, int last )
   {
      string returnString = "   ";
      for (int ctr = 0; ctr <= last - 1; ctr++) {
         if (ctr % 20 == 0)
            returnString += "\n   ";
         returnString += String.Format("{0:X2} ", bytes[ctr]);
      }
      return returnString;
   }
}
// The example displays the following output:
//       Strings to encode:
//          This is the first sentence.
//          This is the second sentence.
//
//       Encoded bytes:
//
//          54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
//          6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
//          73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
//
//       Decoded: This is the first sentence. This is the second sentence.
Imports System.Text

Module Example
    Public Sub Main()
        Dim strings() As String = {"This is the first sentence. ",
                                    "This is the second sentence. "}
        Dim asciiEncoding As Encoding = Encoding.ASCII

        ' Create array of adequate size.
        Dim bytes(50) As Byte
        ' Create index for current position of array.
        Dim index As Integer = 0

        Console.WriteLine("Strings to encode:")
        For Each stringValue In strings
            Console.WriteLine("   {0}", stringValue)

            Dim count As Integer = asciiEncoding.GetByteCount(stringValue)
            If count + index >= bytes.Length Then
                Array.Resize(bytes, bytes.Length + 50)
            End If
            Dim written As Integer = asciiEncoding.GetBytes(stringValue, 0,
                                                            stringValue.Length,
                                                            bytes, index)

            index = index + written
        Next
        Console.WriteLine()
        Console.WriteLine("Encoded bytes:")
        Console.WriteLine("{0}", ShowByteValues(bytes, index))
        Console.WriteLine()

        ' Decode Unicode byte array to a string.
        Dim newString As String = asciiEncoding.GetString(bytes, 0, index)
        Console.WriteLine("Decoded: {0}", newString)
    End Sub

    Private Function ShowByteValues(bytes As Byte(), last As Integer) As String
        Dim returnString As String = "   "
        For ctr As Integer = 0 To last - 1
            If ctr Mod 20 = 0 Then returnString += vbCrLf + "   "
            returnString += String.Format("{0:X2} ", bytes(ctr))
        Next
        Return returnString
    End Function
End Module
' The example displays the following output:
'       Strings to encode:
'          This is the first sentence.
'          This is the second sentence.
'       
'       Encoded bytes:
'       
'          54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
'          6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
'          73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
'       
'       Decoded: This is the first sentence. This is the second sentence.

Dekodér převede bajtové pole, které odráží kódování určitého znaku na sadu znaků, a to buď v matici znaků, nebo v řetězci. Chcete-li dekódovat bajtové pole do pole znaků, zavoláte metodu Encoding.GetChars . Chcete-li dekódovat bajtové pole do řetězce, zavoláte metodu GetString . Pokud chcete určit, kolik znaků je potřeba k uložení dekódovaných bajtů před provedením dekódování, můžete metodu GetCharCount volat.

Následující příklad zakóduje tři řetězce a pak je dekóduje do jednoho pole znaků. Udržuje index, který označuje počáteční pozici v poli znaků pro další sadu dekódovaných znaků. Volá metodu GetCharCount , aby se zajistilo, že pole znaků je dostatečně velké, aby vyhovovalo všem dekódovaným znakům. Potom volá metodu ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) pro dekódování bajtového pole.

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      string[] strings = { "This is the first sentence. ",
                           "This is the second sentence. ",
                           "This is the third sentence. " };
      Encoding asciiEncoding = Encoding.ASCII;
      // Array to hold encoded bytes.
      byte[] bytes;
      // Array to hold decoded characters.
      char[] chars = new char[50];
      // Create index for current position of character array.
      int index = 0;

      foreach (var stringValue in strings) {
         Console.WriteLine("String to Encode: {0}", stringValue);
         // Encode the string to a byte array.
         bytes = asciiEncoding.GetBytes(stringValue);
         // Display the encoded bytes.
         Console.Write("Encoded bytes: ");
         for (int ctr = 0; ctr < bytes.Length; ctr++)
            Console.Write(" {0}{1:X2}",
                          ctr % 20 == 0 ? Environment.NewLine : "",
                          bytes[ctr]);
         Console.WriteLine();

         // Decode the bytes to a single character array.
         int count = asciiEncoding.GetCharCount(bytes);
         if (count + index >=  chars.Length)
            Array.Resize(ref chars, chars.Length + 50);

         int written = asciiEncoding.GetChars(bytes, 0,
                                              bytes.Length,
                                              chars, index);
         index = index + written;
         Console.WriteLine();
      }

      // Instantiate a single string containing the characters.
      string decodedString = new string(chars, 0, index - 1);
      Console.WriteLine("Decoded string: ");
      Console.WriteLine(decodedString);
   }
}
// The example displays the following output:
//    String to Encode: This is the first sentence.
//    Encoded bytes:
//    54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
//    6E 74 65 6E 63 65 2E 20
//
//    String to Encode: This is the second sentence.
//    Encoded bytes:
//    54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
//    65 6E 74 65 6E 63 65 2E 20
//
//    String to Encode: This is the third sentence.
//    Encoded bytes:
//    54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
//    6E 74 65 6E 63 65 2E 20
//
//    Decoded string:
//    This is the first sentence. This is the second sentence. This is the third sentence.
Imports System.Text

Module Example
    Public Sub Main()
        Dim strings() As String = {"This is the first sentence. ",
                                    "This is the second sentence. ",
                                    "This is the third sentence. "}
        Dim asciiEncoding As Encoding = Encoding.ASCII
        ' Array to hold encoded bytes.
        Dim bytes() As Byte
        ' Array to hold decoded characters.
        Dim chars(50) As Char
        ' Create index for current position of character array.
        Dim index As Integer

        For Each stringValue In strings
            Console.WriteLine("String to Encode: {0}", stringValue)
            ' Encode the string to a byte array.
            bytes = asciiEncoding.GetBytes(stringValue)
            ' Display the encoded bytes.
            Console.Write("Encoded bytes: ")
            For ctr As Integer = 0 To bytes.Length - 1
                Console.Write(" {0}{1:X2}", If(ctr Mod 20 = 0, vbCrLf, ""),
                                            bytes(ctr))
            Next
            Console.WriteLine()

            ' Decode the bytes to a single character array.
            Dim count As Integer = asciiEncoding.GetCharCount(bytes)
            If count + index >= chars.Length Then
                Array.Resize(chars, chars.Length + 50)
            End If
            Dim written As Integer = asciiEncoding.GetChars(bytes, 0,
                                                            bytes.Length,
                                                            chars, index)
            index = index + written
            Console.WriteLine()
        Next

        ' Instantiate a single string containing the characters.
        Dim decodedString As New String(chars, 0, index - 1)
        Console.WriteLine("Decoded string: ")
        Console.WriteLine(decodedString)
    End Sub
End Module
' The example displays the following output:
'    String to Encode: This is the first sentence.
'    Encoded bytes:
'    54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
'    6E 74 65 6E 63 65 2E 20
'    
'    String to Encode: This is the second sentence.
'    Encoded bytes:
'    54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
'    65 6E 74 65 6E 63 65 2E 20
'    
'    String to Encode: This is the third sentence.
'    Encoded bytes:
'    54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
'    6E 74 65 6E 63 65 2E 20
'    
'    Decoded string:
'    This is the first sentence. This is the second sentence. This is the third sentence.

Metody kódování a dekódování třídy odvozené z Encoding jsou navrženy tak, aby fungovaly na kompletní sadě dat. To znamená, že všechna data, která mají být kódována nebo dekódována, jsou poskytována v rámci volání jedné metody. V některých případech jsou však data dostupná ve streamu a data, která mají být kódována nebo dekódována, mohou být k dispozici pouze z samostatných operací čtení. To vyžaduje, aby operace kódování nebo dekódování zapamatovala všechny uložené stavy z předchozího vyvolání. Metody tříd odvozené z Encoder a Decoder jsou schopny zpracovat kódování a dekódování operací, které zahrnují více volání metod.

Objekt Encoder pro konkrétní kódování je k dispozici z této Encoding.GetEncoder vlastnosti kódování. Objekt Decoder pro konkrétní kódování je k dispozici z vlastnosti Encoding.GetDecoder tohoto kódování. U dekódovacích operací mějte na paměti, že třídy odvozené z Decoder metody Decoder.GetChars , ale nemají metodu, která odpovídá Encoding.GetString.

Následující příklad znázorňuje rozdíl mezi použitím Encoding.GetString a Decoder.GetChars metodami pro dekódování pole bajtů Unicode. Příklad kóduje řetězec, který obsahuje některé znaky Unicode do souboru, a pak pomocí dvou dekódovacích metod dekóduje je deset bajtů najednou. Vzhledem k tomu, že náhradní pár nastane v desáté a jedenácté bajty, je dekódován v samostatných voláních metody. Jak ukazuje výstup, Encoding.GetString metoda nedokáže správně dekódovat bajty a místo toho je nahradí U+FFFD (REPLACE CHARACTER). Na druhou stranu Decoder.GetChars je metoda schopna úspěšně dekódovat bajtové pole, aby získala původní řetězec.

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

public class Example
{
   public static void Main()
   {
      // Use default replacement fallback for invalid encoding.
      UnicodeEncoding enc = new UnicodeEncoding(true, false, false);

      // Define a string with various Unicode characters.
      string str1 = "AB YZ 19 \uD800\udc05 \u00e4";
      str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308";
      Console.WriteLine("Created original string...\n");

      // Convert string to byte array.
      byte[] bytes = enc.GetBytes(str1);

      FileStream fs = File.Create(@".\characters.bin");
      BinaryWriter bw = new BinaryWriter(fs);
      bw.Write(bytes);
      bw.Close();

      // Read bytes from file.
      FileStream fsIn = File.OpenRead(@".\characters.bin");
      BinaryReader br = new BinaryReader(fsIn);

      const int count = 10;            // Number of bytes to read at a time.
      byte[] bytesRead = new byte[10]; // Buffer (byte array).
      int read;                        // Number of bytes actually read.
      string str2 = String.Empty;      // Decoded string.

      // Try using Encoding object for all operations.
      do {
         read = br.Read(bytesRead, 0, count);
         str2 += enc.GetString(bytesRead, 0, read);
      } while (read == count);
      br.Close();
      Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...");
      CompareForEquality(str1, str2);
      Console.WriteLine();

      // Use Decoder for all operations.
      fsIn = File.OpenRead(@".\characters.bin");
      br = new BinaryReader(fsIn);
      Decoder decoder = enc.GetDecoder();
      char[] chars = new char[50];
      int index = 0;                   // Next character to write in array.
      int written = 0;                 // Number of chars written to array.
      do {
         read = br.Read(bytesRead, 0, count);
         if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length)
            Array.Resize(ref chars, chars.Length + 50);

         written = decoder.GetChars(bytesRead, 0, read, chars, index);
         index += written;
      } while (read == count);
      br.Close();
      // Instantiate a string with the decoded characters.
      string str3 = new String(chars, 0, index);
      Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...");
      CompareForEquality(str1, str3);
   }

   private static void CompareForEquality(string original, string decoded)
   {
      bool result = original.Equals(decoded);
      Console.WriteLine("original = decoded: {0}",
                        original.Equals(decoded, StringComparison.Ordinal));
      if (! result) {
         Console.WriteLine("Code points in original string:");
         foreach (var ch in original)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
         Console.WriteLine();

         Console.WriteLine("Code points in decoded string:");
         foreach (var ch in decoded)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//    Created original string...
//
//    Decoded string using UnicodeEncoding.GetString()...
//    original = decoded: False
//    Code points in original string:
//    0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
//    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
//    0020 0073 0020 0062 0308
//    Code points in decoded string:
//    0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
//    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
//    0020 0073 0020 0062 0308
//
//    Decoded string using UnicodeEncoding.Decoder.GetString()...
//    original = decoded: True
Imports System.IO
Imports System.Text

Module Example
    Public Sub Main()
        ' Use default replacement fallback for invalid encoding.
        Dim enc As New UnicodeEncoding(True, False, False)

        ' Define a string with various Unicode characters.
        Dim str1 As String = String.Format("AB YZ 19 {0}{1} {2}",
                                           ChrW(&hD800), ChrW(&hDC05), ChrW(&h00e4))
        str1 += String.Format("Unicode characters. {0} {1} s {2}{3}",
                              ChrW(&h00a9), ChrW(&h010C), ChrW(&h0062), ChrW(&h0308))
        Console.WriteLine("Created original string...")
        Console.WriteLine()

        ' Convert string to byte array.                     
        Dim bytes() As Byte = enc.GetBytes(str1)

        Dim fs As FileStream = File.Create(".\characters.bin")
        Dim bw As New BinaryWriter(fs)
        bw.Write(bytes)
        bw.Close()

        ' Read bytes from file.
        Dim fsIn As FileStream = File.OpenRead(".\characters.bin")
        Dim br As New BinaryReader(fsIn)

        Const count As Integer = 10      ' Number of bytes to read at a time. 
        Dim bytesRead(9) As Byte         ' Buffer (byte array).
        Dim read As Integer              ' Number of bytes actually read. 
        Dim str2 As String = ""          ' Decoded string.

        ' Try using Encoding object for all operations.
        Do
            read = br.Read(bytesRead, 0, count)
            str2 += enc.GetString(bytesRead, 0, read)
        Loop While read = count
        br.Close()
        Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...")
        CompareForEquality(str1, str2)
        Console.WriteLine()

        ' Use Decoder for all operations.
        fsIn = File.OpenRead(".\characters.bin")
        br = New BinaryReader(fsIn)
        Dim decoder As Decoder = enc.GetDecoder()
        Dim chars(50) As Char
        Dim index As Integer = 0         ' Next character to write in array.
        Dim written As Integer = 0       ' Number of chars written to array.
        Do
            read = br.Read(bytesRead, 0, count)
            If index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length Then
                Array.Resize(chars, chars.Length + 50)
            End If
            written = decoder.GetChars(bytesRead, 0, read, chars, index)
            index += written
        Loop While read = count
        br.Close()
        ' Instantiate a string with the decoded characters.
        Dim str3 As New String(chars, 0, index)
        Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...")
        CompareForEquality(str1, str3)
    End Sub

    Private Sub CompareForEquality(original As String, decoded As String)
        Dim result As Boolean = original.Equals(decoded)
        Console.WriteLine("original = decoded: {0}",
                          original.Equals(decoded, StringComparison.Ordinal))
        If Not result Then
            Console.WriteLine("Code points in original string:")
            For Each ch In original
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
            Console.WriteLine()

            Console.WriteLine("Code points in decoded string:")
            For Each ch In decoded
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
            Console.WriteLine()
        End If
    End Sub
End Module
' The example displays the following output:
'    Created original string...
'    
'    Decoded string using UnicodeEncoding.GetString()...
'    original = decoded: False
'    Code points in original string:
'    0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
'    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
'    0020 0073 0020 0062 0308
'    Code points in decoded string:
'    0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
'    0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
'    0020 0073 0020 0062 0308
'    
'    Decoded string using UnicodeEncoding.Decoder.GetString()...
'    original = decoded: True

Volba záložní strategie

Když se metoda pokusí kódovat nebo dekódovat znak, ale neexistuje žádné mapování, musí implementovat záložní strategii, která určuje, jak se má neúspěšné mapování zpracovat. Existují tři typy záložních strategií:

  • Nejvhodnější náhradní řešení

  • Náhradní náhradní

  • Náhradní výjimka

Důležité

K nejběžnějším problémům s kódovacími operacemi dochází v případě, že znak Unicode nelze namapovat na konkrétní kódování znakové stránky. K nejběžnějším problémům při dekódování operací dochází v případě, že neplatné bajtové sekvence nelze přeložit na platné znaky Unicode. Z těchto důvodů byste měli vědět, jakou záložní strategii používá konkrétní objekt kódování. Kdykoli je to možné, měli byste při vytváření instance objektu určit záložní strategii používanou objektem kódování.

Nejvhodnější náhradní řešení

Pokud znak nemá v cílovém kódování přesnou shodu, kodér se ho může pokusit namapovat na podobný znak. (Nejvhodnější náhradní řešení je většinou kódování, ne dekódování problému. Existuje velmi málo znakových stránek, které obsahují znaky, které nelze úspěšně namapovat na Unicode.) Pro znakovou stránku a kódování znakové sady s dvojitým bajtem, které jsou načteny Encoding.GetEncoding(Int32) a Encoding.GetEncoding(String) přetížení, je nejvhodnější náhradní znaková sada.

Poznámka:

Třídy kódování Unicode poskytované v .NET (UTF8Encoding, UnicodeEncodinga UTF32Encoding) podporují každý znak v každé znakové sadě, takže je lze použít k odstranění problémů s nejvhodnějším náhradním řešením.

Nejvhodnější strategie se liší pro různé znakové stránky. Například u některých znakových stránek se znaky latinky s plnou šířkou mapuje na běžnější znaky latinky s poloviční šířkou. U jiných znakových stránek se toto mapování nevytáčí. I při agresivní strategii, která nejlépe vyhovuje, neexistuje pro některé znaky v některých kódováních žádná představa. Například čínský ideograf nemá rozumné mapování na znakovou stránku 1252. V tomto případě se použije náhradní řetězec. Ve výchozím nastavení je tento řetězec jen jedním OTAZNÍKEM (U+003F).

Poznámka:

Strategie nejvhodnějších řešení nejsou podrobně zdokumentované. Na webu konsorcia Unicode je však dokumentováno několik znakových stránek. V souboru readme.txt v této složce najdete popis, jak interpretovat soubory mapování.

Následující příklad používá znakovou stránku 1252 (znakovou stránku Systému Windows pro západoevropské jazyky), aby ilustrovala mapování co nejlépe hodí a jeho nevýhody. Metoda Encoding.GetEncoding(Int32) se používá k načtení objektu kódování pro znakovou stránku 1252. Ve výchozím nastavení používá mapování nejvhodnější pro znaky Unicode, které nepodporuje. Příklad vytvoří instanci řetězce, který obsahuje tři znaky jiné než ASCII – VELKÉ PÍSMENO LATINKY (U+24C8), HORNÍ INDEX FIVE (U+2075) a NEKONEČNO (U+221E) oddělené mezerami. Jak ukazuje výstup z příkladu, když je řetězec zakódovaný, nahradí se tři původní neprostorové znaky znaky OTAZNÍK (U+003F), ČÍSLICE FIVE (U+0035) a DIGIT EIGHT (U+0038). DIGIT EIGHT je zvláště špatná náhrada za nepodporovaný znak INFINITY a OTAZNÍK označuje, že pro původní znak nebylo k dispozici žádné mapování.

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      // Get an encoding for code page 1252 (Western Europe character set).
      Encoding cp1252 = Encoding.GetEncoding(1252);

      // Define and display a string.
      string str = "\u24c8 \u2075 \u221e";
      Console.WriteLine("Original string: " + str);
      Console.Write("Code points in string: ");
      foreach (var ch in str)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine("\n");

      // Encode a Unicode string.
      Byte[] bytes = cp1252.GetBytes(str);
      Console.Write("Encoded bytes: ");
      foreach (byte byt in bytes)
         Console.Write("{0:X2} ", byt);
      Console.WriteLine("\n");

      // Decode the string.
      string str2 = cp1252.GetString(bytes);
      Console.WriteLine("String round-tripped: {0}", str.Equals(str2));
      if (! str.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
      }
   }
}
// The example displays the following output:
//       Original string: Ⓢ ⁵ ∞
//       Code points in string: 24C8 0020 2075 0020 221E
//
//       Encoded bytes: 3F 20 35 20 38
//
//       String round-tripped: False
//       ? 5 8
//       003F 0020 0035 0020 0038
Imports System.Text

Module Example
    Public Sub Main()
        ' Get an encoding for code page 1252 (Western Europe character set).
        Dim cp1252 As Encoding = Encoding.GetEncoding(1252)

        ' Define and display a string.
        Dim str As String = String.Format("{0} {1} {2}", ChrW(&h24c8), ChrW(&H2075), ChrW(&h221E))
        Console.WriteLine("Original string: " + str)
        Console.Write("Code points in string: ")
        For Each ch In str
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
        Next
        Console.WriteLine()
        Console.WriteLine()

        ' Encode a Unicode string.
        Dim bytes() As Byte = cp1252.GetBytes(str)
        Console.Write("Encoded bytes: ")
        For Each byt In bytes
            Console.Write("{0:X2} ", byt)
        Next
        Console.WriteLine()
        Console.WriteLine()

        ' Decode the string.
        Dim str2 As String = cp1252.GetString(bytes)
        Console.WriteLine("String round-tripped: {0}", str.Equals(str2))
        If Not str.Equals(str2) Then
            Console.WriteLine(str2)
            For Each ch In str2
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
        End If
    End Sub
End Module
' The example displays the following output:
'       Original string: Ⓢ ⁵ ∞
'       Code points in string: 24C8 0020 2075 0020 221E
'       
'       Encoded bytes: 3F 20 35 20 38
'       
'       String round-tripped: False
'       ? 5 8
'       003F 0020 0035 0020 0038

Mapování nejvhodnější je výchozí chování objektu Encoding , který kóduje data Unicode na data znakové stránky a existují starší aplikace, které na toto chování spoléhají. Většina nových aplikací by se ale měla vyhnout nejlepšímu chování z bezpečnostních důvodů. Aplikace by například neměly zakódovat název domény s nejlepším kódováním.

Poznámka:

Můžete také implementovat vlastní mapování náhradních řešení pro kódování. Další informace naleznete v části Implementace vlastní záložní strategie .

Pokud je záložní objekt nejvhodnější pro objekt kódování, můžete při načítání Encoding objektu zvolit jinou záložní strategii voláním Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) nebo Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) přetížením. Následující část obsahuje příklad, který nahradí každý znak, který nelze namapovat na znakovou stránku 1252 hvězdičkou (*).

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding cp1252r = Encoding.GetEncoding(1252,
                                  new EncoderReplacementFallback("*"),
                                  new DecoderReplacementFallback("*"));

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine();

      byte[] bytes = cp1252r.GetBytes(str1);
      string str2 = cp1252r.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//       Round-trip: False
//       * * *
//       002A 0020 002A 0020 002A
Imports System.Text

Module Example
    Public Sub Main()
        Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
                                           New EncoderReplacementFallback("*"),
                                           New DecoderReplacementFallback("*"))

        Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
        Console.WriteLine(str1)
        For Each ch In str1
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
        Next
        Console.WriteLine()

        Dim bytes() As Byte = cp1252r.GetBytes(str1)
        Dim str2 As String = cp1252r.GetString(bytes)
        Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
        If Not str1.Equals(str2) Then
            Console.WriteLine(str2)
            For Each ch In str2
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
            Console.WriteLine()
        End If
    End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       Round-trip: False
'       * * *
'       002A 0020 002A 0020 002A

Náhradní náhradní náhradní

Pokud znak nemá v cílovém schématu přesnou shodu, ale neexistuje žádný vhodný znak, na který lze namapovat, může aplikace zadat náhradní znak nebo řetězec. Toto je výchozí chování dekodéru Unicode, které nahrazuje libovolnou dvoubajtů sekvenci, kterou nemůže dekódovat REPLACEMENT_CHARACTER (U+FFFD). Jedná se také o výchozí chování ASCIIEncoding třídy, které nahrazuje každý znak, který nemůže kódovat nebo dekódovat otazníkem. Následující příklad znázorňuje nahrazení znaků řetězce Unicode z předchozího příkladu. Jak ukazuje výstup, každý znak, který nelze dekódovat do bajtové hodnoty ASCII, je nahrazen 0x3F, což je kód ASCII pro otazník.

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding enc = Encoding.ASCII;

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine("\n");

      // Encode the original string using the ASCII encoder.
      byte[] bytes = enc.GetBytes(str1);
      Console.Write("Encoded bytes: ");
      foreach (var byt in bytes)
         Console.Write("{0:X2} ", byt);
      Console.WriteLine("\n");

      // Decode the ASCII bytes.
      string str2 = enc.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//
//       Encoded bytes: 3F 20 3F 20 3F
//
//       Round-trip: False
//       ? ? ?
//       003F 0020 003F 0020 003F
Imports System.Text

Module Example
    Public Sub Main()
        Dim enc As Encoding = Encoding.Ascii

        Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
        Console.WriteLine(str1)
        For Each ch In str1
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
        Next
        Console.WriteLine()
        Console.WriteLine()

        ' Encode the original string using the ASCII encoder.
        Dim bytes() As Byte = enc.GetBytes(str1)
        Console.Write("Encoded bytes: ")
        For Each byt In bytes
            Console.Write("{0:X2} ", byt)
        Next
        Console.WriteLine()
        Console.WriteLine()

        ' Decode the ASCII bytes.
        Dim str2 As String = enc.GetString(bytes)
        Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
        If Not str1.Equals(str2) Then
            Console.WriteLine(str2)
            For Each ch In str2
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
            Console.WriteLine()
        End If
    End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       
'       Encoded bytes: 3F 20 3F 20 3F
'       
'       Round-trip: False
'       ? ? ?
'       003F 0020 003F 0020 003F

.NET obsahuje EncoderReplacementFallback a DecoderReplacementFallback třídy, které nahrazují náhradní řetězec, pokud znak nemapuje přesně v kódování nebo dekódovací operaci. Ve výchozím nastavení je tento náhradní řetězec otazníkem, ale můžete volat přetížení konstruktoru třídy a zvolit jiný řetězec. Náhradní řetězec je obvykle jediný znak, i když to není požadavek. Následující příklad změní chování kodéru kódové stránky 1252 vytvořením instance EncoderReplacementFallback objektu, který používá hvězdičku (*) jako náhradní řetězec.

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding cp1252r = Encoding.GetEncoding(1252,
                                  new EncoderReplacementFallback("*"),
                                  new DecoderReplacementFallback("*"));

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine();

      byte[] bytes = cp1252r.GetBytes(str1);
      string str2 = cp1252r.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//       Round-trip: False
//       * * *
//       002A 0020 002A 0020 002A
Imports System.Text

Module Example
    Public Sub Main()
        Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
                                           New EncoderReplacementFallback("*"),
                                           New DecoderReplacementFallback("*"))

        Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
        Console.WriteLine(str1)
        For Each ch In str1
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
        Next
        Console.WriteLine()

        Dim bytes() As Byte = cp1252r.GetBytes(str1)
        Dim str2 As String = cp1252r.GetString(bytes)
        Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
        If Not str1.Equals(str2) Then
            Console.WriteLine(str2)
            For Each ch In str2
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
            Console.WriteLine()
        End If
    End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       Round-trip: False
'       * * *
'       002A 0020 002A 0020 002A

Poznámka:

Můžete také implementovat náhradní třídu pro kódování. Další informace naleznete v části Implementace vlastní záložní strategie .

Kromě OTAZNÍKU (U+003F) se znak UNICODE REPLACEMENT CHARACTER (U+FFFD) běžně používá jako náhradní řetězec, zejména při dekódování bajtů, které nelze úspěšně přeložit na znaky Unicode. Můžete si ale vybrat libovolný náhradní řetězec a může obsahovat více znaků.

Náhradní výjimka

Místo poskytnutí náhradního nebo náhradního řetězce může EncoderFallbackException kodér vyvolat, pokud nemůže zakódovat sadu znaků, a dekodér může vyvolat DecoderFallbackException , pokud nemůže dekódovat bajtové pole. Chcete-li vyvolat výjimku v kódování a dekódování operací, zadejte EncoderExceptionFallback objekt a DecoderExceptionFallback objekt, v uvedeném pořadí, Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metodě. Následující příklad znázorňuje náhradní výjimku s ASCIIEncoding třídou.

using System;
using System.Text;

public class Example
{
   public static void Main()
   {
      Encoding enc = Encoding.GetEncoding("us-ascii",
                                          new EncoderExceptionFallback(),
                                          new DecoderExceptionFallback());

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      foreach (var ch in str1)
         Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

      Console.WriteLine("\n");

      // Encode the original string using the ASCII encoder.
      byte[] bytes = {};
      try {
         bytes = enc.GetBytes(str1);
         Console.Write("Encoded bytes: ");
         foreach (var byt in bytes)
            Console.Write("{0:X2} ", byt);

         Console.WriteLine();
      }
      catch (EncoderFallbackException e) {
         Console.Write("Exception: ");
         if (e.IsUnknownSurrogate())
            Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.",
                              Convert.ToUInt16(e.CharUnknownHigh),
                              Convert.ToUInt16(e.CharUnknownLow),
                              e.Index);
         else
            Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
                              Convert.ToUInt16(e.CharUnknown),
                              e.Index);
         return;
      }
      Console.WriteLine();

      // Decode the ASCII bytes.
      try {
         string str2 = enc.GetString(bytes);
         Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
         if (! str1.Equals(str2)) {
            Console.WriteLine(str2);
            foreach (var ch in str2)
               Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

            Console.WriteLine();
         }
      }
      catch (DecoderFallbackException e) {
         Console.Write("Unable to decode byte(s) ");
         foreach (byte unknown in e.BytesUnknown)
            Console.Write("0x{0:X2} ");

         Console.WriteLine("at index {0}", e.Index);
      }
   }
}
// The example displays the following output:
//       Ⓢ ⁵ ∞
//       24C8 0020 2075 0020 221E
//
//       Exception: Unable to encode 0x24C8 at index 0.
Imports System.Text

Module Example
    Public Sub Main()
        Dim enc As Encoding = Encoding.GetEncoding("us-ascii",
                                                   New EncoderExceptionFallback(),
                                                   New DecoderExceptionFallback())

        Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
        Console.WriteLine(str1)
        For Each ch In str1
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
        Next
        Console.WriteLine()
        Console.WriteLine()

        ' Encode the original string using the ASCII encoder.
        Dim bytes() As Byte = {}
        Try
            bytes = enc.GetBytes(str1)
            Console.Write("Encoded bytes: ")
            For Each byt In bytes
                Console.Write("{0:X2} ", byt)
            Next
            Console.WriteLine()
        Catch e As EncoderFallbackException
            Console.Write("Exception: ")
            If e.IsUnknownSurrogate() Then
                Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.",
                                  Convert.ToUInt16(e.CharUnknownHigh),
                                  Convert.ToUInt16(e.CharUnknownLow),
                                  e.Index)
            Else
                Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
                                  Convert.ToUInt16(e.CharUnknown),
                                  e.Index)
            End If
            Exit Sub
        End Try
        Console.WriteLine()

        ' Decode the ASCII bytes.
        Try
            Dim str2 As String = enc.GetString(bytes)
            Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
            If Not str1.Equals(str2) Then
                Console.WriteLine(str2)
                For Each ch In str2
                    Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
                Next
                Console.WriteLine()
            End If
        Catch e As DecoderFallbackException
            Console.Write("Unable to decode byte(s) ")
            For Each unknown As Byte In e.BytesUnknown
                Console.Write("0x{0:X2} ")
            Next
            Console.WriteLine("at index {0}", e.Index)
        End Try
    End Sub
End Module
' The example displays the following output:
'       Ⓢ ⁵ ∞
'       24C8 0020 2075 0020 221E
'       
'       Exception: Unable to encode 0x24C8 at index 0.

Poznámka:

Můžete také implementovat vlastní obslužnou rutinu výjimky pro operaci kódování. Další informace naleznete v části Implementace vlastní záložní strategie .

DecoderFallbackException Objekty EncoderFallbackException poskytují následující informace o podmínce, která způsobila výjimku:

EncoderFallbackException I když objekty DecoderFallbackException poskytují adekvátní diagnostické informace o výjimce, neposkytují přístup ke kódování nebo dekódovací vyrovnávací paměti. Proto neumožňují nahrazení nebo opravu neplatných dat v rámci kódování nebo dekódovací metody.

Implementace vlastní záložní strategie

Kromě mapování, které je implementované interně na kódových stránkách, obsahuje .NET následující třídy pro implementaci záložní strategie:

Kromě toho můžete implementovat vlastní řešení, které využívá náhradní, náhradní, náhradní nebo náhradní náhradní řešení, a to pomocí následujících kroků:

  1. Odvození třídy z EncoderFallback operací kódování a dekódování DecoderFallback operací.

  2. Odvození třídy z EncoderFallbackBuffer operací kódování a dekódování DecoderFallbackBuffer operací.

  3. Pro náhradní výjimku, pokud předdefinované EncoderFallbackException a DecoderFallbackException třídy nevyhovují vašim potřebám, odvodit třídu z objektu výjimky, například Exception nebo ArgumentException.

Odvození z EncoderFallback nebo DecoderFallback

Chcete-li implementovat vlastní náhradní řešení, musíte vytvořit třídu, která dědí z EncoderFallback operací kódování a dekódování DecoderFallback operací. Instance těchto tříd jsou předány Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metodě a slouží jako zprostředkovatel mezi kódovací třídou a záložní implementací.

Při vytváření vlastního náhradního řešení pro kodér nebo dekodér musíte implementovat následující členy:

Odvození z EncoderFallbackBuffer nebo DecoderFallbackBuffer

Pokud chcete implementovat vlastní náhradní řešení, musíte také vytvořit třídu, která dědí z EncoderFallbackBuffer operací kódování a dekódování DecoderFallbackBuffer operací. Instance těchto tříd jsou vráceny metodou CreateFallbackBufferEncoderFallback a DecoderFallback třídami. Metoda EncoderFallback.CreateFallbackBuffer je volána kodérem, když narazí na první znak, který není schopen kódovat, a DecoderFallback.CreateFallbackBuffer metoda je volána dekodérem, když narazí na jeden nebo více bajtů, že není schopen dekódovat. DecoderFallbackBuffer Třídy EncoderFallbackBuffer poskytují záložní implementaci. Každá instance představuje vyrovnávací paměť obsahující náhradní znaky, které nahradí znak, který nelze zakódovat, nebo sekvence bajtů, která nelze dekódovat.

Při vytváření vlastního náhradního řešení pro kodér nebo dekodér musíte implementovat následující členy:

Pokud je záložní implementace náhradní nebo náhradní náhradní, třídy odvozené z EncoderFallbackBuffer a DecoderFallbackBuffer také udržují dvě pole privátní instance: přesný počet znaků ve vyrovnávací paměti a index dalšího znaku v vyrovnávací paměti, který se má vrátit.

Příklad EncoderFallback

Předchozí příklad použil náhradní náhradní náhradu znaků Unicode, které neodpovídaly znakům ASCII hvězdičkou (*). Následující příklad místo toho používá vlastní náhradní implementaci, která nejlépe vyhovuje, aby poskytovala lepší mapování znaků bez ASCII.

Následující kód definuje třídu, CustomMapper která je odvozena z EncoderFallback toho, aby zvládla mapování nejvhodnějších znaků bez ASCII. Jeho CreateFallbackBuffer metoda vrátí CustomMapperFallbackBuffer objekt, který poskytuje implementaci EncoderFallbackBuffer . Třída CustomMapper používá Dictionary<TKey,TValue> objekt k uložení mapování nepodporovaných znaků Unicode (hodnota klíče) a odpovídajících 8bitových znaků (které jsou uloženy ve dvou po sobě jdoucích bajtech v 64bitovém celočíselném čísle). Aby bylo toto mapování k dispozici pro záložní vyrovnávací paměť, CustomMapper instance se předá jako parametr konstruktoru CustomMapperFallbackBuffer třídy. Vzhledem k tomu, že nejdelší mapování je řetězec INF znaku Unicode U+221E, MaxCharCount vrátí vlastnost 3.

public class CustomMapper : EncoderFallback
{
   public string DefaultString;
   internal Dictionary<ushort, ulong> mapping;

   public CustomMapper() : this("*")
   {
   }

   public CustomMapper(string defaultString)
   {
      this.DefaultString = defaultString;

      // Create table of mappings
      mapping = new Dictionary<ushort, ulong>();
      mapping.Add(0x24C8, 0x53);
      mapping.Add(0x2075, 0x35);
      mapping.Add(0x221E, 0x49004E0046);
   }

   public override EncoderFallbackBuffer CreateFallbackBuffer()
   {
      return new CustomMapperFallbackBuffer(this);
   }

   public override int MaxCharCount
   {
      get { return 3; }
   }
}
Public Class CustomMapper : Inherits EncoderFallback
    Public DefaultString As String
    Friend mapping As Dictionary(Of UShort, ULong)

    Public Sub New()
        Me.New("?")
    End Sub

    Public Sub New(ByVal defaultString As String)
        Me.DefaultString = defaultString

        ' Create table of mappings
        mapping = New Dictionary(Of UShort, ULong)
        mapping.Add(&H24C8, &H53)
        mapping.Add(&H2075, &H35)
        mapping.Add(&H221E, &H49004E0046)
    End Sub

    Public Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBuffer
        Return New CustomMapperFallbackBuffer(Me)
    End Function

    Public Overrides ReadOnly Property MaxCharCount As Integer
        Get
            Return 3
        End Get
    End Property
End Class

Následující kód definuje CustomMapperFallbackBuffer třídu, která je odvozena z EncoderFallbackBuffer. Slovník, který obsahuje mapování, které nejlépe vyhovuje a které je definováno v CustomMapper instanci, je k dispozici z jeho konstruktoru třídy. Jeho Fallback metoda vrátí true , pokud některý z znaků Unicode, které kodér ASCII nemůže zakódovat, jsou definovány ve slovníku mapování; jinak vrátí false. Pro každou náhradní proměnnou určuje počet znaků, které se mají vrátit, a privátní countindex proměnná označuje pozici v vyrovnávací paměti charsToReturnřetězce , další znak, který se má vrátit.

public class CustomMapperFallbackBuffer : EncoderFallbackBuffer
{
   int count = -1;                   // Number of characters to return
   int index = -1;                   // Index of character to return
   CustomMapper fb;
   string charsToReturn;

   public CustomMapperFallbackBuffer(CustomMapper fallback)
   {
      this.fb = fallback;
   }

   public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
   {
      // Do not try to map surrogates to ASCII.
      return false;
   }

   public override bool Fallback(char charUnknown, int index)
   {
      // Return false if there are already characters to map.
      if (count >= 1) return false;

      // Determine number of characters to return.
      charsToReturn = String.Empty;

      ushort key = Convert.ToUInt16(charUnknown);
      if (fb.mapping.ContainsKey(key)) {
         byte[] bytes = BitConverter.GetBytes(fb.mapping[key]);
         int ctr = 0;
         foreach (var byt in bytes) {
            if (byt > 0) {
               ctr++;
               charsToReturn += (char) byt;
            }
         }
         count = ctr;
      }
      else {
         // Return default.
         charsToReturn = fb.DefaultString;
         count = 1;
      }
      this.index = charsToReturn.Length - 1;

      return true;
   }

   public override char GetNextChar()
   {
      // We'll return a character if possible, so subtract from the count of chars to return.
      count--;
      // If count is less than zero, we've returned all characters.
      if (count < 0)
         return '\u0000';

      this.index--;
      return charsToReturn[this.index + 1];
   }

   public override bool MovePrevious()
   {
      // Original: if count >= -1 and pos >= 0
      if (count >= -1) {
         count++;
         return true;
      }
      else {
         return false;
      }
   }

   public override int Remaining
   {
      get { return count < 0 ? 0 : count; }
   }

   public override void Reset()
   {
      count = -1;
      index = -1;
   }
}
Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBuffer

    Dim count As Integer = -1        ' Number of characters to return
    Dim index As Integer = -1        ' Index of character to return
    Dim fb As CustomMapper
    Dim charsToReturn As String

    Public Sub New(ByVal fallback As CustomMapper)
        MyBase.New()
        Me.fb = fallback
    End Sub

    Public Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char, ByVal index As Integer) As Boolean
        ' Do not try to map surrogates to ASCII.
        Return False
    End Function

    Public Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean
        ' Return false if there are already characters to map.
        If count >= 1 Then Return False

        ' Determine number of characters to return.
        charsToReturn = String.Empty

        Dim key As UShort = Convert.ToUInt16(charUnknown)
        If fb.mapping.ContainsKey(key) Then
            Dim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key))
            Dim ctr As Integer
            For Each byt In bytes
                If byt > 0 Then
                    ctr += 1
                    charsToReturn += Chr(byt)
                End If
            Next
            count = ctr
        Else
            ' Return default.
            charsToReturn = fb.DefaultString
            count = 1
        End If
        Me.index = charsToReturn.Length - 1

        Return True
    End Function

    Public Overrides Function GetNextChar() As Char
        ' We'll return a character if possible, so subtract from the count of chars to return.
        count -= 1
        ' If count is less than zero, we've returned all characters.
        If count < 0 Then Return ChrW(0)

        Me.index -= 1
        Return charsToReturn(Me.index + 1)
    End Function

    Public Overrides Function MovePrevious() As Boolean
        ' Original: if count >= -1 and pos >= 0
        If count >= -1 Then
            count += 1
            Return True
        Else
            Return False
        End If
    End Function

    Public Overrides ReadOnly Property Remaining As Integer
        Get
            Return If(count < 0, 0, count)
        End Get
    End Property

    Public Overrides Sub Reset()
        count = -1
        index = -1
    End Sub
End Class

Následující kód pak vytvoří instanci objektu CustomMapper a předá instanci do Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metody. Výstup označuje, že nejlepší náhradní implementace úspěšně zpracovává tři znaky jiné než ASCII v původním řetězci.

using System;
using System.Collections.Generic;
using System.Text;

class Program
{
   static void Main()
   {
      Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback());

      string str1 = "\u24C8 \u2075 \u221E";
      Console.WriteLine(str1);
      for (int ctr = 0; ctr <= str1.Length - 1; ctr++) {
         Console.Write("{0} ", Convert.ToUInt16(str1[ctr]).ToString("X4"));
         if (ctr == str1.Length - 1)
            Console.WriteLine();
      }
      Console.WriteLine();

      // Encode the original string using the ASCII encoder.
      byte[] bytes = enc.GetBytes(str1);
      Console.Write("Encoded bytes: ");
      foreach (var byt in bytes)
         Console.Write("{0:X2} ", byt);

      Console.WriteLine("\n");

      // Decode the ASCII bytes.
      string str2 = enc.GetString(bytes);
      Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
      if (! str1.Equals(str2)) {
         Console.WriteLine(str2);
         foreach (var ch in str2)
            Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

         Console.WriteLine();
      }
   }
}
Imports System.Text
Imports System.Collections.Generic

Module Module1

    Sub Main()
        Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New CustomMapper(), New DecoderExceptionFallback())

        Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&H24C8), ChrW(&H2075), ChrW(&H221E))
        Console.WriteLine(str1)
        For ctr As Integer = 0 To str1.Length - 1
            Console.Write("{0} ", Convert.ToUInt16(str1(ctr)).ToString("X4"))
            If ctr = str1.Length - 1 Then Console.WriteLine()
        Next
        Console.WriteLine()

        ' Encode the original string using the ASCII encoder.
        Dim bytes() As Byte = enc.GetBytes(str1)
        Console.Write("Encoded bytes: ")
        For Each byt In bytes
            Console.Write("{0:X2} ", byt)
        Next
        Console.WriteLine()
        Console.WriteLine()

        ' Decode the ASCII bytes.
        Dim str2 As String = enc.GetString(bytes)
        Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
        If Not str1.Equals(str2) Then
            Console.WriteLine(str2)
            For Each ch In str2
                Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
            Next
            Console.WriteLine()
        End If
    End Sub
End Module

Viz také