Jak używać klas kodowania znaków w programie .NET

W tym artykule wyjaśniono, jak używać klas, które platforma .NET zapewnia do kodowania i dekodowania tekstu przy użyciu różnych schematów kodowania. W instrukcjach przyjęto założenie, że wprowadzono wprowadzenie do kodowania znaków w programie .NET.

Kodery i dekodery

Platforma .NET udostępnia klasy kodowania, które kodują i dekodują tekst przy użyciu różnych systemów kodowania. Na przykład UTF8Encoding Klasa opisuje reguły kodowania do i dekodowania z, UTF-8. Platforma .NET używa kodowania UTF-16 (reprezentowanego przez UnicodeEncoding klasę) dla string wystąpień. Kodery i dekodery są dostępne dla innych schematów kodowania.

Kodowanie i dekodowanie może również obejmować walidację. Na przykład UnicodeEncoding Klasa sprawdza wszystkie char wystąpienia w zakresie surogatu, aby upewnić się, że znajdują się one w prawidłowych parach zastępczych. Strategia rezerwowa określa, jak koder obsługuje nieprawidłowe znaki lub jak dekoder obsługuje nieprawidłowe bajty.

Ostrzeżenie

Klasy kodowania .NET umożliwiają przechowywanie i konwertowanie danych znakowych. Nie powinny być używane do przechowywania danych binarnych w postaci ciągów. W zależności od użytego kodowania Konwertowanie danych binarnych na format ciągu z klasami kodowania może spowodować nieoczekiwane zachowanie i generować niedokładne lub uszkodzone dane. Aby przekonwertować dane binarne na postać ciągu, użyj Convert.ToBase64String metody.

Wszystkie klasy kodowania znaków w programie .NET dziedziczą z System.Text.Encoding klasy, która jest klasą abstrakcyjną, która definiuje funkcje wspólne dla wszystkich kodowań znaków. Aby uzyskać dostęp do poszczególnych obiektów kodowania wdrożonych w programie .NET, wykonaj następujące czynności:

  • Użyj właściwości statycznych Encoding klasy, które zwracają obiekty reprezentujące standardowe kodowanie znaków dostępne w .NET (ASCII, UTF-7, UTF-8, UTF-16 i UTF-32). Na przykład Encoding.Unicode Właściwość zwraca UnicodeEncoding obiekt. Każdy obiekt używa powrotu zamiennej do obsługi ciągów, których nie można kodować, i bajtów, których nie można zdekodować. Aby uzyskać więcej informacji, zobacz rezerwowe zastąpienie.

  • Wywołaj konstruktora klasy kodowania. W ten sposób można utworzyć wystąpienie obiektów dla kodowania ASCII, UTF-7, UTF-8, UTF-16 i UTF-32. Domyślnie każdy obiekt używa powrotu zamiennej do obsługi ciągów, których nie można kodować, i bajtów, których nie można zdekodować, ale można określić, że zamiast tego ma zostać zgłoszony wyjątek. Aby uzyskać więcej informacji, zobacz rezerwowe rezerwy i wyjątki.

  • Wywołaj Encoding(Int32) konstruktora i przekaż go do liczby całkowitej, która reprezentuje kodowanie. Obiekty kodowania standardowego używają rezerw zamiennych, a strony kodowej i dwubajtowego zestawu znaków (DBCS) używają najlepiej dopasowanej rezerwy do obsługi ciągów, których nie można kodować, i bajtów, których nie można zdekodować. Aby uzyskać więcej informacji, zobacz najlepiej dopasowany powrotu.

  • Wywołaj Encoding.GetEncoding metodę, która zwraca dowolne kodowanie standardowe, strony kodowej lub DBCS dostępne w programie .NET. Przeciążenia umożliwiają określenie obiektu rezerwowego dla kodera i dekodera.

