Dela via


Använda teckenkodningsklasser i .NET

Den här artikeln beskriver hur du använder de klasser som .NET tillhandahåller för kodning och avkodning av text med hjälp av olika kodningsscheman. Anvisningarna förutsätter att du har läst Introduktion till teckenkodning i .NET.

Kodare och avkodare

.NET tillhandahåller kodningsklasser som kodar och avkodar text med hjälp av olika kodningssystem. Klassen beskriver till exempel UTF8Encoding reglerna för kodning till och avkodning från UTF-8. .NET använder UTF-16-kodning (representeras av UnicodeEncoding klassen) för string instanser. Kodare och avkodare är tillgängliga för andra kodningsscheman.

Kodning och avkodning kan också innehålla validering. Klassen kontrollerar till exempel UnicodeEncoding alla char instanser i surrogatintervallet för att se till att de är i giltiga surrogatpar. En reservstrategi avgör hur en kodare hanterar ogiltiga tecken eller hur en avkodare hanterar ogiltiga byte.

Varning

Med .NET-kodningsklasser kan du lagra och konvertera teckendata. De bör inte användas för att lagra binära data i strängform. Beroende på vilken kodning som används kan konvertering av binära data till strängformat med kodningsklasserna medföra oväntat beteende och generera felaktiga eller skadade data. Om du vill konvertera binära data till ett strängformulär använder du Convert.ToBase64String metoden .

Alla teckenkodningsklasser i .NET ärver från System.Text.Encoding klassen, vilket är en abstrakt klass som definierar de funktioner som är gemensamma för alla teckenkodningar. Om du vill komma åt de enskilda kodningsobjekten som implementeras i .NET gör du följande:

  • Använd de statiska egenskaperna för Encoding klassen, som returnerar objekt som representerar standardteckenkodningarna som är tillgängliga i .NET (ASCII, UTF-7, UTF-8, UTF-16 och UTF-32). Egenskapen Encoding.Unicode returnerar till exempel ett UnicodeEncoding objekt. Varje objekt använder ersättningsåterställning för att hantera strängar som det inte kan koda och byte som det inte kan avkoda. Mer information finns i Ersättningsåterställning.

  • Anropa kodningens klasskonstruktor. Objekt för ASCII-, UTF-7-, UTF-8-, UTF-16- och UTF-32-kodningar kan instansieras på det här sättet. Som standard använder varje objekt ersättningsåterställning för att hantera strängar som det inte kan koda och byte som det inte kan avkoda, men du kan ange att ett undantag ska genereras i stället. Mer information finns i Reserv för ersättning och Undantagsåterställning.

  • Encoding(Int32) Anropa konstruktorn och skicka det ett heltal som representerar kodningen. Standardkodningsobjekt använder ersättningsåterställning, kodsida och DBCS-kodningsobjekt (Double Byte Character Set) använder bästa möjliga återställning för att hantera strängar som de inte kan koda och byte som de inte kan avkoda. Mer information finns i Best-fit reserv.

  • Encoding.GetEncoding Anropa metoden, som returnerar valfri standard-, kodsida eller DBCS-kodning som är tillgänglig i .NET. Med överlagringar kan du ange ett reservobjekt för både kodaren och avkodaren.

Du kan hämta information om alla kodningar som är tillgängliga i .NET genom att anropa Encoding.GetEncodings metoden. .NET stöder teckenkodningsscheman som anges i följande tabell.

