Codificação de caracteres no .NET
Este artigo fornece uma introdução aos sistemas de codificação de caracteres usados pelo .NET. O artigo explica como os Stringtipos , Char, Rune, e StringInfo funcionam com Unicode, UTF-16 e UTF-8.
O termo caractere é usado aqui no sentido geral do que um leitor percebe como um único elemento de exibição. Exemplos comuns são a letra "a", o símbolo "@" e o emoji "🐂". Às vezes, o que parece ser um caractere é, na verdade, composto por vários elementos de exibição independentes, como explica a seção sobre aglomerados de grafemas.
Os string e char tipos
Uma instância da string classe representa algum texto. A string
é logicamente uma sequência de valores de 16 bits, cada um dos quais é uma instância da char struct. O string. A propriedade Length retorna o número de char
instâncias na string
instância.
A função de exemplo a seguir imprime os valores em notação hexadecimal de todas as char
instâncias em um string
:
void PrintChars(string s)
{
Console.WriteLine($"\"{s}\".Length = {s.Length}");
for (int i = 0; i < s.Length; i++)
{
Console.WriteLine($"s[{i}] = '{s[i]}' ('\\u{(int)s[i]:x4}')");
}
Console.WriteLine();
}
Passe o string "Olá" para esta função, e você obtém a seguinte saída:
PrintChars("Hello");
"Hello".Length = 5
s[0] = 'H' ('\u0048')
s[1] = 'e' ('\u0065')
s[2] = 'l' ('\u006c')
s[3] = 'l' ('\u006c')
s[4] = 'o' ('\u006f')
Cada caractere é representado por um único char
valor. Esse padrão é válido para a maioria das línguas do mundo. Por exemplo, aqui está a saída para dois caracteres chineses que soam como nǐ hǎo e significam Olá:
PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')
No entanto, para alguns idiomas e para alguns símbolos e emojis, são necessárias duas char
instâncias para representar um único caractere. Por exemplo, compare os caracteres e char
ocorrências na palavra que significa Osage na língua Osage :
PrintChars("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟");
"𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟".Length = 17
s[0] = '�' ('\ud801')
s[1] = '�' ('\udccf')
s[2] = '�' ('\ud801')
s[3] = '�' ('\udcd8')
s[4] = '�' ('\ud801')
s[5] = '�' ('\udcfb')
s[6] = '�' ('\ud801')
s[7] = '�' ('\udcd8')
s[8] = '�' ('\ud801')
s[9] = '�' ('\udcfb')
s[10] = '�' ('\ud801')
s[11] = '�' ('\udcdf')
s[12] = ' ' ('\u0020')
s[13] = '�' ('\ud801')
s[14] = '�' ('\udcbb')
s[15] = '�' ('\ud801')
s[16] = '�' ('\udcdf')
No exemplo anterior, cada caractere, exceto o espaço, é representado por duas char
instâncias.
Um único emoji Unicode também é representado por dois char
s, como visto no exemplo a seguir mostrando um emoji de boi:
"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')
Esses exemplos mostram que o valor de string.Length
, que indica o número de char
instâncias, não indica necessariamente o número de caracteres exibidos. Uma única char
instância por si só não representa necessariamente um personagem.
Os char
pares que mapeiam para um único caractere são chamados pares substitutos. Para entender como eles funcionam, você precisa entender a codificação Unicode e UTF-16.
Pontos de código Unicode
Unicode é um padrão internacional de codificação para uso em várias plataformas e com várias linguagens e scripts.
O padrão Unicode define mais de 1,1 milhão de pontos de código. Um ponto de código é um valor inteiro que pode variar de 0 a U+10FFFF
(decimal 1.114.111). Alguns pontos de código são atribuídos a letras, símbolos ou emojis. Outros são atribuídos a ações que controlam como o texto ou os caracteres são exibidos, como avançar para uma nova linha. Muitos pontos de código ainda não foram atribuídos.
Aqui estão alguns exemplos de atribuições de pontos de código, com links para gráficos Unicode nos quais eles aparecem:
Decimal | Hex | Exemplo | Description |
---|---|---|---|
10 | U+000A |
N/A | ALIMENTAÇÃO DE LINHA |
97 | U+0061 |
a | LETRA PEQUENA LATINA A |
562 | U+0232 |
Ȳ | LETRA MAIÚSCULA LATINA Y COM MACRON |
68,675 | U+10C43 |
𐱃 | OLD TURKIC LETTER ORKHON NA |
127,801 | U+1F339 |
🌹 | EMOJI ROSE |
Os pontos de código são habitualmente referidos usando a sintaxe U+xxxx
, onde xxxx
é o valor inteiro codificado por hex.
Dentro da gama completa de pontos de código existem dois subintervalos:
- O Plano Multilingue Básico (BMP) no intervalo
U+0000..U+FFFF
. Esta faixa de 16 bits fornece 65.536 pontos de código, o suficiente para cobrir a maioria dos sistemas de escrita do mundo. - Pontos de código suplementares no intervalo
U+10000..U+10FFFF
. Este intervalo de 21 bits fornece mais de um milhão de pontos de código adicionais que podem ser usados para idiomas menos conhecidos e outros fins, como emojis.
O diagrama a seguir ilustra a relação entre o BMP e os pontos de código suplementares.
Unidades de código UTF-16
UTF-16 (16-bit Unicode Transformation Format) é um sistema de codificação de caracteres que usa unidades de código de 16 bits para representar pontos de código Unicode. O .NET usa UTF-16 para codificar o texto em um string
arquivo . Uma char
instância representa uma unidade de código de 16 bits.
Uma única unidade de código de 16 bits pode representar qualquer ponto de código no intervalo de 16 bits do Plano Multilingue Básico. Mas para um ponto de código no intervalo suplementar, duas char
instâncias são necessárias.
Pares substitutos
A tradução de dois valores de 16 bits para um único valor de 21 bits é facilitada por um intervalo especial chamado pontos de código substitutos, de U+D800
para U+DFFF
(decimal 55.296 a 57.343), inclusive.
O diagrama a seguir ilustra a relação entre o BMP e os pontos de código substitutos.
Quando um ponto de código substituto alto (U+D800..U+DBFF
) é imediatamente seguido por um ponto de código substituto baixo (U+DC00..U+DFFF
), o par é interpretado como um ponto de código suplementar usando a seguinte fórmula:
code point = 0x10000 +
((high surrogate code point - 0xD800) * 0x0400) +
(low surrogate code point - 0xDC00)
Aqui está a mesma fórmula usando notação decimal:
code point = 65,536 +
((high surrogate code point - 55,296) * 1,024) +
(low surrogate code point - 56,320)
Um ponto de código substituto alto não tem um valor numérico maior do que um ponto de código substituto baixo . O ponto de código substituto alto é chamado de "alto" porque é usado para calcular os 10 bits de ordem superior de um intervalo de pontos de código de 20 bits. O ponto de código substituto baixo é usado para calcular os 10 bits de ordem inferior.
Por exemplo, o ponto de código real que corresponde ao par 0xD83C
substituto e 0xDF39
é calculado da seguinte forma:
actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
= 0x10000 + ( 0x003C * 0x0400) + 0x0339
= 0x10000 + 0xF000 + 0x0339
= 0x1F339
Aqui está o mesmo cálculo usando notação decimal:
actual = 65,536 + ((55,356 - 55,296) * 1,024) + (57,145 - 56320)
= 65,536 + ( 60 * 1,024) + 825
= 65,536 + 61,440 + 825
= 127,801
O exemplo anterior demonstra que "\ud83c\udf39"
é a codificação UTF-16 do U+1F339 ROSE ('🌹')
ponto de código mencionado anteriormente.
Valores escalares Unicode
O termo valor escalar Unicode refere-se a todos os pontos de código diferentes dos pontos de código substitutos. Em outras palavras, um valor escalar é qualquer ponto de código ao qual é atribuído um caractere ou que pode ser atribuído um caractere no futuro. "Caractere" aqui refere-se a qualquer coisa que possa ser atribuída a um ponto de código, o que inclui coisas como ações que controlam como o texto ou os caracteres são exibidos.
O diagrama a seguir ilustra os pontos de código de valor escalar.
O Rune tipo como um valor escalar
Importante
O Rune
tipo não está disponível no .NET Framework.
No .NET, o System.Text.Rune tipo representa um valor escalar Unicode.
Os Rune
construtores validam que a instância resultante é um valor escalar Unicode válido, caso contrário, eles lançam uma exceção. O exemplo a seguir mostra o código que instancia Rune
instâncias com êxito porque a entrada representa valores escalares válidos:
Rune a = new Rune('a');
Rune b = new Rune(0x0061);
Rune c = new Rune('\u0061');
Rune d = new Rune(0x10421);
Rune e = new Rune('\ud801', '\udc21');
O exemplo a seguir lança uma exceção porque o ponto de código está no intervalo substituto e não faz parte de um par substituto:
Rune f = new Rune('\ud801');
O exemplo a seguir lança uma exceção porque o ponto de código está além do intervalo suplementar:
Rune g = new Rune(0x12345678);
Rune Exemplo de uso: alterar maiúsculas e minúsculas
Uma API que usa um char
e assume que está trabalhando com um ponto de código que é um valor escalar não funciona corretamente se o for de um par substituto char
. Por exemplo, considere o seguinte método que chama Char.ToUpperInvariant cada char um em um string:
// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string ConvertToUpperBadExample(string input)
{
StringBuilder builder = new StringBuilder(input.Length);
for (int i = 0; i < input.Length; i++) /* or 'foreach' */
{
builder.Append(char.ToUpperInvariant(input[i]));
}
return builder.ToString();
}
Se o input
string contiver a letra er
Minúscula Deseret (𐑉
), este código não o converterá em maiúsculas (𐐡
). O código chama char.ToUpperInvariant
separadamente em cada ponto U+D801
de código substituto e U+DC49
. Mas U+D801
não tem informação suficiente por si só para identificá-lo como uma letra minúscula, então char.ToUpperInvariant
deixa-o em paz. E lida U+DC49
da mesma forma. O resultado é que o input
string "minúsculo" não é convertido em maiúsculo.
Aqui estão duas opções para converter corretamente um string em maiúsculo:
Chame String.ToUpperInvariant a entrada string em vez de iterar
char
-por-char
. Ostring.ToUpperInvariant
método tem acesso a ambas as partes de cada par substituto, para que possa lidar com todos os pontos de código Unicode corretamente.Itere através dos valores escalares Unicode como
Rune
instâncias em vez dechar
instâncias, como mostrado no exemplo a seguir. Como umaRune
instância é um valor escalar Unicode válido, ela pode ser passada para APIs que esperam operar em um valor escalar. Por exemplo, chamar Rune.ToUpperInvariant como mostrado no exemplo a seguir dá resultados corretos:static string ConvertToUpper(string input) { StringBuilder builder = new StringBuilder(input.Length); foreach (Rune rune in input.EnumerateRunes()) { builder.Append(Rune.ToUpperInvariant(rune)); } return builder.ToString(); }
Outras Rune APIs
O Rune
tipo expõe análogos de muitas das char
APIs. Por exemplo, os seguintes métodos espelham APIs estáticas no char
tipo:
Para obter o valor escalar bruto de uma Rune
instância, use a Rune.Value propriedade.
Para converter uma Rune
instância de volta em uma sequência de char
s, use Rune.ToString ou o Rune.EncodeToUtf16 método.
Como qualquer valor escalar Unicode é representável por um único char
ou por um par substituto, qualquer Rune
instância pode ser representada por, no máximo, 2 char
instâncias. Use Rune.Utf16SequenceLength para ver quantas char
instâncias são necessárias para representar uma Rune
instância.
Para obter mais informações sobre o tipo .NETRune
, consulte a referência da Rune
API.
Aglomerados de grafemas
O que parece um caractere pode resultar de uma combinação de vários pontos de código, então um termo mais descritivo que é frequentemente usado no lugar de "caractere" é agrupamento de grafema. O termo equivalente no .NET é elemento text.
Considere as string
instâncias "a", "á", "á" e "👩🏽🚒
". Se o seu sistema operacional manipulá-los conforme especificado pelo padrão Unicode, cada uma dessas string
instâncias aparecerá como um único elemento de texto ou cluster de grafemas. Mas os dois últimos são representados por mais de um ponto de código de valor escalar.
O string "a" é representado por um valor escalar e contém uma
char
instância.U+0061 LATIN SMALL LETTER A
O string "á" é representado por um valor escalar e contém uma
char
instância.U+00E1 LATIN SMALL LETTER A WITH ACUTE
O string "á" parece o mesmo que "á", mas é representado por dois valores escalares e contém duas
char
instâncias.U+0061 LATIN SMALL LETTER A
U+0301 COMBINING ACUTE ACCENT
Finalmente, o string "
👩🏽🚒
" é representado por quatro valores escalares e contém setechar
instâncias.U+1F469 WOMAN
(gama suplementar, requer um par substituto)U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4
(gama suplementar, requer um par substituto)U+200D ZERO WIDTH JOINER
U+1F692 FIRE ENGINE
(gama suplementar, requer um par substituto)
Em alguns dos exemplos anteriores - como o modificador de acento combinado ou o modificador de tom de pele - o ponto de código não é exibido como um elemento autônomo na tela. Em vez disso, serve para modificar a aparência de um elemento de texto que veio antes dele. Esses exemplos mostram que podem ser necessários vários valores escalares para compor o que pensamos como um único "caractere" ou "cluster de grafemas".
Para enumerar os clusters de grafema de um string
, use a StringInfo classe como mostrado no exemplo a seguir. Se você estiver familiarizado com o Swift, o tipo .NET StringInfo
é conceitualmente semelhante ao tipo do character
Swift.
Exemplo: ocorrências de elementos count char, Runee text
Em APIs .NET, um cluster de grafema é chamado de elemento de texto. O método a seguir demonstra as diferenças entre char
, Rune
, e instâncias de elemento de texto em um string
:
static void PrintTextElementCount(string s)
{
Console.WriteLine(s);
Console.WriteLine($"Number of chars: {s.Length}");
Console.WriteLine($"Number of runes: {s.EnumerateRunes().Count()}");
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(s);
int textElementCount = 0;
while (enumerator.MoveNext())
{
textElementCount++;
}
Console.WriteLine($"Number of text elements: {textElementCount}");
}
PrintTextElementCount("a");
// Number of chars: 1
// Number of runes: 1
// Number of text elements: 1
PrintTextElementCount("á");
// Number of chars: 2
// Number of runes: 2
// Number of text elements: 1
PrintTextElementCount("👩🏽🚒");
// Number of chars: 7
// Number of runes: 4
// Number of text elements: 1
Exemplo: dividir string instâncias
Ao dividir string
instâncias, evite dividir pares substitutos e clusters de grafemas. Considere o seguinte exemplo de código incorreto, que pretende inserir quebras de linha a cada 10 caracteres em um string:
// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string InsertNewlinesEveryTencharsBadExample(string input)
{
StringBuilder builder = new StringBuilder();
// First, append chunks in multiples of 10 chars
// followed by a newline.
int i = 0;
for (; i < input.Length - 10; i += 10)
{
builder.Append(input, i, 10);
builder.AppendLine(); // newline
}
// Then append any leftover data followed by
// a final newline.
builder.Append(input, i, input.Length - i);
builder.AppendLine(); // newline
return builder.ToString();
}
Como esse código enumera instâncias, um par substituto char
que passa por um limite de 10char
será dividido e uma nova linha injetada entre eles. Essa inserção introduz corrupção de dados, porque os pontos de código substitutos são significativos apenas como pares.
O potencial de corrupção de dados não será eliminado se você enumerar Rune
instâncias (valores escalares) em vez de char
instâncias. Um conjunto de instâncias pode compor um cluster de Rune
grafema que se estende por um limite de 10char
. Se o conjunto de clusters de grafema estiver dividido, não poderá ser interpretado corretamente.
Uma abordagem melhor é quebrar a contagem de clusters de grafema string , ou elementos de texto, como no exemplo a seguir:
static string InsertNewlinesEveryTenTextElements(string input)
{
StringBuilder builder = new StringBuilder();
// Append chunks in multiples of 10 chars
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(input);
int textElementCount = 1;
while (enumerator.MoveNext())
{
builder.Append(enumerator.Current);
if (textElementCount % 10 == 0 && textElementCount > 0)
{
builder.AppendLine(); // newline
}
textElementCount++;
}
// Add a final newline.
builder.AppendLine(); // newline
return builder.ToString();
}
Como observado anteriormente, antes do .NET 5, a classe tinha um bug fazendo com que alguns clusters de grafema StringInfo
fossem manipulados incorretamente.
UTF-8 e UTF-32
As seções anteriores se concentraram no UTF-16 porque é isso que o .NET usa para codificar string
instâncias. Existem outros sistemas de codificação para Unicode - UTF-8 e UTF-32. Essas codificações usam unidades de código de 8 bits e unidades de código de 32 bits, respectivamente.
Como UTF-16, UTF-8 requer várias unidades de código para representar alguns valores escalares Unicode. UTF-32 pode representar qualquer valor escalar em uma única unidade de código de 32 bits.
Aqui estão alguns exemplos mostrando como o mesmo ponto de código Unicode é representado em cada um desses três sistemas de codificação Unicode:
Scalar: U+0061 LATIN SMALL LETTER A ('a')
UTF-8 : [ 61 ] (1x 8-bit code unit = 8 bits total)
UTF-16: [ 0061 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 00000061 ] (1x 32-bit code unit = 32 bits total)
Scalar: U+0429 CYRILLIC CAPITAL LETTER SHCHA ('Щ')
UTF-8 : [ D0 A9 ] (2x 8-bit code units = 16 bits total)
UTF-16: [ 0429 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 00000429 ] (1x 32-bit code unit = 32 bits total)
Scalar: U+A992 JAVANESE LETTER GA ('ꦒ')
UTF-8 : [ EA A6 92 ] (3x 8-bit code units = 24 bits total)
UTF-16: [ A992 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 0000A992 ] (1x 32-bit code unit = 32 bits total)
Scalar: U+104CC OSAGE CAPITAL LETTER TSHA ('𐓌')
UTF-8 : [ F0 90 93 8C ] (4x 8-bit code units = 32 bits total)
UTF-16: [ D801 DCCC ] (2x 16-bit code units = 32 bits total)
UTF-32: [ 000104CC ] (1x 32-bit code unit = 32 bits total)
Como observado anteriormente, uma única unidade de código UTF-16 de um par substituto não tem sentido por si só. Da mesma forma, uma única unidade de código UTF-8 não tem sentido por si só se estiver em uma sequência de dois, três ou quatro usados para calcular um valor escalar.
Nota
A partir do C# 11, você pode representar literais UTF-8 string usando o sufixo "u8" em um literal string. Para obter mais informações sobre literais UTF-8 string , consulte a seção "string literais" do artigo sobre tipos de referência incorporados no Guia C#.
Dianness
No .NET, as unidades de código UTF-16 de a string são armazenadas na memória contígua como uma sequência de inteiros de 16 bits (char
instâncias). Os bits de unidades de código individuais são dispostos de acordo com a endianness da arquitetura atual.
Em uma arquitetura little-endian, a string consistência dos pontos [ D801 DCCC ]
de código UTF-16 seria disposta na memória como os bytes [ 0x01, 0xD8, 0xCC, 0xDC ]
. Em uma arquitetura big-endian, esse mesmo string seria disposto na memória como os bytes [ 0xD8, 0x01, 0xDC, 0xCC ]
.
Os sistemas informáticos que comunicam entre si devem chegar a acordo sobre a representação dos dados que atravessam o fio. A maioria dos protocolos de rede usa UTF-8 como um padrão ao transmitir texto, em parte para evitar problemas que podem resultar de uma máquina big-endian se comunicando com uma máquina little-endian. A string consistência dos pontos [ F0 90 93 8C ]
de código UTF-8 será sempre representada como os bytes [ 0xF0, 0x90, 0x93, 0x8C ]
, independentemente da endianness.
Para usar UTF-8 para transmitir texto, os aplicativos .NET geralmente usam código como o exemplo a seguir:
string stringToWrite = GetString();
byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0, stringAsUtf8Bytes.Length);
No exemplo anterior, o método Encoding.UTF8.GetBytes decodifica o UTF-16 string
de volta em uma série de valores escalares Unicode e, em seguida, recodifica esses valores escalares em UTF-8 e coloca a sequência resultante em uma byte
matriz. O método Encoding.UTF8.GetString executa a transformação oposta, convertendo uma matriz UTF-8 byte
em uma UTF-16 string
.
Aviso
Como o UTF-8 é comum na internet, pode ser tentador ler bytes brutos do fio e tratar os dados como se fossem UTF-8. No entanto, você deve validar que ele está realmente bem formado. Um cliente mal-intencionado pode enviar UTF-8 mal formado para o seu serviço. Se você operar com esses dados como se estivessem bem formados, isso pode causar erros ou falhas de segurança em seu aplicativo. Para validar dados UTF-8, você pode usar um método como Encoding.UTF8.GetString
, que executará a validação durante a conversão dos dados de entrada em um string
arquivo .
Codificação bem formada
Uma codificação Unicode bem formada é uma string das unidades de código que podem ser decodificadas de forma inequívoca e sem erro em uma sequência de valores escalares Unicode. Dados bem formados podem ser transcodificados livremente entre UTF-8, UTF-16 e UTF-32.
A questão de saber se uma sequência de codificação é bem formada ou não não está relacionada com a endianidade da arquitetura de uma máquina. Uma sequência UTF-8 mal formada é mal formada da mesma forma em máquinas big-endian e little-endian.
Aqui estão alguns exemplos de codificações mal formadas:
Em UTF-8, a sequência
[ 6C C2 61 ]
é mal formada porqueC2
não pode ser seguida por61
.Em UTF-16, a sequência
[ DC00 DD00 ]
(ou, em C#, o ) é mal formada porque o substitutoDC00
baixo não pode ser seguido por outro substituto string"\udc00\udd00"
DD00
baixo.Em UTF-32, a sequência
[ 0011ABCD ]
é mal formada porque0011ABCD
está fora do intervalo de valores escalares Unicode.
No .NET, string
as instâncias quase sempre contêm dados UTF-16 bem formados, mas isso não é garantido. Os exemplos a seguir mostram código C# válido que cria dados UTF-16 mal formados em string
instâncias.
Um literal mal formado:
const string s = "\ud800";
Uma substring que divide um par substituto:
string x = "\ud83e\udd70"; // "🥰" string y = x.Substring(1, 1); // "\udd70" standalone low surrogate
APIs como Encoding.UTF8.GetString
nunca retornar instâncias mal formadas string
. Encoding.GetString
e métodos detetam Encoding.GetBytes
sequências mal formadas na entrada e executam a substituição de caracteres ao gerar a saída. Por exemplo, se Encoding.ASCII.GetString(byte[])
vir um byte não-ASCII na entrada (fora do intervalo U+0000..U+007F), ele insere um '?' na instância retornada string
. Encoding.UTF8.GetString(byte[])
substitui sequências UTF-8 mal formadas por U+FFFD REPLACEMENT CHARACTER ('�')
na instância retornada string
. Para obter mais informações, consulte o padrão Unicode, Seções 5.22 e 3.9.
As classes internas Encoding
também podem ser configuradas para lançar uma exceção em vez de executar a substituição de caracteres quando sequências mal formadas são vistas. Essa abordagem é frequentemente usada em aplicativos sensíveis à segurança, onde a substituição de caracteres pode não ser aceitável.
byte[] utf8Bytes = ReadFromNetwork();
UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
string asString = encoding.GetString(utf8Bytes); // will throw if 'utf8Bytes' is ill-formed
Para obter informações sobre como usar as classes internas, consulte Como usar classes de Encoding
codificação de caracteres no .NET.