Możesz pobrać informacje o wszystkich kodowaniach dostępnych w programie .NET, wywołując Encoding.GetEncodings metodę. Platforma .NET obsługuje schematy kodowania znaków wymienione w poniższej tabeli.

Klasa kodowania Opis
ASCII Koduje ograniczony zakres znaków przy użyciu krótszych siedmiu bitów bajtu. Ponieważ to kodowanie obsługuje tylko wartości znakowe z U + 0000 za pośrednictwem U + 007F, w większości przypadków nie jest to wystarczające dla międzynarodowych aplikacji.
UTF-7 Reprezentuje znaki jako sekwencje 7-bitowego znaków ASCII. Znaki Unicode inne niż ASCII są reprezentowane przez sekwencję ucieczki znaków ASCII. UTF-7 obsługuje protokoły takie jak wiadomości e-mail i grupy dyskusyjne. Jednak kodowanie UTF-7 nie jest szczególnie bezpieczne ani niezawodne. W niektórych przypadkach zmiana jednego bitu może radykalnie zmienić interpretację całego ciągu UTF-7. W innych przypadkach, różne ciągi UTF-7 mogą kodować ten sam tekst. W przypadku sekwencji, które zawierają znaki inne niż ASCII, UTF-7 wymaga więcej miejsca niż UTF-8, a Kodowanie/dekodowanie jest wolniejsze. W związku z tym należy zamiast tego użyć formatu UTF-8, jeśli jest to możliwe.
UTF-8 Reprezentuje każdy punkt kodu Unicode jako sekwencję od 1 do 4 bajtów. UTF-8 obsługuje 8-bitowe rozmiary danych i współpracuje z wieloma istniejącymi systemami operacyjnymi. W przypadku zakresu znaków ASCII UTF-8 jest taka sama jak kodowanie ASCII i umożliwia szerszego zestawu znaków. Jednak w przypadku skryptów języka chińskiego-japońskiego (CJK) UTF-8 może wymagać trzech bajtów dla każdego znaku i może spowodować większe rozmiary danych niż UTF-16. Czasami ilość danych ASCII, takich jak tagi HTML, uzasadnia zwiększony rozmiar dla zakresu CJK.
UTF-16 Reprezentuje każdy punkt kodu Unicode jako sekwencję jednej lub 2 16-bitowej liczby całkowitej. Najpopularniejsze znaki Unicode wymagają tylko jednego punktu kodu UTF-16, chociaż znaki dodatkowe Unicode (U + 10000 i nowsze) wymagają dwóch zastępczych punktów kodowych w formacie UTF-16. Obsługiwane są zarówno kolejność bajtów little-endian, jak i big-endian. Kodowanie UTF-16 jest używane przez środowisko uruchomieniowe języka wspólnego do reprezentowania Char i String wartości, i jest używane przez system operacyjny Windows do reprezentowania WCHAR wartości.
UTF-32 Reprezentuje każdy punkt kodu Unicode jako 32-bitową liczbę całkowitą. Obsługiwane są zarówno kolejność bajtów little-endian, jak i big-endian. Kodowanie UTF-32 jest używane, gdy aplikacje chcą uniknąć zachowania surogatu kodu UTF-16 w systemach operacyjnych, w których zakodowane miejsce jest zbyt ważne. Pojedyncze glify renderowane na ekranie mogą wciąż być kodowane przy użyciu więcej niż jednego znaku UTF-32.
Kodowanie ANSI/ISO Zapewnia obsługę różnych stron kodowych. W systemach operacyjnych Windows strony kodowe są używane do obsługi określonego języka lub grupy języków. W przypadku tabeli, która wyświetla listę stron kodowych obsługiwanych przez platformę .NET, zobacz Encoding Klasa. Można pobrać obiekt kodowania dla konkretnej strony kodowej, wywołując Encoding.GetEncoding(Int32) metodę. Strona kodowa zawiera 256 punktów kodu i jest zależna od zera. W większości stron kodowych od 0 do 127 reprezentuje zestaw znaków ASCII, a punkty kodów 128 przez 255 różnią się znacząco między stronami kodowymi. Na przykład strona kodowa 1252 zawiera znaki dla systemów pisania łacińskiego, w tym angielski, niemiecki i francuski. Ostatnie 128 punktów kodowych na stronie kodowej 1252 zawierają znaki akcentu. Strona kodowa 1253 zawiera kody znaków, które są wymagane w greckim systemie pisania. Ostatnie 128 punktów kodu na stronie kodowej 1253 zawierają znaki greckie. W związku z tym aplikacja, która opiera się na stronach kodowych ANSI, nie może przechowywać greckich i niemieckich w tym samym strumieniu tekstowym, chyba że zawiera identyfikator, który wskazuje na stronę kodową, do której się odwołuje.
Kodowanie zestawów znaków dwubajtowych (DBCS) Obsługuje języki, takie jak chiński, japoński i koreański, które zawierają więcej niż 256 znaków. W DBCS para punktów kodu (dwubajtowy) reprezentuje każdy znak. Encoding.IsSingleByteWłaściwość zwraca false dla kodowań DBCS. Można pobrać obiekt kodowania dla określonego zestawu DBCS, wywołując Encoding.GetEncoding(Int32) metodę. Gdy aplikacja obsługuje dane DBCS, pierwszy bajt znaku dwubajtowego (bajt wiodący) jest przetwarzany w połączeniu z bajtem końcowym, który natychmiast następuje po nim. Ponieważ pojedyncza para dwubajtowych punktów kodowych może reprezentować różne znaki w zależności od strony kodowej, schemat ten nadal nie pozwala na połączenie dwóch języków, takich jak japoński i chiński, w tym samym strumieniu danych.