Kodningsklass beskrivning
ASCII Kodar ett begränsat antal tecken med hjälp av de lägre sju bitarna av en byte. Eftersom den här kodningen endast stöder teckenvärden från U+0000 och med U+007Fär den i de flesta fall otillräcklig för internationaliserade program.
UTF-7 Representerar tecken som sekvenser med 7-bitars ASCII-tecken. Unicode-tecken som inte är ASCII representeras av en escape-sekvens med ASCII-tecken. UTF-7 stöder protokoll som e-post och diskussionsgrupp. UTF-7 är dock inte särskilt säkert eller robust. I vissa fall kan en ändring av en bit radikalt ändra tolkningen av en hel UTF-7-sträng. I andra fall kan olika UTF-7-strängar koda samma text. För sekvenser som innehåller icke-ASCII-tecken kräver UTF-7 mer utrymme än UTF-8, och kodning/avkodning är långsammare. Därför bör du använda UTF-8 i stället för UTF-7 om möjligt.
UTF-8 Representerar varje Unicode-kodpunkt som en sekvens på en till fyra byte. UTF-8 stöder 8-bitars datastorlekar och fungerar bra med många befintliga operativsystem. FÖR ASCII-teckenintervallet är UTF-8 identiskt med ASCII-kodning och tillåter en bredare uppsättning tecken. Men för CJK-skript (chinese-japanese-korean) kan UTF-8 kräva tre byte för varje tecken och kan orsaka större datastorlekar än UTF-16. Ibland motiverar mängden ASCII-data, till exempel HTML-taggar, den ökade storleken för CJK-intervallet.
UTF-16 Representerar varje Unicode-kodpunkt som en sekvens med ett eller två 16-bitars heltal. De vanligaste Unicode-tecknen kräver bara en UTF-16-kodpunkt, även om Unicode-tilläggstecken (U+10000 och senare) kräver två UTF-16 surrogatkodpunkter. Både småsluts- och storslutsbytebeställningar stöds. UTF-16-kodning används av den vanliga språkkörningen för att representera Char och String värden, och den används av Windows-operativsystemet för att representera WCHAR värden.
UTF-32 Representerar varje Unicode-kodpunkt som ett 32-bitars heltal. Både småsluts- och storslutsbytebeställningar stöds. UTF-32-kodning används när program vill undvika surrogatkodpunktbeteendet för UTF-16-kodning på operativsystem för vilka kodat utrymme är för viktigt. Enstaka tecken som återges på en skärm kan fortfarande kodas med mer än ett UTF-32-tecken.
ANSI/ISO-kodning Ger stöd för en mängd olika kodsidor. I Windows-operativsystem används kodsidor för att stödja ett visst språk eller en viss grupp språk. En tabell som visar de kodsidor som stöds av .NET finns i Encoding klassen . Du kan hämta ett kodningsobjekt för en viss kodsida genom att anropa Encoding.GetEncoding(Int32) metoden. En kodsida innehåller 256 kodpunkter och är nollbaserad. På de flesta kodsidor representerar kodpunkterna 0 till 127 ASCII-teckenuppsättningen och kodpunkterna 128 till 255 skiljer sig avsevärt mellan kodsidorna. Kodsidan 1252 innehåller till exempel tecken för latinska skrivsystem, inklusive engelska, tyska och franska. De sista 128 kodpunkterna på kodsidan 1252 innehåller dekortecken. Kodsidan 1253 innehåller teckenkoder som krävs i det grekiska skrivsystemet. De sista 128 kodpunkterna på kodsidan 1253 innehåller de grekiska tecknen. Därför kan ett program som förlitar sig på ANSI-kodsidor inte lagra grekiska och tyska i samma textström om det inte innehåller en identifierare som anger den refererade kodsidan.
DBCS-kodningar (Double-Byte Character Set) Stöder språk, till exempel kinesiska, japanska och koreanska, som innehåller mer än 256 tecken. I en DBCS representerar ett par kodpunkter (en dubbel byte) varje tecken. Egenskapen Encoding.IsSingleByte returnerar false för DBCS-kodningar. Du kan hämta ett kodningsobjekt för en viss DBCS genom att anropa Encoding.GetEncoding(Int32) metoden. När ett program hanterar DBCS-data bearbetas det första bytet av ett DBCS-tecken (leadbyte) i kombination med den spårbyte som omedelbart följer den. Eftersom ett enda par kodpunkter med dubbla byte kan representera olika tecken beroende på kodsidan, tillåter det här schemat fortfarande inte kombinationen av två språk, till exempel japanska och kinesiska, i samma dataström.

Med dessa kodningar kan du arbeta med Unicode-tecken samt med kodningar som oftast används i äldre program. Dessutom kan du skapa en anpassad kodning genom att definiera en klass som härleds från Encoding och åsidosätter dess medlemmar.

Stöd för .NET Core-kodning

Som standard gör .NET Core inga andra kodningar för kodning av kodningar än kodsidan 28591 och Unicode-kodningarna tillgängliga, till exempel UTF-8 och UTF-16. Du kan dock lägga till kodning för kodning av kodningssidor som finns i Windows-standardappar som riktar in sig på .NET i din app. Mer information finns i avsnittet CodePagesEncodingProvider .

Välja en kodningsklass

