Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek vysvětluje, jak používat třídy, které .NET poskytuje pro kódování a dekódování textu pomocí různých schémat kódování. Pokyny předpokládají, že jste si přečetli Úvod do kódování znaků v rozhraní .NET.
Kodéry a dekodéry
.NET poskytuje třídy kódování, které kódují a dekódují text pomocí různých kódovacích systémů. Například třída UTF8Encoding popisuje pravidla pro kódování a dekódování z UTF-8. .NET používá kódování UTF-16 (reprezentované UnicodeEncoding třídy) pro string
instance. Kodéry a dekodéry jsou k dispozici pro jiná schémata kódování.
Kódování a dekódování může také zahrnovat ověření. Například třída UnicodeEncoding kontroluje všechny instance char
v rozsahu náhradních znaků, aby se ujistily, že jsou v platných náhradních dvojicích. Záložní strategie určuje, jak kodér zpracovává neplatné znaky nebo jak dekodér zpracovává neplatné bajty.
Výstraha
Třídy kódování .NET poskytují způsob, jak ukládat a převádět data znaků. Neměly by se používat k ukládání binárních dat ve formě řetězce. V závislosti na použitém kódování může převod binárních dat na formát řetězce s třídami kódování způsobit neočekávané chování a způsobit nepřesná nebo poškozená data. Chcete-li převést binární data na řetězcový formulář, použijte metodu Convert.ToBase64String.
Všechny třídy kódování znaků v rozhraní .NET dědí z třídy System.Text.Encoding, což je abstraktní třída, která definuje funkce společné pro všechna kódování znaků. Pokud chcete získat přístup k jednotlivým objektům kódování implementovaným v .NET, postupujte takto:
Použijte statické vlastnosti třídy Encoding, která vrací objekty, které představují standardní kódování znaků dostupné v .NET (ASCII, UTF-7, UTF-8, UTF-16 a UTF-32). Například vlastnost Encoding.Unicode vrátí objekt UnicodeEncoding. Každý objekt používá náhradní metodu pro zpracování řetězců, které nemůže kódovat, a bajtů, které nemůže dekódovat. Další informace naleznete v tématu náhradní řešení.
Zavolejte konstruktor třídy kódování. Objekty pro kódování ASCII, UTF-7, UTF-8, UTF-16 a UTF-32 lze tímto způsobem instancovat. Ve výchozím nastavení každý objekt používá náhradní funkci pro zpracování řetězců, které nelze kódovat, a bajty, které nelze dekódovat, ale můžete určit vyvolání výjimky. Další informace naleznete v tématu náhradní záložní a výjimka záložní.
Zavolejte konstruktor Encoding(Int32) a předejte ho celé číslo, které představuje kódování. Standardní kódovací objekty používají náhradní substituci, zatímco kódové stránky a dvoubajtové znakové sady (DBCS) používají substituci nejlepší shody k zpracování řetězců, které nemohou kódovat, a bajtů, které nemohou dekódovat. Více informací naleznete v tématu nejvhodnější náhradní.
Volejte metodu Encoding.GetEncoding, která vrátí všechny standardní kódové stránky nebo kódování DBCS dostupné v .NET. Přetížení umožňují určit záložní objekt pro kodér i dekodér.
Informace o všech kódováních dostupných v .NET můžete načíst voláním metody Encoding.GetEncodings. .NET podporuje schémata kódování znaků uvedená v následující tabulce.
Třída kódování | Popis |
---|---|
ASCII | Zakóduje omezený rozsah znaků pomocí nižších sedmi bitů bajtu. Vzhledem k tomu, že toto kódování podporuje pouze hodnoty znaků z U+0000 až U+007F , ve většině případů je nedostatečné pro mezinárodní aplikace. |
UTF-7 | Představuje znaky jako sekvence 7bitových znaků ASCII. Znaky Unicode jiné než ASCII jsou reprezentovány pomocí escape sekvence znaků ASCII. UTF-7 podporuje protokoly, jako je e-mail a diskusní skupina. UTF-7 ale není zvlášť zabezpečený ani robustní. V některých případech může změna jednoho bitu radikálně změnit interpretaci celého řetězce UTF-7. V jiných případech mohou různé řetězce UTF-7 kódovat stejný text. U sekvencí, které obsahují znaky jiné než ASCII, vyžaduje UTF-7 více místa než UTF-8 a kódování/dekódování je pomalejší. V důsledku toho byste místo UTF-7 měli použít UTF-8, pokud je to možné. |
UTF-8 | Představuje každý bod kódu Unicode jako posloupnost jednoho až čtyř bajtů. UTF-8 podporuje 8bitové velikosti dat a funguje dobře s mnoha existujícími operačními systémy. Pro rozsah znaků ASCII je UTF-8 identický s kódováním ASCII a umožňuje širší sadu znaků. U skriptů CJK (Chinese-Japanese-Korean) však může UTF-8 pro každý znak vyžadovat tři bajty a může způsobit větší velikosti dat než UTF-16. Někdy množství dat ASCII, jako jsou značky HTML, odůvodňuje zvýšenou velikost oblasti CJK. |
UTF-16 | Představuje každý bod kódu Unicode jako posloupnost jednoho nebo dvou 16bitových celých čísel. Nejběžnější znaky Unicode vyžadují pouze jeden bod kódu UTF-16, i když doplňkové znaky Unicode (U+10000 a vyšší) vyžadují dva náhradní body kódu UTF-16. Podporována jsou jak pořadí bajtů little-endian, tak big-endian. Kódování UTF-16 používá modul CLR (Common Language Runtime) k reprezentaci Char a String hodnot a používá ho operační systém Windows k reprezentaci hodnot WCHAR . |
UTF-32 | Představuje každý bod kódu Unicode jako 32bitové celé číslo. Podporována jsou jak pořadí bajtů little-endian, tak big-endian. Kódování UTF-32 se používá, když aplikace chtějí zabránit chování náhradních bodů kódu kódování UTF-16 v operačních systémech, pro které je kódovaný prostor příliš důležitý. Jednotlivé glyfy vykreslené na displeji mohou být stále zakódovány více než jedním znakem UTF-32. |
Kódování ANSI/ISO | Poskytuje podporu pro různé kódové stránky. V operačních systémech Windows se k podpoře konkrétního jazyka nebo skupiny jazyků používají znakové stránky. Tabulku, která obsahuje stránky kódu podporované rozhraním .NET, najdete v Encoding třídě. Objekt kódování pro konkrétní znakovou stránku můžete načíst voláním metody Encoding.GetEncoding(Int32). Znaková stránka obsahuje 256 bodů kódu a je založená na nule. Ve většině znakových stránek představují body kódu 0 až 127 znakovou sadu ASCII a body kódu 128 až 255 se výrazně liší mezi znakovými stránkami. Například znaková stránka 1252 poskytuje znaky pro systémy zápisu latinky, včetně angličtiny, němčiny a francouzštiny. Posledních 128 bodů kódu na znakové stránce 1252 obsahuje zvýrazňující znaky. Znaková stránka 1253 obsahuje kódy znaků, které jsou vyžadovány v řeckém systému zápisu. Posledních 128 bodů kódu na znakové stránce 1253 obsahuje řecké znaky. V důsledku toho aplikace, která spoléhá na znakové stránky ANSI, nemůže uložit řečtinu a němčinu do stejného textového streamu, pokud neobsahuje identifikátor, který označuje odkazovanou znakovou stránku. |
Kódování dvojbajtových znakových sad (DBCS) | Podporuje jazyky, jako jsou čínština, japonština a korejština, které obsahují více než 256 znaků. Ve službě DBCS představuje dvojice bodů kódu (dvojitý bajt) každý znak. Vlastnost Encoding.IsSingleByte vrátí false pro kódování DBCS. Objekt kódování pro konkrétní službu DBCS můžete načíst voláním Encoding.GetEncoding(Int32) metody. Když aplikace zpracovává data dbCS, první bajt znaku DBCS (hlavní bajt) se zpracuje v kombinaci s bajtem stopy, který za ním bezprostředně následuje. Vzhledem k tomu, že jeden pár dvoubatových bodů kódu může představovat různé znaky v závislosti na znakové stránce, toto schéma stále neumožňuje kombinaci dvou jazyků, jako je japonština a čínština, ve stejném datovém proudu. |
Tato kódování umožňují pracovat s znaky Unicode a s kódováními, které se nejčastěji používají ve starších aplikacích. Kromě toho můžete vytvořit vlastní kódování definováním třídy odvozené od Encoding a přepsáním jejích členů.
Podpora kódování .NET Core
Ve výchozím nastavení .NET Core neposkytuje žádné kódování znakové stránky jiné než kódová stránka 28591 a kódování Unicode, jako je UTF-8 a UTF-16. Do vaší aplikace však můžete přidat kódování znakové stránky, které se nacházejí ve standardních aplikacích pro Windows zaměřených na .NET. Další informace najdete v tématu CodePagesEncodingProvider.
Výběr třídy kódování
Pokud máte možnost zvolit kódování, které má vaše aplikace používat, měli byste použít kódování Unicode, nejlépe UTF8Encoding nebo UnicodeEncoding. (.NET také podporuje třetí kódování Unicode, UTF32Encoding.)
Pokud plánujete použít kódování ASCII (ASCIIEncoding), zvolte místo toho UTF8Encoding. Dvě kódování jsou pro znakovou sadu ASCII stejné, ale UTF8Encoding má následující výhody:
Může představovat každý znak Unicode, zatímco ASCIIEncoding podporuje pouze hodnoty znaků Unicode mezi U+0000 a U+007F.
Poskytuje detekci chyb a lepší zabezpečení.
Byl vyladěn tak, aby byl co nejrychleji a měl by být rychlejší než jakékoli jiné kódování. I pro obsah, který je zcela ASCII, operace prováděné s UTF8Encoding jsou rychlejší než operace prováděné s ASCIIEncoding.
Měli byste zvážit použití ASCIIEncoding pouze pro starší verze aplikací. I u starších aplikací ale může být UTF8Encoding lepší volbou z následujících důvodů (za předpokladu výchozího nastavení):
Pokud má vaše aplikace obsah, který není výhradně ASCII a kóduje ho pomocí ASCIIEncoding, každý znak jiné než ASCII kóduje jako otazník (?). Pokud aplikace pak dekóduje tato data, informace se ztratí.
Pokud má vaše aplikace obsah, který není výhradně ASCII a kóduje ho pomocí UTF8Encoding, výsledek se zdá být nečitelný, pokud je interpretován jako ASCII. Pokud však aplikace k dekódování těchto dat používá dekodér UTF-8, data jsou úspěšně zpracována obousměrně.
Ve webové aplikaci by znaky odeslané klientovi v reakci na webový požadavek měly odrážet kódování použité v klientovi. Ve většině případů byste měli nastavit vlastnost HttpResponse.ContentEncoding na hodnotu vrácenou vlastností HttpRequest.ContentEncoding k zobrazení textu v kódování, které uživatel očekává.
Použití objektu kódování
Kodér převede řetězec znaků (nejčastěji znaky Unicode) na jeho číselný (bajtový) ekvivalent. Pomocí kodéru ASCII můžete například převést znaky Unicode na ASCII, aby se mohly zobrazit v konzole. K provedení převodu zavoláte metodu Encoding.GetBytes. Pokud chcete určit, kolik bajtů je potřeba k uložení kódovaných znaků před provedením kódování, můžete volat metodu GetByteCount.
Následující příklad používá jedno bajtové pole ke kódování řetězců ve dvou samostatných operacích. Udržuje index, který označuje počáteční pozici v bajtovém poli pro další sadu BAJTŮ kódovaných ASCII. Volá metodu ASCIIEncoding.GetByteCount(String), aby se zajistilo, že pole bajtů je dostatečně velké, aby vyhovovalo zakódovanému řetězci. Potom zavolá metodu ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32), která kóduje znaky v řetězci.
using System;
using System.Text;
public class Example
{
public static void Main()
{
string[] strings= { "This is the first sentence. ",
"This is the second sentence. " };
Encoding asciiEncoding = Encoding.ASCII;
// Create array of adequate size.
byte[] bytes = new byte[49];
// Create index for current position of array.
int index = 0;
Console.WriteLine("Strings to encode:");
foreach (var stringValue in strings) {
Console.WriteLine($" {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($"{ShowByteValues(bytes, index)}");
Console.WriteLine();
// Decode Unicode byte array to a string.
string newString = asciiEncoding.GetString(bytes, 0, index);
Console.WriteLine($"Decoded: {newString}");
}
private static string ShowByteValues(byte[] bytes, int last )
{
string returnString = " ";
for (int ctr = 0; ctr <= last - 1; ctr++) {
if (ctr % 20 == 0)
returnString += "\n ";
returnString += String.Format("{0:X2} ", bytes[ctr]);
}
return returnString;
}
}
// The example displays the following output:
// Strings to encode:
// This is the first sentence.
// This is the second sentence.
//
// Encoded bytes:
//
// 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
// 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
// 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
//
// Decoded: This is the first sentence. This is the second sentence.
Imports System.Text
Module Example
Public Sub Main()
Dim strings() As String = {"This is the first sentence. ",
"This is the second sentence. "}
Dim asciiEncoding As Encoding = Encoding.ASCII
' Create array of adequate size.
Dim bytes(50) As Byte
' Create index for current position of array.
Dim index As Integer = 0
Console.WriteLine("Strings to encode:")
For Each stringValue In strings
Console.WriteLine(" {0}", stringValue)
Dim count As Integer = asciiEncoding.GetByteCount(stringValue)
If count + index >= bytes.Length Then
Array.Resize(bytes, bytes.Length + 50)
End If
Dim written As Integer = asciiEncoding.GetBytes(stringValue, 0,
stringValue.Length,
bytes, index)
index = index + written
Next
Console.WriteLine()
Console.WriteLine("Encoded bytes:")
Console.WriteLine("{0}", ShowByteValues(bytes, index))
Console.WriteLine()
' Decode Unicode byte array to a string.
Dim newString As String = asciiEncoding.GetString(bytes, 0, index)
Console.WriteLine("Decoded: {0}", newString)
End Sub
Private Function ShowByteValues(bytes As Byte(), last As Integer) As String
Dim returnString As String = " "
For ctr As Integer = 0 To last - 1
If ctr Mod 20 = 0 Then returnString += vbCrLf + " "
returnString += String.Format("{0:X2} ", bytes(ctr))
Next
Return returnString
End Function
End Module
' The example displays the following output:
' Strings to encode:
' This is the first sentence.
' This is the second sentence.
'
' Encoded bytes:
'
' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
' 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
' 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
'
' Decoded: This is the first sentence. This is the second sentence.
Dekodér převede bajtové pole, které odráží kódování určitého znaku na sadu znaků, a to buď v matici znaků, nebo v řetězci. Chcete-li dekódovat bajtové pole do pole znaků, zavoláte metodu Encoding.GetChars. Chcete-li dekódovat bajtové pole do řetězce, zavoláte metodu GetString. Pokud chcete určit, kolik znaků je potřeba k uložení dekódovaných bajtů před provedením dekódování, můžete volat GetCharCount metodu.
Následující příklad zakóduje tři řetězce a pak je dekóduje do jednoho pole znaků. Udržuje index, který označuje počáteční pozici v poli znaků pro další sadu dekódovaných znaků. Volá metodu GetCharCount, aby se zajistilo, že pole znaků je dostatečně velké, aby vyhovovalo všem dekódovaným znakům. Potom zavolá metodu ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32), která dekóduje pole bajtů.
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: {stringValue}");
// Encode the string to a byte array.
bytes = asciiEncoding.GetBytes(stringValue);
// Display the encoded bytes.
Console.Write("Encoded bytes: ");
for (int ctr = 0; ctr < bytes.Length; ctr++)
Console.Write(" {0}{1:X2}",
ctr % 20 == 0 ? Environment.NewLine : "",
bytes[ctr]);
Console.WriteLine();
// Decode the bytes to a single character array.
int count = asciiEncoding.GetCharCount(bytes);
if (count + index >= chars.Length)
Array.Resize(ref chars, chars.Length + 50);
int written = asciiEncoding.GetChars(bytes, 0,
bytes.Length,
chars, index);
index = index + written;
Console.WriteLine();
}
// Instantiate a single string containing the characters.
string decodedString = new string(chars, 0, index - 1);
Console.WriteLine("Decoded string: ");
Console.WriteLine(decodedString);
}
}
// The example displays the following output:
// String to Encode: This is the first sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
// 6E 74 65 6E 63 65 2E 20
//
// String to Encode: This is the second sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
// 65 6E 74 65 6E 63 65 2E 20
//
// String to Encode: This is the third sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
// 6E 74 65 6E 63 65 2E 20
//
// Decoded string:
// This is the first sentence. This is the second sentence. This is the third sentence.
Imports System.Text
Module Example
Public Sub Main()
Dim strings() As String = {"This is the first sentence. ",
"This is the second sentence. ",
"This is the third sentence. "}
Dim asciiEncoding As Encoding = Encoding.ASCII
' Array to hold encoded bytes.
Dim bytes() As Byte
' Array to hold decoded characters.
Dim chars(50) As Char
' Create index for current position of character array.
Dim index As Integer
For Each stringValue In strings
Console.WriteLine("String to Encode: {0}", stringValue)
' Encode the string to a byte array.
bytes = asciiEncoding.GetBytes(stringValue)
' Display the encoded bytes.
Console.Write("Encoded bytes: ")
For ctr As Integer = 0 To bytes.Length - 1
Console.Write(" {0}{1:X2}", If(ctr Mod 20 = 0, vbCrLf, ""),
bytes(ctr))
Next
Console.WriteLine()
' Decode the bytes to a single character array.
Dim count As Integer = asciiEncoding.GetCharCount(bytes)
If count + index >= chars.Length Then
Array.Resize(chars, chars.Length + 50)
End If
Dim written As Integer = asciiEncoding.GetChars(bytes, 0,
bytes.Length,
chars, index)
index = index + written
Console.WriteLine()
Next
' Instantiate a single string containing the characters.
Dim decodedString As New String(chars, 0, index - 1)
Console.WriteLine("Decoded string: ")
Console.WriteLine(decodedString)
End Sub
End Module
' The example displays the following output:
' String to Encode: This is the first sentence.
' Encoded bytes:
' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
' 6E 74 65 6E 63 65 2E 20
'
' String to Encode: This is the second sentence.
' Encoded bytes:
' 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
' 65 6E 74 65 6E 63 65 2E 20
'
' String to Encode: This is the third sentence.
' Encoded bytes:
' 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
' 6E 74 65 6E 63 65 2E 20
'
' Decoded string:
' This is the first sentence. This is the second sentence. This is the third sentence.
Metody kódování a dekódování třídy odvozené z Encoding jsou navrženy tak, aby fungovaly na kompletní sadě dat; to znamená, že všechna data, která mají být zakódována nebo dekódována, je zadána v jednom volání metody. V některých případech jsou však data dostupná ve streamu a data, která mají být kódována nebo dekódována, mohou být k dispozici pouze z samostatných operací čtení. To vyžaduje, aby operace kódování nebo dekódování zapamatovala všechny uložené stavy z předchozího vyvolání. Metody tříd odvozených z Encoder a Decoder jsou schopny zpracovat operace kódování a dekódování, které pokrývají více volání metod.
Objekt Encoder pro konkrétní kódování je k dispozici z vlastnosti Encoding.GetEncoder tohoto kódování. Objekt Decoder pro konkrétní kódování je k dispozici z vlastnosti Encoding.GetDecoder tohoto kódování. U dekódovacích operací mějte na paměti, že třídy odvozené z Decoder zahrnují Decoder.GetChars metodu, ale nemají metodu, která odpovídá Encoding.GetString.
Následující příklad ukazuje rozdíl mezi použitím Encoding.GetString a Decoder.GetChars metod dekódování pole bajtů Unicode. Příklad kóduje řetězec, který obsahuje některé znaky Unicode do souboru, a pak pomocí dvou dekódovacích metod dekóduje je deset bajtů najednou. Protože náhradní pár nastane v desátém a jedenáctém bajtech, je dekódován pomocí samostatných volání metody. Jak ukazuje výstup, metoda Encoding.GetString nedokáže správně dekódovat bajty a místo toho je nahradí funkcí U+FFFD (REPLACE CHARACTER). Na druhou stranu metoda Decoder.GetChars dokáže úspěšně dekódovat pole bajtů, aby získal původní řetězec.
using System;
using System.IO;
using System.Text;
public class Example
{
public static void Main()
{
// Use default replacement fallback for invalid encoding.
UnicodeEncoding enc = new UnicodeEncoding(true, false, false);
// Define a string with various Unicode characters.
string str1 = "AB YZ 19 \uD800\udc05 \u00e4";
str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308";
Console.WriteLine("Created original string...\n");
// Convert string to byte array.
byte[] bytes = enc.GetBytes(str1);
FileStream fs = File.Create(@".\characters.bin");
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Close();
// Read bytes from file.
FileStream fsIn = File.OpenRead(@".\characters.bin");
BinaryReader br = new BinaryReader(fsIn);
const int count = 10; // Number of bytes to read at a time.
byte[] bytesRead = new byte[10]; // Buffer (byte array).
int read; // Number of bytes actually read.
string str2 = String.Empty; // Decoded string.
// Try using Encoding object for all operations.
do {
read = br.Read(bytesRead, 0, count);
str2 += enc.GetString(bytesRead, 0, read);
} while (read == count);
br.Close();
Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...");
CompareForEquality(str1, str2);
Console.WriteLine();
// Use Decoder for all operations.
fsIn = File.OpenRead(@".\characters.bin");
br = new BinaryReader(fsIn);
Decoder decoder = enc.GetDecoder();
char[] chars = new char[50];
int index = 0; // Next character to write in array.
int written = 0; // Number of chars written to array.
do {
read = br.Read(bytesRead, 0, count);
if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length)
Array.Resize(ref chars, chars.Length + 50);
written = decoder.GetChars(bytesRead, 0, read, chars, index);
index += written;
} while (read == count);
br.Close();
// Instantiate a string with the decoded characters.
string str3 = new String(chars, 0, index);
Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...");
CompareForEquality(str1, str3);
}
private static void CompareForEquality(string original, string decoded)
{
bool result = original.Equals(decoded);
Console.WriteLine($"original = decoded: {original.Equals(decoded, StringComparison.Ordinal)}");
if (! result) {
Console.WriteLine("Code points in original string:");
foreach (var ch in original)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
Console.WriteLine("Code points in decoded string:");
foreach (var ch in decoded)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Created original string...
//
// Decoded string using UnicodeEncoding.GetString()...
// original = decoded: False
// Code points in original string:
// 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
// 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
// 0020 0073 0020 0062 0308
// Code points in decoded string:
// 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
// 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
// 0020 0073 0020 0062 0308
//
// Decoded string using UnicodeEncoding.Decoder.GetString()...
// original = decoded: True
Imports System.IO
Imports System.Text
Module Example
Public Sub Main()
' Use default replacement fallback for invalid encoding.
Dim enc As New UnicodeEncoding(True, False, False)
' Define a string with various Unicode characters.
Dim str1 As String = String.Format("AB YZ 19 {0}{1} {2}",
ChrW(&hD800), ChrW(&hDC05), ChrW(&h00e4))
str1 += String.Format("Unicode characters. {0} {1} s {2}{3}",
ChrW(&h00a9), ChrW(&h010C), ChrW(&h0062), ChrW(&h0308))
Console.WriteLine("Created original string...")
Console.WriteLine()
' Convert string to byte array.
Dim bytes() As Byte = enc.GetBytes(str1)
Dim fs As FileStream = File.Create(".\characters.bin")
Dim bw As New BinaryWriter(fs)
bw.Write(bytes)
bw.Close()
' Read bytes from file.
Dim fsIn As FileStream = File.OpenRead(".\characters.bin")
Dim br As New BinaryReader(fsIn)
Const count As Integer = 10 ' Number of bytes to read at a time.
Dim bytesRead(9) As Byte ' Buffer (byte array).
Dim read As Integer ' Number of bytes actually read.
Dim str2 As String = "" ' Decoded string.
' Try using Encoding object for all operations.
Do
read = br.Read(bytesRead, 0, count)
str2 += enc.GetString(bytesRead, 0, read)
Loop While read = count
br.Close()
Console.WriteLine("Decoded string using UnicodeEncoding.GetString()...")
CompareForEquality(str1, str2)
Console.WriteLine()
' Use Decoder for all operations.
fsIn = File.OpenRead(".\characters.bin")
br = New BinaryReader(fsIn)
Dim decoder As Decoder = enc.GetDecoder()
Dim chars(50) As Char
Dim index As Integer = 0 ' Next character to write in array.
Dim written As Integer = 0 ' Number of chars written to array.
Do
read = br.Read(bytesRead, 0, count)
If index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length Then
Array.Resize(chars, chars.Length + 50)
End If
written = decoder.GetChars(bytesRead, 0, read, chars, index)
index += written
Loop While read = count
br.Close()
' Instantiate a string with the decoded characters.
Dim str3 As New String(chars, 0, index)
Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString()...")
CompareForEquality(str1, str3)
End Sub
Private Sub CompareForEquality(original As String, decoded As String)
Dim result As Boolean = original.Equals(decoded)
Console.WriteLine("original = decoded: {0}",
original.Equals(decoded, StringComparison.Ordinal))
If Not result Then
Console.WriteLine("Code points in original string:")
For Each ch In original
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine("Code points in decoded string:")
For Each ch In decoded
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Created original string...
'
' Decoded string using UnicodeEncoding.GetString()...
' original = decoded: False
' Code points in original string:
' 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F
' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
' 0020 0073 0020 0062 0308
' Code points in decoded string:
' 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F
' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C
' 0020 0073 0020 0062 0308
'
' Decoded string using UnicodeEncoding.Decoder.GetString()...
' original = decoded: True
Volba záložní strategie
Když se metoda pokusí kódovat nebo dekódovat znak, ale neexistuje žádné mapování, musí implementovat záložní strategii, která určuje, jak se má neúspěšné mapování zpracovat. Existují tři typy záložních strategií:
Nejvhodnější náhradní řešení
Záložní náhrada
Náhradní výjimka
Důležité
K nejběžnějším problémům s kódovacími operacemi dochází v případě, že znak Unicode nelze namapovat na konkrétní kódování znakové stránky. K nejběžnějším problémům při dekódování operací dochází v případě, že neplatné bajtové sekvence nelze přeložit na platné znaky Unicode. Z těchto důvodů byste měli vědět, jakou záložní strategii používá konkrétní objekt kódování. Kdykoli je to možné, měli byste při vytváření instance objektu určit záložní strategii používanou objektem kódování.
Nejvhodnější náhrada
Pokud znak nemá v cílovém kódování přesnou shodu, kodér se ho může pokusit namapovat na podobný znak. cs-CZ: (Nejvhodnější náhrada je většinou problém kódování, nikoli dekódování. Existuje velmi málo znakových stránek, které obsahují znaky, jež nelze úspěšně namapovat na Unicode.) Nejvhodnější náhrada je výchozí pro znakovou stránku a kódování dvoubajtové znakové sady, které jsou načteny přetíženími metod Encoding.GetEncoding(Int32) a Encoding.GetEncoding(String).
Poznámka:
Třídy kódování Unicode poskytované v rozhraní .NET (UTF8Encoding, UnicodeEncodinga UTF32Encoding) podporují každý znak v každé znakové sadě, takže je lze použít k odstranění problémů s náhradním řešením.
Nejvhodnější strategie se liší pro různé znakové stránky. Například u některých znakových stránek se znaky latinky plné šířky mapují na běžnější znaky latinky poloviční šířky. U jiných kódových stránek toto mapování není provedeno. Ani při agresivní strategii nejlepšího přizpůsobení neexistuje žádné vhodné přizpůsobení pro některé znaky v některých kódováních. Například čínský ideograf nemá rozumné mapování na kódovou stránku 1252. V tomto případě se použije náhradní řetězec. Ve výchozím nastavení je tento řetězec jen jedním otazníkem (U+003F).
Poznámka:
Strategie nejvhodnějších řešení nejsou podrobně zdokumentované. Na webu Unicode Consortium je však zdokumentováno několik kódových stránek. V souboru readme.txt v této složce najdete popis, jak interpretovat soubory mapování.
Následující příklad používá kódovou stránku 1252 (kódovou stránku systému Windows pro západoevropské jazyky), aby ilustroval nejvhodnější mapování a jeho nevýhody. Metoda Encoding.GetEncoding(Int32) slouží k načtení objektu kódování pro znakovou stránku 1252. Ve výchozím nastavení používá mapování nejvhodnější pro znaky Unicode, které nepodporuje. Příklad vytvoří instanci řetězce, který obsahuje tři znaky jiné než ASCII – LATINKY VELKÉ PÍSMENO V KROUŽKU (U+24C8), HORNÍ INDEX ČÍSLO PĚT (U+2075) a NEKONEČNO (U+221E) oddělené mezerami. Jak ukazuje výstup z příkladu, když je řetězec zakódován, nahradí se tři původní neprázdné znaky znaky OTAZNÍK (U+003F), ČÍSLICE PĚT (U+0035) a ČÍSLICE OSM (U+0038). DIGIT EIGHT je zvláště špatná náhrada za nepodporovaný znak INFINITY a OTAZNÍK označuje, že pro původní znak nebylo k dispozici žádné mapování.
using System;
using System.Text;
public class Example
{
public static void Main()
{
// Get an encoding for code page 1252 (Western Europe character set).
Encoding cp1252 = Encoding.GetEncoding(1252);
// Define and display a string.
string str = "\u24c8 \u2075 \u221e";
Console.WriteLine("Original string: " + str);
Console.Write("Code points in string: ");
foreach (var ch in str)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine("\n");
// Encode a Unicode string.
Byte[] bytes = cp1252.GetBytes(str);
Console.Write("Encoded bytes: ");
foreach (byte byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");
// Decode the string.
string str2 = cp1252.GetString(bytes);
Console.WriteLine($"String round-tripped: {str.Equals(str2)}");
if (! str.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
}
}
}
// The example displays the following output:
// Original string: Ⓢ ⁵ ∞
// Code points in string: 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 35 20 38
//
// String round-tripped: False
// ? 5 8
// 003F 0020 0035 0020 0038
Imports System.Text
Module Example
Public Sub Main()
' Get an encoding for code page 1252 (Western Europe character set).
Dim cp1252 As Encoding = Encoding.GetEncoding(1252)
' Define and display a string.
Dim str As String = String.Format("{0} {1} {2}", ChrW(&h24c8), ChrW(&H2075), ChrW(&h221E))
Console.WriteLine("Original string: " + str)
Console.Write("Code points in string: ")
For Each ch In str
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode a Unicode string.
Dim bytes() As Byte = cp1252.GetBytes(str)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Console.WriteLine()
' Decode the string.
Dim str2 As String = cp1252.GetString(bytes)
Console.WriteLine("String round-tripped: {0}", str.Equals(str2))
If Not str.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
End If
End Sub
End Module
' The example displays the following output:
' Original string: Ⓢ ⁵ ∞
' Code points in string: 24C8 0020 2075 0020 221E
'
' Encoded bytes: 3F 20 35 20 38
'
' String round-tripped: False
' ? 5 8
' 003F 0020 0035 0020 0038
Mapování nejlepší shody je výchozí chování pro objekt Encoding, který kóduje data Unicode do dat kódové stránky, a existují starší aplikace, které na toto chování spoléhají. Většina nových aplikací by se ale měla vyhnout chování s nejlepším přizpůsobením z bezpečnostních důvodů. Aplikace by například neměly provádět kódování názvu domény pomocí nejlepšího přizpůsobení.
Poznámka:
Můžete také implementovat vlastní mapování nejlépe vyhovujícího náhradního řešení pro kódování. Další informace najdete v části Implementace vlastní záložní strategie.
Pokud je ve výchozím nastavení nejlepší přizpůsobení pro objekt kódování, můžete při načtení objektu Encoding zvolit jinou náhradní strategii voláním metody Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) nebo přetížené verze Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). Následující část obsahuje příklad, který nahradí každý znak, který nelze namapovat na znakovou stránku 1252 hvězdičkou (*).
using System;
using System.Text;
public class Example
{
public static void Main()
{
Encoding cp1252r = Encoding.GetEncoding(1252,
new EncoderReplacementFallback("*"),
new DecoderReplacementFallback("*"));
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
byte[] bytes = cp1252r.GetBytes(str1);
string str2 = cp1252r.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A
Imports System.Text
Module Example
Public Sub Main()
Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
New EncoderReplacementFallback("*"),
New DecoderReplacementFallback("*"))
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Dim bytes() As Byte = cp1252r.GetBytes(str1)
Dim str2 As String = cp1252r.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
' Round-trip: False
' * * *
' 002A 0020 002A 0020 002A
Náhradní řešení
Pokud znak nemá v cílovém schématu přesnou shodu, ale neexistuje žádný vhodný znak, na který lze namapovat, může aplikace zadat náhradní znak nebo řetězec. Toto je výchozí chování dekodéru Unicode, který nahrazuje jakoukoli dvoubajtovou sekvenci, kterou nelze dekódovat, znakem REPLACEMENT_CHARACTER (U+FFFD). Jedná se také o výchozí chování třídy ASCIIEncoding, která nahrazuje každý znak, který nemůže kódovat nebo dekódovat otazníkem. Následující příklad znázorňuje nahrazení znaků řetězce Unicode z předchozího příkladu. Jak ukazuje výstup, každý znak, který nelze dekódovat do bajtové hodnoty ASCII, je nahrazen 0x3F, což je kód ASCII pro otazník.
using System;
using System.Text;
public class Example
{
public static void Main()
{
Encoding enc = Encoding.ASCII;
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine("\n");
// Encode the original string using the ASCII encoder.
byte[] bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");
// Decode the ASCII bytes.
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 3F 20 3F
//
// Round-trip: False
// ? ? ?
// 003F 0020 003F 0020 003F
Imports System.Text
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.Ascii
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode the original string using the ASCII encoder.
Dim bytes() As Byte = enc.GetBytes(str1)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Console.WriteLine()
' Decode the ASCII bytes.
Dim str2 As String = enc.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
'
' Encoded bytes: 3F 20 3F 20 3F
'
' Round-trip: False
' ? ? ?
' 003F 0020 003F 0020 003F
.NET obsahuje třídy EncoderReplacementFallback a DecoderReplacementFallback, které nahrazují náhradní řetězec, pokud znak nemapuje přesně v kódování nebo dekódovací operaci. Ve výchozím nastavení je tento náhradní řetězec otazníkem, ale můžete použít přetížení konstruktoru třídy a zvolit alternativní řetězec. Výměnný řetězec je obvykle jediným znakem, i když to není požadavek. Následující příklad změní chování kodéru kódové stránky 1252 vytvořením instance objektu EncoderReplacementFallback, který jako náhradní řetězec používá hvězdičku (*).
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: {str1.Equals(str2)}");
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A
Imports System.Text
Module Example
Public Sub Main()
Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
New EncoderReplacementFallback("*"),
New DecoderReplacementFallback("*"))
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Dim bytes() As Byte = cp1252r.GetBytes(str1)
Dim str2 As String = cp1252r.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
' Round-trip: False
' * * *
' 002A 0020 002A 0020 002A
Poznámka:
Můžete také implementovat náhradní třídu pro kódování. Další informace najdete v části Implementace vlastní záložní strategie.
Kromě OTAZNÍKU (U+003F) se znak UNICODE REPLACEMENT CHARACTER (U+FFFD) běžně používá jako náhradní řetězec, zejména při dekódování bajtů, které nelze úspěšně přeložit na znaky Unicode. Můžete si ale vybrat libovolný náhradní řetězec a může obsahovat více znaků.
Náhradní mechanismus pro výjimky
Místo poskytnutí nejlepšího možného nebo náhradního řetězce může kodér vyvolat výjimku EncoderFallbackException, pokud není schopen zakódovat sadu znaků, a dekodér může vyvolat výjimku DecoderFallbackException, pokud není schopen dekódovat pole bajtů. Chcete-li vyvolat výjimku v operacích kódování a dekódování, zadejte objekt EncoderExceptionFallback a objekt DecoderExceptionFallback v uvedeném pořadí metodě Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). Následující příklad znázorňuje náhradní mechanismus výjimky třídy ASCIIEncoding.
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{Convert.ToUInt16(e.CharUnknownHigh):X4} 0x{Convert.ToUInt16(e.CharUnknownLow):X3} at index {e.Index}.");
else
Console.WriteLine($"Unable to encode 0x{Convert.ToUInt16(e.CharUnknown):X4} at index {e.Index}.");
return;
}
Console.WriteLine();
// Decode the ASCII bytes.
try {
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {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 {e.Index}");
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Exception: Unable to encode 0x24C8 at index 0.
Imports System.Text
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.GetEncoding("us-ascii",
New EncoderExceptionFallback(),
New DecoderExceptionFallback())
Dim str1 As String = String.Format("{0} {1} {2}", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E))
Console.WriteLine(str1)
For Each ch In str1
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
Console.WriteLine()
' Encode the original string using the ASCII encoder.
Dim bytes() As Byte = {}
Try
bytes = enc.GetBytes(str1)
Console.Write("Encoded bytes: ")
For Each byt In bytes
Console.Write("{0:X2} ", byt)
Next
Console.WriteLine()
Catch e As EncoderFallbackException
Console.Write("Exception: ")
If e.IsUnknownSurrogate() Then
Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.",
Convert.ToUInt16(e.CharUnknownHigh),
Convert.ToUInt16(e.CharUnknownLow),
e.Index)
Else
Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
Convert.ToUInt16(e.CharUnknown),
e.Index)
End If
Exit Sub
End Try
Console.WriteLine()
' Decode the ASCII bytes.
Try
Dim str2 As String = enc.GetString(bytes)
Console.WriteLine("Round-trip: {0}", str1.Equals(str2))
If Not str1.Equals(str2) Then
Console.WriteLine(str2)
For Each ch In str2
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"))
Next
Console.WriteLine()
End If
Catch e As DecoderFallbackException
Console.Write("Unable to decode byte(s) ")
For Each unknown As Byte In e.BytesUnknown
Console.Write("0x{0:X2} ")
Next
Console.WriteLine("at index {0}", e.Index)
End Try
End Sub
End Module
' The example displays the following output:
' Ⓢ ⁵ ∞
' 24C8 0020 2075 0020 221E
'
' Exception: Unable to encode 0x24C8 at index 0.
Poznámka:
Můžete také implementovat vlastní zpracování výjimek pro operaci kódování. Další informace najdete v části Implementace vlastní záložní strategie.
Objekty EncoderFallbackException a DecoderFallbackException poskytují následující informace o podmínce, která způsobila výjimku:
Objekt EncoderFallbackException obsahuje IsUnknownSurrogate metodu, která označuje, zda znak nebo znaky, které nelze zakódovat, představují neznámý náhradní pár (v takovém případě metoda vrátí
true
) nebo neznámý jeden znak (v takovém případě metoda vrátífalse
). Znaky v náhradní dvojici jsou k dispozici ve vlastnostech EncoderFallbackException.CharUnknownHigh a EncoderFallbackException.CharUnknownLow. Neznámý jeden znak je k dispozici z vlastnosti EncoderFallbackException.CharUnknown. Vlastnost EncoderFallbackException.Index označuje pozici v řetězci, ve kterém byl nalezen první znak, který nelze zakódovat.Objekt DecoderFallbackException obsahuje vlastnost BytesUnknown, která vrací pole bajtů, které nelze dekódovat. Vlastnost DecoderFallbackException.Index označuje počáteční pozici neznámých bajtů.
I když objekty EncoderFallbackException a DecoderFallbackException poskytují adekvátní diagnostické informace o výjimce, neposkytují přístup k vyrovnávací paměti kódování nebo dekódování. Proto neumožňují nahrazení nebo opravu neplatných dat v rámci kódování nebo dekódovací metody.
Implementace vlastní nouzové záložní strategie
Kromě nejlepšího přizpůsobení mapování, které je interně implementované na kódových stránkách, jsou v .NET zahrnuty následující třídy k implementaci záložní strategie:
K nahrazení znaků v operacích kódování použijte EncoderReplacementFallback a EncoderReplacementFallbackBuffer.
K nahrazení znaků v operacích dekódování použijte DecoderReplacementFallback a DecoderReplacementFallbackBuffer.
Použijte EncoderExceptionFallback a EncoderExceptionFallbackBuffer k vyvolání EncoderFallbackException, pokud znak nelze zakódovat.
Použijte DecoderExceptionFallback a DecoderExceptionFallbackBuffer k vyvolání DecoderFallbackException, pokud znak nelze dekódovat.
Kromě toho můžete implementovat vlastní řešení, které využívá nejlepší alternativu, náhradu nebo výjimku, a to pomocí následujících kroků:
Odvození třídy z EncoderFallback pro operace kódování a z DecoderFallback pro dekódovací operace.
Odvození třídy z EncoderFallbackBuffer pro operace kódování a z DecoderFallbackBuffer pro dekódovací operace.
Pro náhradní mechanismus pro výjimky, pokud třídy EncoderFallbackException a DecoderFallbackException předdefinované nevyhovují vašim potřebám, odvoďte třídu z objektu výjimky, jako je Exception nebo ArgumentException.
Odvození z EncoderFallback nebo DecoderFallback
Pokud chcete implementovat vlastní náhradní řešení, musíte vytvořit třídu, která dědí z EncoderFallback pro operace kódování a z DecoderFallback pro dekódovací operace. Instance těchto tříd jsou předány metodě Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) a slouží jako zprostředkovatelé mezi kódovací třídou a náhradní implementací.
Při vytváření vlastního náhradního řešení pro kodér nebo dekodér musíte implementovat následující členy:
Vlastnost EncoderFallback.MaxCharCount nebo DecoderFallback.MaxCharCount, která vrací maximální možný počet znaků, které může strategie náhrady jako nejlepší shoda, standardní náhrada nebo výjimka vrátit pro nahrazení jednoho znaku. Výchozí hodnota pro vlastní výjimku je nula.
Metoda EncoderFallback.CreateFallbackBuffer nebo DecoderFallback.CreateFallbackBuffer, která vrací vlastní EncoderFallbackBuffer nebo DecoderFallbackBuffer implementaci. Metoda je volána kodérem, když narazí na první znak, který nemůže úspěšně zakódovat, nebo dekodérem, když narazí na první bajt, který nemůže úspěšně dekódovat.
Odvozeno z EncoderFallbackBuffer nebo DecoderFallbackBuffer
Pokud chcete implementovat vlastní náhradní řešení, musíte také vytvořit třídu, která dědí z EncoderFallbackBuffer pro operace kódování a z DecoderFallbackBuffer pro dekódovací operace. Instance těchto tříd jsou vráceny metodou CreateFallbackBuffer tříd EncoderFallback a DecoderFallback. EncoderFallback.CreateFallbackBuffer metoda je volána kodérem, když narazí na první znak, který není schopen kódovat, a DecoderFallback.CreateFallbackBuffer metoda je volána dekodérem, když narazí na jeden nebo více bajtů, že není schopen dekódovat. Třídy EncoderFallbackBuffer a DecoderFallbackBuffer poskytují záložní implementaci. Každá instance představuje vyrovnávací paměť obsahující náhradní znaky, které nahradí znak, který nelze zakódovat, nebo sekvence bajtů, která nelze dekódovat.
Při vytváření vlastního náhradního řešení pro kodér nebo dekodér musíte implementovat následující členy:
Metoda EncoderFallbackBuffer.Fallback nebo DecoderFallbackBuffer.Fallback. EncoderFallbackBuffer.Fallback je volán kodérem, aby poskytl náhradní vyrovnávací paměti informace o znaku, který nemůže zakódovat. Vzhledem k tomu, že znak, který má být kódován, může být náhradní dvojicí, je tato metoda přetížena. Jedno z přetížení dostane znak k zakódování a jeho index v řetězci. Druhé přetížení je předáno jako vyšší a nižší náhradní znak spolu s jejich indexem v řetězci. Dekodér volá metodu DecoderFallbackBuffer.Fallback, která poskytuje záložní vyrovnávací paměť s informacemi o bajtech, které nemůže dekódovat. Této metodě je předáván pole bajtů, které nemůže dekódovat, spolu s indexem prvního bajtu. Záložní metoda by měla vrátit
true
, pokud záložní vyrovnávací paměť je schopna poskytnout nejvhodnější nebo náhradní znak či znaky; jinak by měla vrátitfalse
. V případě náhradní výjimky by náhradní metoda měla vyvolat výjimku.Metoda EncoderFallbackBuffer.GetNextChar nebo DecoderFallbackBuffer.GetNextChar, která se volá opakovaně kodérem nebo dekodérem, aby získala další znak ze záložní vyrovnávací paměti. Po vrácení všech záložních znaků by metoda měla vrátit U+0000.
Vlastnost EncoderFallbackBuffer.Remaining nebo DecoderFallbackBuffer.Remaining, která vrátí počet znaků zbývajících v záložní vyrovnávací paměti.
Metoda EncoderFallbackBuffer.MovePrevious nebo DecoderFallbackBuffer.MovePrevious, která přesune aktuální pozici v záložní vyrovnávací paměti na předchozí znak.
Metoda EncoderFallbackBuffer.Reset nebo DecoderFallbackBuffer.Reset, která znovu inicializuje záložní vyrovnávací paměť.
Pokud je záložní implementace nejbližší vhodné nebo náhradní, třídy odvozené z EncoderFallbackBuffer a DecoderFallbackBuffer také udržují dvě privátní instance polí: přesný počet znaků v paměťovém bufferu; a index dalšího znaku v paměťovém bufferu k vrácení.
Příklad použití EncoderFallback
Předchozí příklad použil náhradní řešení k nahrazení znaků Unicode, které neodpovídaly znakům ASCII, hvězdičkou (*). Následující příklad místo toho používá vlastní optimální náhradní implementaci, aby poskytla lepší mapování ne-ASCII znaků.
Následující kód definuje třídu s názvem CustomMapper
, která je odvozena z EncoderFallback pro zpracování mapování nejvhodnějších znaků bez ASCII. Jeho CreateFallbackBuffer
metoda vrátí CustomMapperFallbackBuffer
objekt, který poskytuje EncoderFallbackBuffer implementaci. Třída CustomMapper
používá objekt Dictionary<TKey,TValue> k uložení mapování nepodporovaných znaků Unicode (hodnota klíče) a odpovídajících 8bitových znaků (které jsou uloženy ve dvou po sobě jdoucích bajtech v 64bitovém celočíselném čísle). Aby bylo toto mapování zpřístupněno záložní vyrovnávací paměti, instance CustomMapper
se předává jako parametr konstruktoru třídy CustomMapperFallbackBuffer
. Protože nejdelší mapování je řetězec "INF" pro znak Unicode U+221E, vlastnost MaxCharCount
vrací hodnotu 3.
public class CustomMapper : EncoderFallback
{
public string DefaultString;
internal Dictionary<ushort, ulong> mapping;
public CustomMapper() : this("*")
{
}
public CustomMapper(string defaultString)
{
this.DefaultString = defaultString;
// Create table of mappings
mapping = new Dictionary<ushort, ulong>();
mapping.Add(0x24C8, 0x53);
mapping.Add(0x2075, 0x35);
mapping.Add(0x221E, 0x49004E0046);
}
public override EncoderFallbackBuffer CreateFallbackBuffer()
{
return new CustomMapperFallbackBuffer(this);
}
public override int MaxCharCount
{
get { return 3; }
}
}
Public Class CustomMapper : Inherits EncoderFallback
Public DefaultString As String
Friend mapping As Dictionary(Of UShort, ULong)
Public Sub New()
Me.New("?")
End Sub
Public Sub New(ByVal defaultString As String)
Me.DefaultString = defaultString
' Create table of mappings
mapping = New Dictionary(Of UShort, ULong)
mapping.Add(&H24C8, &H53)
mapping.Add(&H2075, &H35)
mapping.Add(&H221E, &H49004E0046)
End Sub
Public Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBuffer
Return New CustomMapperFallbackBuffer(Me)
End Function
Public Overrides ReadOnly Property MaxCharCount As Integer
Get
Return 3
End Get
End Property
End Class
Následující kód definuje CustomMapperFallbackBuffer
třídu, která je odvozena z EncoderFallbackBuffer. Slovník, který obsahuje nejvhodnější mapování a který je definován v instanci CustomMapper
, je k dispozici z konstruktoru její třídy. Jeho Fallback
metoda vrátí true
, pokud kterýkoliv z Unicode znaků, které kodér ASCII nemůže zakódovat, je definován ve slovníku mapování, v opačném případě vrátí false
. Pro každý záložní mechanismus privátní proměnná count
označuje počet znaků, které se mají vrátit, a privátní proměnná index
označuje pozici ve vyrovnávací paměti řetězce charsToReturn
, dalšího znaku, který se má vrátit.
public class CustomMapperFallbackBuffer : EncoderFallbackBuffer
{
int count = -1; // Number of characters to return
int index = -1; // Index of character to return
CustomMapper fb;
string charsToReturn;
public CustomMapperFallbackBuffer(CustomMapper fallback)
{
this.fb = fallback;
}
public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
{
// Do not try to map surrogates to ASCII.
return false;
}
public override bool Fallback(char charUnknown, int index)
{
// Return false if there are already characters to map.
if (count >= 1) return false;
// Determine number of characters to return.
charsToReturn = String.Empty;
ushort key = Convert.ToUInt16(charUnknown);
if (fb.mapping.ContainsKey(key)) {
byte[] bytes = BitConverter.GetBytes(fb.mapping[key]);
int ctr = 0;
foreach (var byt in bytes) {
if (byt > 0) {
ctr++;
charsToReturn += (char) byt;
}
}
count = ctr;
}
else {
// Return default.
charsToReturn = fb.DefaultString;
count = 1;
}
this.index = charsToReturn.Length - 1;
return true;
}
public override char GetNextChar()
{
// We'll return a character if possible, so subtract from the count of chars to return.
count--;
// If count is less than zero, we've returned all characters.
if (count < 0)
return '\u0000';
this.index--;
return charsToReturn[this.index + 1];
}
public override bool MovePrevious()
{
// Original: if count >= -1 and pos >= 0
if (count >= -1) {
count++;
return true;
}
else {
return false;
}
}
public override int Remaining
{
get { return count < 0 ? 0 : count; }
}
public override void Reset()
{
count = -1;
index = -1;
}
}
Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBuffer
Dim count As Integer = -1 ' Number of characters to return
Dim index As Integer = -1 ' Index of character to return
Dim fb As CustomMapper
Dim charsToReturn As String
Public Sub New(ByVal fallback As CustomMapper)
MyBase.New()
Me.fb = fallback
End Sub
Public Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char, ByVal index As Integer) As Boolean
' Do not try to map surrogates to ASCII.
Return False
End Function
Public Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean
' Return false if there are already characters to map.
If count >= 1 Then Return False
' Determine number of characters to return.
charsToReturn = String.Empty
Dim key As UShort = Convert.ToUInt16(charUnknown)
If fb.mapping.ContainsKey(key) Then
Dim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key))
Dim ctr As Integer
For Each byt In bytes
If byt > 0 Then
ctr += 1
charsToReturn += Chr(byt)
End If
Next
count = ctr
Else
' Return default.
charsToReturn = fb.DefaultString
count = 1
End If
Me.index = charsToReturn.Length - 1
Return True
End Function
Public Overrides Function GetNextChar() As Char
' We'll return a character if possible, so subtract from the count of chars to return.
count -= 1
' If count is less than zero, we've returned all characters.
If count < 0 Then Return ChrW(0)
Me.index -= 1
Return charsToReturn(Me.index + 1)
End Function
Public Overrides Function MovePrevious() As Boolean
' Original: if count >= -1 and pos >= 0
If count >= -1 Then
count += 1
Return True
Else
Return False
End If
End Function
Public Overrides ReadOnly Property Remaining As Integer
Get
Return If(count < 0, 0, count)
End Get
End Property
Public Overrides Sub Reset()
count = -1
index = -1
End Sub
End Class
Následující kód pak vytvoří instanci objektu CustomMapper
a předá instanci do Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) metody. Výstup označuje, že nejlepší náhradní implementace úspěšně zpracovává tři znaky jiné než ASCII v původním řetězci.
using System;
using System.Collections.Generic;
using System.Text;
class Program
{
static void Main()
{
Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback());
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
for (int ctr = 0; ctr <= str1.Length - 1; ctr++) {
Console.Write("{0} ", Convert.ToUInt16(str1[ctr]).ToString("X4"));
if (ctr == str1.Length - 1)
Console.WriteLine();
}
Console.WriteLine();
// Encode the original string using the ASCII encoder.
byte[] bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");
// Decode the ASCII bytes.
string str2 = enc.GetString(bytes);
Console.WriteLine($"Round-trip: {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