Te kodowania umożliwiają współdziałanie z znakami Unicode, a także z kodowaniem najczęściej używanym w starszych aplikacjach. Ponadto można utworzyć niestandardowe kodowanie, definiując klasę, która dziedziczy z Encoding i zastępując jej składowe.

Obsługa kodowania .NET Core

Domyślnie platforma .NET Core nie udostępnia żadnych kodowań stron kodowych innych niż strona kodowa 28591 i kodowanie Unicode, takie jak UTF-8 i UTF-16. Można jednak dodać kodowanie stron kodowych Znalezione w standardowych aplikacjach systemu Windows, które są przeznaczone dla platformy .NET dla aplikacji. Aby uzyskać więcej informacji, zobacz CodePagesEncodingProvider temat.

Wybieranie klasy kodowania

Jeśli masz możliwość wybrania kodowania, które ma być używane przez aplikację, użyj kodowania Unicode, najlepiej UTF8Encoding lub UnicodeEncoding . (Platforma .NET obsługuje także trzecie kodowanie Unicode, UTF32Encoding ).

Jeśli planujesz użycie kodowania ASCII ( ASCIIEncoding ), wybierz UTF8Encoding zamiast tego. Dwa kodowania są identyczne dla zestawu znaków ASCII, ale UTF8Encoding ma następujące zalety:

  • Może reprezentować każdy znak Unicode, który ASCIIEncoding obsługuje tylko wartości znaków Unicode między u + 0000 i u + 007F.

  • Zapewnia wykrywanie błędów i lepsze zabezpieczenia.

  • Został dostrojony tak szybko, jak to możliwe i powinien być szybszy niż inne kodowanie. Nawet w przypadku zawartości, która jest całkowicie ASCII, operacje wykonywane przy użyciu programu UTF8Encoding są szybsze niż operacje wykonywane przy użyciu programu ASCIIEncoding .