Om du har möjlighet att välja den kodning som ska användas av ditt program bör du använda en Unicode-kodning, helst antingen UTF8Encoding eller UnicodeEncoding. (.NET stöder också en tredje Unicode-kodning, UTF32Encoding.)

Om du planerar att använda en ASCII-kodning (ASCIIEncoding) väljer du UTF8Encoding i stället. De två kodningarna är identiska för ASCII-teckenuppsättningen, men UTF8Encoding har följande fördelar:

  • Det kan representera varje Unicode-tecken, medan ASCIIEncoding endast stöder Unicode-teckenvärdena mellan U+0000 och U+007F.

  • Det ger felidentifiering och bättre säkerhet.

  • Den har finjusterats för att vara så snabb som möjligt och bör vara snabbare än någon annan kodning. Även för innehåll som är helt ASCII är åtgärder som utförs med UTF8Encoding snabbare än åtgärder som utförs med ASCIIEncoding.

Du bör endast överväga att använda ASCIIEncoding för äldre program. Men även för äldre program UTF8Encoding kan det vara ett bättre val av följande skäl (om du antar standardinställningar):

  • Om ditt program har innehåll som inte är strikt ASCII och kodar det med ASCIIEncodingkodas varje icke-ASCII-tecken som ett frågetecken (?). Om programmet sedan avkodar dessa data går informationen förlorad.

  • Om ditt program har innehåll som inte är strikt ASCII och kodar det med UTF8Encoding, verkar resultatet obegripligt om det tolkas som ASCII. Men om programmet sedan använder en UTF-8-avkodare för att avkoda dessa data, utför data en tur och retur-resa.

I ett webbprogram bör tecken som skickas till klienten som svar på en webbbegäran återspegla den kodning som används på klienten. I de flesta fall bör du ange HttpResponse.ContentEncoding egenskapen till det värde som returneras av HttpRequest.ContentEncoding egenskapen för att visa text i den kodning som användaren förväntar sig.

Använda ett kodningsobjekt

En kodare konverterar en sträng med tecken (oftast Unicode-tecken) till dess numeriska motsvarighet (byte). Du kan till exempel använda en ASCII-kodare för att konvertera Unicode-tecken till ASCII så att de kan visas i konsolen. Om du vill utföra konverteringen anropar Encoding.GetBytes du metoden. Om du vill ta reda på hur många byte som behövs för att lagra kodade tecken innan du utför kodningen kan du anropa GetByteCount metoden.

I följande exempel används en matris med en enda byte för att koda strängar i två separata åtgärder. Det upprätthåller ett index som anger startpositionen i bytematrisen för nästa uppsättning ASCII-kodade byte. Den anropar ASCIIEncoding.GetByteCount(String) metoden för att säkerställa att bytematrisen är tillräckligt stor för att rymma den kodade strängen. Sedan anropas ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) metoden för att koda tecknen i strängen.

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.

En avkodare konverterar en bytematris som återspeglar en viss teckenkodning till en uppsättning tecken, antingen i en teckenmatris eller i en sträng. Om du vill avkoda en bytematris till en teckenmatris anropar Encoding.GetChars du metoden. Om du vill avkoda en bytematris till en sträng anropar GetString du metoden. Om du vill fastställa hur många tecken som behövs för att lagra de avkodade byteen innan du utför avkodningen kan du anropa GetCharCount metoden.

I följande exempel kodas tre strängar och avkodas sedan till en enda matris med tecken. Det upprätthåller ett index som anger startpositionen i teckenmatrisen för nästa uppsättning avkodade tecken. Den anropar GetCharCount metoden för att säkerställa att teckenmatrisen är tillräckligt stor för att rymma alla avkodade tecken. Sedan anropas ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) metoden för att avkoda bytematrisen.

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.

Kodnings- och avkodningsmetoderna för en klass som härleds från Encoding är utformade för att fungera med en fullständig uppsättning data, det vill säga att alla data som ska kodas eller avkodas tillhandahålls i ett enda metodanrop. I vissa fall är dock data tillgängliga i en dataström, och de data som ska kodas eller avkodas kan endast vara tillgängliga från separata läsåtgärder. Detta kräver att kodnings- eller avkodningsåtgärden kommer ihåg alla sparade tillstånd från dess tidigare anrop. Metoder för klasser som härleds från Encoder och Decoder kan hantera kodnings- och avkodningsåtgärder som omfattar flera metodanrop.

Ett Encoder objekt för en viss kodning är tillgängligt från den kodningens Encoding.GetEncoder egenskap. Ett Decoder objekt för en viss kodning är tillgängligt från den kodningens Encoding.GetDecoder egenskap. Observera att klasser som härleds från Decoder innehåller en Decoder.GetChars metod för avkodningsåtgärder, men att de inte har någon metod som motsvarar Encoding.GetString.

I följande exempel visas skillnaden mellan att använda Encoding.GetString metoderna och Decoder.GetChars för att avkoda en Unicode-bytematris. Exemplet kodar en sträng som innehåller vissa Unicode-tecken till en fil och använder sedan de två avkodningsmetoderna för att avkoda dem tio byte i taget. Eftersom ett surrogatpar förekommer i den tionde och elfte byteen avkodas det i separata metodanrop. Som utdata visar Encoding.GetString kan metoden inte avkoda byteen korrekt och ersätter dem i stället med U+FFFD (REPLACEMENT CHARACTER). Å Decoder.GetChars andra sidan kan metoden avkoda bytematrisen för att hämta den ursprungliga strängen.

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

Välja en reservstrategi

När en metod försöker koda eller avkoda ett tecken men det inte finns någon mappning måste den implementera en reservstrategi som avgör hur den misslyckade mappningen ska hanteras. Det finns tre typer av reservstrategier:

  • Best-fit reserv

  • Reserv för ersättning

  • Undantagsåterställning

Viktigt!

De vanligaste problemen med kodningsåtgärder uppstår när ett Unicode-tecken inte kan mappas till en viss kodning av kodningssidor. De vanligaste problemen med avkodningsåtgärder uppstår när ogiltiga bytesekvenser inte kan översättas till giltiga Unicode-tecken. Därför bör du veta vilken reservstrategi ett visst kodningsobjekt använder. När det är möjligt bör du ange den återställningsstrategi som används av ett kodningsobjekt när du instansierar objektet.

Best-Fit Fallback

När ett tecken inte har någon exakt matchning i målkodningen kan kodaren försöka mappa det till ett liknande tecken. (Bästa möjliga reserv är främst en kodning snarare än ett avkodningsproblem. Det finns mycket få kodsidor som innehåller tecken som inte kan mappas till Unicode.) Återställning efter bästa passform är standardinställningen för kodsidan och teckenuppsättningskodningar med dubbla byte som hämtas av Encoding.GetEncoding(Int32) överlagringarna och Encoding.GetEncoding(String) .

Kommentar

I teorin stöder Unicode-kodningsklasserna i .NET (UTF8Encoding, UnicodeEncoding, och UTF32Encoding) varje tecken i varje teckenuppsättning, så att de kan användas för att eliminera problem med bästa möjliga återställning.

Strategier som passar bäst varierar för olika kodsidor. För vissa kodsidor mappas till exempel latinska tecken med full bredd till vanliga latinska tecken med halv bredd. För andra kodsidor görs inte den här mappningen. Även under en aggressiv strategi för bästa passform finns det ingen tänkbar passform för vissa karaktärer i vissa kodningar. En kinesisk ideografi har till exempel ingen rimlig mappning till kodsidan 1252. I det här fallet används en ersättningssträng. Som standard är den här strängen bara ett enda FRÅGETECKEN (U+003F).

Kommentar

Strategier som passar bäst dokumenteras inte i detalj. Flera kodsidor dokumenteras dock på Unicode Consortiums webbplats. Läs readme.txt filen i mappen för en beskrivning av hur du tolkar mappningsfilerna.

I följande exempel används kodsidan 1252 (Windows-kodsidan för västeuropeiska språk) för att illustrera bästa möjliga mappning och dess nackdelar. Metoden Encoding.GetEncoding(Int32) används för att hämta ett kodningsobjekt för kodningssidan 1252. Som standard använder den en mappning som passar bäst för Unicode-tecken som den inte stöder. Exemplet instansierar en sträng som innehåller tre icke-ASCII-tecken – CIRCLED LATIN CAPITAL LETTER S (U+24C8), SUPERSCRIPT FIVE (U+2075) och INFINITY (U+221E) – avgränsade med blanksteg. Som utdata från exemplet visar, när strängen är kodad, ersätts de tre ursprungliga icke-blankstegsteckenen med FRÅGETECKEN (U+003F), DIGIT FIVE (U+0035) och DIGIT EIGHT (U+0038). DIGIT EIGHT är en särskilt dålig ersättning för DET OÄNDLIGT tecken som inte stöds, och FRÅGETECKEN anger att ingen mappning var tillgänglig för det ursprungliga tecknet.

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