Należy rozważyć użycie ASCIIEncoding tylko w przypadku starszych aplikacji. Jednak nawet w przypadku starszych aplikacji UTF8Encoding może być lepszym wyborem z następujących powodów (przy założeniu ustawień domyślnych):

  • Jeśli aplikacja ma zawartość, która nie jest ściśle ASCII i koduje ją z ASCIIEncoding , każdy znak inny niż ASCII jest kodowany jako znak zapytania (?). Jeśli aplikacja następnie dekoduje te dane, informacje zostaną utracone.

  • Jeśli aplikacja ma zawartość, która nie jest ściśle ASCII i koduje ją z UTF8Encoding , wynik wydaje się niezrozumiały, jeśli jest interpretowany jako ASCII. Jeśli jednak aplikacja używa dekodera UTF-8, aby zdekodować te dane, dane są wykonywane pomyślnie.

W aplikacji sieci Web znaki wysyłane do klienta w odpowiedzi na żądanie sieci Web powinny odzwierciedlać kodowanie używane na kliencie. W większości przypadków należy ustawić HttpResponse.ContentEncoding Właściwość na wartość zwracaną przez HttpRequest.ContentEncoding Właściwość, aby wyświetlić tekst w kodowaniu, którego oczekuje użytkownik.

Używanie obiektu kodowania

Koder konwertuje ciąg znaków (najczęściej są to znaki Unicode) na jego odpowiednik liczbowy (Byte). Można na przykład użyć kodera ASCII do przekonwertowania znaków Unicode na ASCII, aby można było je wyświetlić w konsoli. Aby przeprowadzić konwersję, należy wywołać Encoding.GetBytes metodę. Jeśli chcesz określić liczbę bajtów potrzebnych do przechowywania zakodowanych znaków przed wykonaniem kodowania, możesz wywołać GetByteCount metodę.

W poniższym przykładzie zastosowano tablicę jednobajtową do kodowania ciągów w dwóch oddzielnych operacjach. Zachowuje indeks wskazujący pozycję początkową w tablicy bajtów dla następnego zestawu bajtów zakodowanych w formacie ASCII. Wywołuje metodę, ASCIIEncoding.GetByteCount(String) Aby upewnić się, że tablica bajtów jest wystarczająco duża, aby pomieścić zakodowany ciąg. Następnie wywołuje metodę w ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) celu zakodowania znaków w ciągu.

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.

Dekoder konwertuje tablicę bajtową, która odzwierciedla określony kodowanie znaków w zestawie znaków, w tablicy znaków lub w ciągu. Aby zdekodować tablicę bajtową do tablicy znaków, należy wywołać Encoding.GetChars metodę. Aby zdekodować tablicę bajtową do ciągu, należy wywołać GetString metodę. Jeśli chcesz określić liczbę znaków potrzebną do przechowywania zdekodowanych bajtów przed wykonaniem dekodowania, możesz wywołać GetCharCount metodę.

Poniższy przykład koduje trzy ciągi, a następnie dekoduje je w postaci pojedynczej tablicy znaków. Zachowuje indeks wskazujący pozycję początkową w tablicy znaków dla następnego zestawu zdekodowanych znaków. Wywołuje metodę, GetCharCount Aby upewnić się, że tablica znaków jest wystarczająco duża, aby pomieścić wszystkie znaki zdekodowane. Następnie wywołuje metodę, ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) Aby zdekodować tablicę bajtów.

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 kodowania i dekodowania klasy pochodnej Encoding są przeznaczone do pracy nad kompletnym zestawem danych. to oznacza, że wszystkie dane, które mają zostać zakodowane lub zdekodowane, są dostarczane w jednym wywołaniu metody. Jednak w niektórych przypadkach dane są dostępne w strumieniu, a dane, które mają być kodowane lub dekodowane, mogą być dostępne tylko z oddzielnych operacji odczytu. Wymaga to kodowania lub dekodowania operacji, aby zapamiętać każdy zapisany stan w poprzednim wywołaniu. Metody klas pochodnych Encoder i Decoder są w stanie obsługiwać operacje kodowania i dekodowania obejmujące wiele wywołań metod.

EncoderObiekt dla określonego kodowania jest dostępny we właściwości tego kodowania Encoding.GetEncoder . DecoderObiekt dla określonego kodowania jest dostępny we właściwości tego kodowania Encoding.GetDecoder . W przypadku operacji dekodowania należy zauważyć, że klasy pochodne z Decoder zawierają Decoder.GetChars metodę, ale nie mają metody odpowiadającej Encoding.GetString .

Poniższy przykład ilustruje różnicę między użyciem Encoding.GetString Decoder.GetChars metod i do dekodowania tablicy bajtowej Unicode. Przykład koduje ciąg, który zawiera znaki Unicode do pliku, a następnie używa dwóch metod dekodowania do zdekodowania ich dziesięć bajtów jednocześnie. Ponieważ para dwuskładnikowa występuje w dziesiątych i jedenastych bajtach, jest dekodowane w oddzielnych wywołaniach metod. Ponieważ dane wyjściowe są wyświetlane, Encoding.GetString Metoda nie jest w stanie poprawnie zdekodować bajtów i zamiast tego zastępuje je literałem U + FFFD (znak zastępczy). Z drugiej strony Decoder.GetChars Metoda może pomyślnie zdekodować tablicę bajtową, aby uzyskać oryginalny ciąg.

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

Wybór strategii rezerwowej

Gdy metoda próbuje zakodować lub zdekodować znak, ale nie istnieje żadne mapowanie, musi zaimplementować strategię rezerwową, która określa sposób obsługi mapowania zakończonego niepowodzeniem. Istnieją trzy typy strategii powrotu:

  • Dopasowanie najlepszego dopasowania

  • Alternatywa wymiany

  • Rezerwa wyjątku

Ważne

Najczęstsze problemy w operacjach kodowania są wykonywane, gdy znak Unicode nie może zostać zmapowany na konkretne kodowanie strony kodowej. Najczęstsze problemy związane z dekodowaniem są wykonywane, gdy nieprawidłowe sekwencje bajtów nie mogą być przetłumaczone na prawidłowe znaki Unicode. Z tego względu należy wiedzieć, która strategia rezerwowa używa danego obiektu kodowania. Zawsze, gdy jest to możliwe, należy określić strategię rezerwową używaną przez obiekt kodowania podczas tworzenia wystąpienia obiektu.

Best-Fit rezerwowy

Gdy znak nie ma dokładnego dopasowania w kodowaniu docelowym, koder może próbować zmapować go do podobnego znaku. (Najlepszym dopasowaniem jest raczej kodowanie, a nie problem z dekodowaniem. Istnieje kilka stron kodowych, które zawierają znaki, których nie można pomyślnie zmapować na Unicode.) Najlepszym dopasowaniem jest wartość domyślna dla kodowania strony kodowej i dwubajtowego zestawu znaków, które są pobierane przez Encoding.GetEncoding(Int32) przeciążenia i Encoding.GetEncoding(String) .

Uwaga

Teoretycznie, klasy kodowania Unicode zapewniane w .NET ( UTF8Encoding , UnicodeEncoding i UTF32Encoding ) obsługują każdy znak w każdym zestawie znaków, dzięki czemu mogą one być używane w celu wyeliminowania najbardziej pasujących problemów powrotu.

Najlepiej dopasowane strategie różnią się w zależności od stron kodowych. Na przykład w przypadku niektórych stron kodowych znaki łacińskie o pełnej szerokości są mapowane na bardziej popularne znaki łacińskie połówkowej szerokości. W przypadku innych stron kodowych mapowanie nie jest wykonywane. Nawet w ramach agresywnej strategii najlepiej dopasowanej nie ma żadnych znaków w przypadku niektórych kodowań. Na przykład, ideograph chiński nie ma uzasadnionego mapowania na stronę kodową 1252. W takim przypadku używany jest ciąg zastępczy. Domyślnie ten ciąg jest tylko pojedynczym ZNAKiem zapytania (U + 003F).