Bästa möjliga mappning är standardbeteendet för ett Encoding objekt som kodar Unicode-data till kodsidesdata, och det finns äldre program som förlitar sig på det här beteendet. De flesta nya program bör dock undvika bästa möjliga beteende av säkerhetsskäl. Till exempel bör program inte placera ett domännamn via en bäst anpassad kodning.

Kommentar

Du kan också implementera en anpassad reservmappning med bästa passform för en kodning. Mer information finns i avsnittet Implementera en anpassad återställningsstrategi .

Om bästa möjliga återställning är standard för ett kodningsobjekt kan du välja en annan återställningsstrategi när du hämtar ett Encoding objekt genom att anropa Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) eller Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) överbelasta. Följande avsnitt innehåller ett exempel som ersätter varje tecken som inte kan mappas till kodsidan 1252 med en asterisk (*).

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

Reserv för ersättning

När ett tecken inte har någon exakt matchning i målschemat, men det inte finns något lämpligt tecken som det kan mappas till, kan programmet ange ett ersättningstecken eller en sträng. Detta är standardbeteendet för Unicode-avkodaren, som ersätter alla två bytessekvenser som inte kan avkodas med REPLACEMENT_CHARACTER (U+FFFD). Det är också standardbeteendet för ASCIIEncoding klassen, som ersätter varje tecken som det inte kan koda eller avkoda med ett frågetecken. I följande exempel visas teckenersättning för Unicode-strängen från föregående exempel. Som utdata visar ersätts varje tecken som inte kan avkodas till ett ASCII-bytevärde av 0x3F, vilket är ASCII-koden för ett frågetecken.

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 innehåller klasserna EncoderReplacementFallback och DecoderReplacementFallback som ersätter en ersättningssträng om ett tecken inte mappas exakt i en kodnings- eller avkodningsåtgärd. Som standard är den här ersättningssträngen ett frågetecken, men du kan anropa en klasskonstruktoröverlagring för att välja en annan sträng. Ersättningssträngen är vanligtvis ett enda tecken, även om detta inte är ett krav. I följande exempel ändras beteendet för kodningssidan 1252 genom att instansiera ett EncoderReplacementFallback objekt som använder en asterisk (*) som ersättningssträng.

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

Kommentar

Du kan också implementera en ersättningsklass för en kodning. Mer information finns i avsnittet Implementera en anpassad återställningsstrategi .

Förutom FRÅGETECKEN (U+003F) används Unicode REPLACEMENT CHARACTER (U+FFFD) ofta som en ersättningssträng, särskilt vid avkodning av bytesekvenser som inte kan översättas till Unicode-tecken. Du kan dock välja valfri ersättningssträng och den kan innehålla flera tecken.

Undantagsåterställning

I stället för att tillhandahålla en reservdel som passar bäst eller en ersättningssträng kan en kodare utlösa en EncoderFallbackException om den inte kan koda en uppsättning tecken, och en avkodare kan utlösa en DecoderFallbackException om den inte kan avkoda en bytematris. Om du vill utlösa ett undantag i kodnings- och avkodningsåtgärder anger du ett EncoderExceptionFallback objekt respektive ett DecoderExceptionFallback objekt till Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metoden. I följande exempel visas undantagsåterställning med ASCIIEncoding klassen.

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.

Kommentar

Du kan också implementera en anpassad undantagshanterare för en kodningsåtgärd. Mer information finns i avsnittet Implementera en anpassad återställningsstrategi .

Objekten EncoderFallbackException och DecoderFallbackException innehåller följande information om villkoret som orsakade undantaget:

Även om objekten EncoderFallbackException och DecoderFallbackException ger tillräcklig diagnostisk information om undantaget ger de inte åtkomst till kodnings- eller avkodningsbufferten. Därför tillåter de inte att ogiltiga data ersätts eller korrigeras inom kodnings- eller avkodningsmetoden.

Implementera en anpassad återställningsstrategi

Förutom den mappning som passar bäst som implementeras internt av kodsidor innehåller .NET följande klasser för att implementera en reservstrategi:

Dessutom kan du implementera en anpassad lösning som använder återställning efter bästa passform, återställning efter ersättning eller undantagsåterställning genom att följa dessa steg:

  1. Härled en klass från EncoderFallback för kodningsåtgärder och från DecoderFallback för avkodningsåtgärder.

  2. Härled en klass från EncoderFallbackBuffer för kodningsåtgärder och från DecoderFallbackBuffer för avkodningsåtgärder.

  3. För undantagsåterställning, om de fördefinierade EncoderFallbackException klasserna och DecoderFallbackException klasserna inte uppfyller dina behov, härleder du en klass från ett undantagsobjekt som Exception eller ArgumentException.

Härled från EncoderFallback eller DecoderFallback

Om du vill implementera en anpassad återställningslösning måste du skapa en klass som ärver från EncoderFallback för kodningsåtgärder och från DecoderFallback för avkodningsåtgärder. Instanser av dessa klasser skickas till Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metoden och fungerar som mellanhand mellan kodningsklassen och återställningsimplementeringen.

När du skapar en anpassad reservlösning för en kodare eller avkodare måste du implementera följande medlemmar:

Härled från EncoderFallbackBuffer eller DecoderFallbackBuffer

Om du vill implementera en anpassad återställningslösning måste du också skapa en klass som ärver från EncoderFallbackBuffer för kodningsåtgärder och från DecoderFallbackBuffer för avkodningsåtgärder. Instanser av dessa klasser returneras med CreateFallbackBuffer metoden EncoderFallback och DecoderFallback klasserna. Metoden EncoderFallback.CreateFallbackBuffer anropas av kodaren när den stöter på det första tecknet som den inte kan koda, och DecoderFallback.CreateFallbackBuffer metoden anropas av avkodaren när den stöter på en eller flera byte som den inte kan avkoda. Klasserna EncoderFallbackBuffer och DecoderFallbackBuffer tillhandahåller reservimplementeringen. Varje instans representerar en buffert som innehåller återställningstecken som ersätter det tecken som inte kan kodas eller bytesekvensen som inte kan avkodas.

När du skapar en anpassad reservlösning för en kodare eller avkodare måste du implementera följande medlemmar:

Om återställningsimplementeringen är en reservdel som passar bäst eller en ersättningsåterställning, kommer klasserna från EncoderFallbackBuffer och DecoderFallbackBuffer underhåller även två privata instansfält: det exakta antalet tecken i bufferten och indexet för nästa tecken i bufferten som ska returneras.

Ett Exempel på encoderFallback

I ett tidigare exempel användes ersättningsåterställning för att ersätta Unicode-tecken som inte motsvarade ASCII-tecken med en asterisk (*). I följande exempel används en anpassad reservimplementering med bästa passform i stället för att ge en bättre mappning av icke-ASCII-tecken.

Följande kod definierar en klass med namnet CustomMapper som härleds från EncoderFallback för att hantera den bästa mappningen av icke-ASCII-tecken. Dess CreateFallbackBuffer metod returnerar ett CustomMapperFallbackBuffer objekt som tillhandahåller implementeringen EncoderFallbackBuffer . Klassen CustomMapper använder ett Dictionary<TKey,TValue> objekt för att lagra mappningar av Unicode-tecken som inte stöds (nyckelvärdet) och deras motsvarande 8-bitarstecken (som lagras i två på varandra följande byte i ett 64-bitars heltal). För att göra den här mappningen tillgänglig för återställningsbufferten skickas instansen CustomMapper som en parameter till CustomMapperFallbackBuffer klasskonstruktorn. Eftersom den längsta mappningen är strängen "INF" för Unicode-tecknet U+221E returnerar MaxCharCount egenskapen 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

Följande kod definierar CustomMapperFallbackBuffer klassen, som härleds från EncoderFallbackBuffer. Ordlistan som innehåller mappningar som passar bäst och som definieras i instansen CustomMapper är tillgänglig från dess klasskonstruktor. Metoden Fallback returnerar true om något av de Unicode-tecken som ASCII-kodaren inte kan koda definieras i mappningsordlistan. Annars returneras false. För varje återställning anger den privata count variabeln antalet tecken som återstår att returnera, och den privata index variabeln anger positionen i strängbufferten, charsToReturn, för nästa tecken som ska returneras.

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

Följande kod instansierar CustomMapper sedan objektet och skickar en instans av det till Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metoden. Utdata anger att den bästa återställningsimplementeringen hanterar de tre icke-ASCII-tecknen i den ursprungliga strängen.

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

Se även