Uwaga

Strategie najlepiej dopasowane nie są udokumentowane szczegółowo. Jednak kilka stron kodowych są udokumentowane w witrynie sieci Web konsorcjum Unicode . Zapoznaj się z plikiem readme.txt w tym folderze, aby uzyskać opis sposobu interpretacji plików mapowania.

Poniższy przykład używa strony kodowej 1252 (strona kodowa systemu Windows dla języków zachodnich Europy) do zilustrowania najlepszego mapowania i jego wad. Encoding.GetEncoding(Int32)Metoda jest używana do pobierania obiektu kodowania dla strony kodowej 1252. Domyślnie używa mapowania najlepszego dopasowania dla znaków Unicode, które nie są obsługiwane. Przykład tworzy wystąpienie ciągu, który zawiera trzy znaki inne niż ASCII — WYRÓŻNIONe Wielka litera S (U + 24C8), indeks GÓRNy 5 (U + 2075) i NIESKOŃCZONość (U + 221E) — rozdzielone spacjami. Jako dane wyjściowe z przykładu pokazują, gdy ciąg jest zakodowany, trzy oryginalne znaki niebędące znakami spacji są zastępowane na podstawie znaku zapytania (U + 003F), cyfry pięć (U + 0035) i cyfry osiem (U + 0038). CYFRa osiem to szczególnie słabe zastąpienie dla nieobsługiwanego znaku NIESKOŃCZONości, a znak zapytania wskazuje, że dla oryginalnego znaku nie jest dostępne żadne mapowanie.

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

Najwygodniejsze mapowanie jest domyślnym zachowaniem dla Encoding obiektu, który koduje dane Unicode na dane strony kodowej, a istnieją starsze aplikacje korzystające z tego zachowania. Jednak większość nowych aplikacji powinna unikać optymalnego zachowania ze względów bezpieczeństwa. Na przykład aplikacje nie powinny umieszczać nazwy domeny za pomocą najlepszego dopasowania kodowania.

Uwaga

Możesz również zaimplementować niestandardowe mapowanie rezerwy najlepszego dopasowania dla kodowania. Aby uzyskać więcej informacji, zobacz sekcję implementowanie niestandardowej strategii rezerwowej .

Jeśli jest to wartość domyślna dla obiektu Encoding, można wybrać inną strategię rezerwową podczas pobierania Encoding obiektu przez wywołanie metody Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) lub Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) przeciążenia. Poniższa sekcja zawiera przykład zastępujący każdy znak, którego nie można zamapować na stronę kodową 1252 przy użyciu gwiazdki (*).

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

Alternatywa wymiany

Gdy znak nie ma dokładnego dopasowania w schemacie docelowym, ale nie ma odpowiedniego znaku, do którego można zamapować, aplikacja może określić znak zastępczy lub ciąg. Jest to domyślne zachowanie dla dekodera Unicode, który zastępuje dowolną dwubajtową sekwencję, której nie można zdekodować przy użyciu REPLACEMENT_CHARACTER (U + FFFD). Jest to również domyślne zachowanie ASCIIEncoding klasy, która zastępuje każdy znak, którego nie można kodować ani zdekodować ze znakiem zapytania. Poniższy przykład ilustruje zastąpienie znaków w ciągu Unicode z poprzedniego przykładu. Ponieważ dane wyjściowe są wyświetlane, każdy znak, którego nie można zdekodować do wartości bajtowej ASCII, jest zastępowany przez 0x3F, który jest kodem ASCII dla znaku zapytania.

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

Platforma .NET zawiera EncoderReplacementFallback DecoderReplacementFallback klasy i, które zastępują ciąg zamienny, jeśli znak nie jest mapowany dokładnie w operacji kodowania lub dekodowania. Domyślnie ten ciąg zamienny jest znakiem zapytania, ale można wywołać przeciążenie konstruktora klasy w celu wybrania innego ciągu. Zazwyczaj ciąg zamienny jest pojedynczym znakiem, chociaż nie jest to wymagane. Poniższy przykład zmienia zachowanie kodera 1252 strony kodowej przez utworzenie wystąpienia EncoderReplacementFallback obiektu, który używa gwiazdki (*) jako ciągu zastępczego.

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

Uwaga

Można również zaimplementować klasę zastępczą dla kodowania. Aby uzyskać więcej informacji, zobacz sekcję implementowanie niestandardowej strategii rezerwowej .

Oprócz znaku zapytania (U + 003F) znak ZASTĘPCZy Unicode (U + FFFD) jest często używany jako ciąg zamienny, szczególnie w przypadku dekodowania sekwencji bajtów, których nie można pomyślnie przetłumaczyć na znaki Unicode. Można jednak wybrać dowolny ciąg zamienny, który może zawierać wiele znaków.

Rezerwa wyjątku

Zamiast podawania najlepszego dopasowania lub ciągu zamiennego, koder może zgłosić, EncoderFallbackException Jeśli nie jest w stanie zakodować zestawu znaków, a dekoder może zgłosić, DecoderFallbackException Jeśli nie można zdekodować tablicy bajtów. Aby zgłosić wyjątek w operacjach kodowania i dekodowania, należy dostarczyć EncoderExceptionFallback obiekt i DecoderExceptionFallback obiekt odpowiednio do Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metody. Poniższy przykład ilustruje rezerwę wyjątku z ASCIIEncoding klasą.

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.

Uwaga

Można również zaimplementować niestandardową procedurę obsługi wyjątków dla operacji kodowania. Aby uzyskać więcej informacji, zobacz sekcję implementowanie niestandardowej strategii rezerwowej .

EncoderFallbackExceptionObiekty i DecoderFallbackException zapewniają następujące informacje o stanie, który spowodował wyjątek:

Chociaż EncoderFallbackException obiekty i DecoderFallbackException zapewniają odpowiednie informacje diagnostyczne o wyjątku, nie zapewniają dostępu do buforu kodowania lub dekodowania. W związku z tym nie zezwalają na zamienianie lub korygowanie nieprawidłowych danych w ramach metody kodowania lub dekodowania.

Implementowanie niestandardowej strategii rezerwowej

Oprócz najlepszego mapowania, które jest implementowane wewnętrznie przez strony kodowe, platforma .NET zawiera następujące klasy do implementowania strategii rezerwowej:

Ponadto można zaimplementować niestandardowe rozwiązanie, które używa najlepszego dopasowania powrotu, powrotu zamiennej lub powrotu do wyjątków, wykonując następujące czynności:

  1. Utwórz klasę z EncoderFallback dla operacji kodowania i z DecoderFallback dla operacji dekodowania.

  2. Utwórz klasę z EncoderFallbackBuffer dla operacji kodowania i z DecoderFallbackBuffer dla operacji dekodowania.

  3. W przypadku powrotu wyjątku, jeśli wstępnie zdefiniowane EncoderFallbackException i DecoderFallbackException klasy nie spełniają Twoich potrzeb, należy utworzyć klasę z obiektu wyjątku, takiego jak Exception lub ArgumentException .

Wyprowadzanie z EncoderFallback lub DecoderFallback

Aby zaimplementować niestandardowe rozwiązanie alternatywne, należy utworzyć klasę, która dziedziczy z EncoderFallback dla operacji kodowania i z DecoderFallback dla operacji dekodowania. Wystąpienia tych klas są przesyłane do Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metody i służą jako pośrednik między klasą kodowania a implementacją rezerwową.

W przypadku tworzenia niestandardowego rozwiązania alternatywnego dla kodera lub dekodera należy zaimplementować następujące elementy członkowskie:

Wyprowadzanie z EncoderFallbackBuffer lub DecoderFallbackBuffer

Aby zaimplementować niestandardowe rozwiązanie alternatywne, należy również utworzyć klasę, która dziedziczy z EncoderFallbackBuffer dla operacji kodowania i z DecoderFallbackBuffer dla operacji dekodowania. Wystąpienia tych klas są zwracane przez CreateFallbackBuffer metodę EncoderFallback DecoderFallback klas i. EncoderFallback.CreateFallbackBufferMetoda jest wywoływana przez koder, gdy napotka pierwszy znak, który nie jest w stanie kodować, a DecoderFallback.CreateFallbackBuffer Metoda jest wywoływana przez dekoder, gdy napotka jeden lub więcej bajtów, których nie można zdekodować. EncoderFallbackBufferKlasy i DecoderFallbackBuffer zapewniają implementację rezerwową. Każde wystąpienie reprezentuje bufor, który zawiera znaki rezerwowe, które zastąpią znak, którego nie można zakodować lub nie można zdekodować sekwencji bajtów.

W przypadku tworzenia niestandardowego rozwiązania alternatywnego dla kodera lub dekodera należy zaimplementować następujące elementy członkowskie:

Jeśli implementacja rezerwowa jest najlepszym dopasowaną rezerwą lub alternatywą zamienną, klasy pochodne od EncoderFallbackBuffer i DecoderFallbackBuffer również utrzymują dwa pola wystąpienia prywatnego: dokładną liczbę znaków w buforze oraz indeks następnego znaku w buforze do zwrócenia.

Przykład EncoderFallback

W poprzednim przykładzie użyto powrotu zamiennej zastępującej znaki Unicode, które nie odpowiadają znakom ASCII z gwiazdką (*). W poniższym przykładzie zastosowano niestandardową implementację rezerwową najlepiej w celu zapewnienia lepszego mapowania znaków innych niż ASCII.

Poniższy kod definiuje klasę o nazwie CustomMapper , która pochodzi od EncoderFallback do obsługi najlepszego mapowania znaków innych niż ASCII. CreateFallbackBufferMetoda zwraca CustomMapperFallbackBuffer obiekt, który dostarcza EncoderFallbackBuffer implementację. CustomMapperKlasa używa Dictionary<TKey,TValue> obiektu do przechowywania mapowań nieobsługiwanych znaków Unicode (wartość klucza) i odpowiadających im znaków 8-bitowych (które są przechowywane w dwóch kolejnych bajtach w 64-bitowej liczbie całkowitej). Aby to mapowanie było dostępne dla buforu rezerwowego, CustomMapper wystąpienie jest przesyłane jako parametr do CustomMapperFallbackBuffer konstruktora klasy. Ponieważ najdłuższym mapowaniem jest ciąg "INF" dla znaku Unicode U + 221E, MaxCharCount Właściwość zwraca wartość 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

Poniższy kod definiuje CustomMapperFallbackBuffer klasę, która pochodzi od EncoderFallbackBuffer . Słownik zawierający najlepsze mapowania i zdefiniowany w CustomMapper wystąpieniu jest dostępny z konstruktora klasy. Jego Fallback Metoda zwraca true Jeśli którykolwiek ze znaków Unicode, które nie mogą kodować KODERa ASCII, jest zdefiniowany w słowniku mapowania; w przeciwnym razie zwraca false . Dla każdego powrotu zmienna prywatna count wskazuje liczbę znaków, która pozostała do zwrócenia, a zmienna prywatna index wskazuje pozycję w buforze ciągów, charsToReturn od następnego znaku do zwrócenia.

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

Poniższy kod powoduje utworzenie wystąpienia CustomMapper obiektu i przekazanie jego wystąpienia do Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metody. Dane wyjściowe wskazują, że Najlepsza zgodność implementacja rezerwy pomyślnie obsługuje trzy znaki inne niż ASCII w oryginalnym ciągu.

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

Zobacz także