Applied Microsoft .NET Framework Programming
Shared by: HC120914021449
-
Stats
- views:
- 7
- posted:
- 9/13/2012
- language:
- Portuguese
- pages:
- 88
Document Sample


Working with Text
• Neste capítulo, explicarei a mecânica associada ao processamento com caracteres
individuais e com strings no .NET Framework. Começarei por falar da estrutura
System.Char e sobre as várias formas de manipulação dos caracteres. Depois, irei falar
sobre o tipo System.String, que permite manipular strings imutáveis. (Depois de criados
os strings imutáveis não podem ser modificados de forma nenhuma.) Depois de analisar
os strings, irei mostrar como se realizam eficientemente várias operações para construir
dinamicamente um string, usando a classe System.Text.StringBuilder. Depois de tratar
os mecanismos básicos dos strings, irei depois descrever como se formatam objectos em
strings e como se podem tornar persistentes ou transmitir strings usando várias formas
de codificação.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 1 de 88
Tradução: Carlos Martins 12. Working with Text
Characters (1)
• No .NET Framework, os caracteres são sempre representados pelo código Unicode
expresso a 16 bits, facilitando o desenvolvimento de aplicações globais. Um caracter é
representado com uma instância da estrutura System.Char (um tipo valor). O tipo
System.Char é muito simples. Oferece dois campos constantes públicos: MinValue,
definido como 0x0000, e MaxValue, definido com 0xffff.
• Dada uma instância de Char, poderá invocar o método estático GetUnicodeCategory
que devolve o um valor do tipo enumerado System.Globalizantion.UnicodeCategory.
Este valor indica se o caracter é um caracter de controlo, um símbolo de moeda, uma
letra minúscula, uma letra maiúscula, um caracter de pontuação, um símbolo
matemático, etc. (como definido pelo standard Unicode 3.0).
• Para facilitar a escrita de programas, o tipo Char também oferece alguns métodos
estáticos: IsDigit, IsLetter, IsWhiteSpace, IsUpper, IsLower, IsPunctuation,
IsLetterOrDigit, IsControl, IsSeparator, IsSurrogate e IsSymbol, Todos estes métodos
invocam internamente GetUnicodeCategory e devolvem true ou false. Todos estes
métodos tomam como argumento um caracter ou um String e o índice de um caracter no
String.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 2 de 88
Tradução: Carlos Martins 12. Working with Text
Characters (2)
• É também possível converter um caracter para o seu equivalente maiúscula ou
minúscula, usando os métodos ToUpper ou ToLower. A chamada a cada um desses
métodos converte o caracter usando a informação de cultura associado à thread
invocante (que os métodos obtêm internamente questionando o propriedade estática
System.Threading.Thread.CurrentCulture); em alternativa, é possível passar ao método
uma cultura particular especificando uma instância do tipo System.Globalization.-
CultureInfo. Os métodos ToUpper e ToLower necessitam da informação de cultura,
porque a conversão de letras de minúsculas para maiúsculas, ou vice-versa, é uma
operação que depende da cultura Por exemplo, o turco considera a maiúscula de U+0069
(letra latina i pequeno) como U+0130 (letra latina I com ponto em cima) enquanto que
outras culturas consideram que o resultado é U+0049 (letra latina maiúscula I).
• Além destes membros estáticos, o tipo Char também define alguns métodos de
instância. O método Equals devolve true se duas instâncias de caracteres representarem
o mesmo code point expresso a 16 bit; esta comparação não depende da cultura. O
método CompareTo (definido pela interface IComparable) devolve a comparação de
dois code points; esta comparação também não depende da cultura. O método ToString
devolve um string com um caracter. O oposto a ToString é o método Parse, que toma
um string com um caracter e devolve esse caracter.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 3 de 88
Tradução: Carlos Martins 12. Working with Text
Characters (3)
• O último método, GetNumericValue, devolve o equivalente numérico de um caracter. O
seguinte código exemplifica:
using System;
class App {
static void Main() {
Double d;
// '\u0033 id the "digit 3"
d = Char.GetNumericValue('\u0033'); // '3' would work too
Console.WriteLine(d.ToString()); // Displays "3"
// '\u00bc' is the "vulgar fraction one quarter ('1/4')"
d = Char.GetNumericValue('\u00bc');
Console.WriteLine(d.ToString()); // Displays "0.25"
// 'A' is the "Latin capital letter A"
d = Char.GetNumericValue('A');
Console.WriteLine(d.ToString()); // Displays "-1"
}
}
• Finalmente, existem três técnicas que permitem converter entre vários tipos numéricos
para instâncias de Char e vice versa. Estas técnicas são enumeradas a seguir por ordem
de preferência:
Casting A forma mais simples de converter um Char para um valor numérico como
Int32 e a simples coerção. Das três técnicas esta é a mais eficiente porque o compilador
emite instruções IL para fazer a conversão, não sendo invocados quaisquer métodos.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 4 de 88
Tradução: Carlos Martins 12. Working with Text
Characters (4)
Além disso, algumas linguagens (como o C#) permitem indicar se a conversão deve ser
feita com código checked ou unchecked (discutido no Capítulo 5). Isto permite decidir
de pretendemos que seja lançada, ou não, a excepção System.OverflowException quando
a conversão implica perca de informação. O único inconveniente desta técnica é que o
compilador tem que tratar o tipo do valor desejado como primitivo. Portanto, em VB,
por exemplo, não poderá usar esta técnica para converter Char para UInt16 (ou vice-
versa) porque o VB não considera UInt16 como um tipo primitivo.
Use the Convert type O tipo System.Convert define vários métodos estáticos para
converter um Char para um tipo numérico e vice-versa. Todos esses métodos realizam a
conversão como uma operação checked, provocando o lançamento da excepção
System.OverflowException se da conversão implica perda de informação.
Use the IConvertible interface O tipo Char e todos os tipos numéricos da FCL
implementam a interface IConvertible. Esta interface define métodos como ToUInt16 e
ToChar. Esta técnica tem pior desempenho que qualquer das duas anteriores, porque a
chamada a um método de uma interface requer que a instância seja boxed – o tipo Char
e todos os tipos numéricos são tipos valor. Os métodos de IConvertible lançam
excepções quando o tipo não pode ser convertido (como, por exemplo, na conversão de
Char para Boolean), ou se da conversão resulta perda de informação. (Tenha em
consideração que muitos tipos, como o tipo Char e os tipos numéricos, implementam a
interface IConvertible como implementação explícita de interface.)
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 5 de 88
Tradução: Carlos Martins 12. Working with Text
Characters (5)
Isto significa que é necessário fazer a coerção para uma instância de IConvertible antes
de poder invocar qualquer dos métodos das interface.
• O seguinte código demonstra a utilização destas três técnicas:
using System;
class App {
static void Main() {
Char c;
Int32 n;
// Convert number <-> character using C# casting
c = (Char) 65
Console.WriteLine(c); // Displays "A"
n = (Int32) c;
Console.WriteLine(n); // Displays "65"
c = unchecked((Char) (65536 + 65));
Console.WriteLine(c); // Displays "A"
// Convert number <-> character using Convert
c = Convert.ToChar(65);
Console.WriteLine(c); // Displays "A"
n = Convert.ToInt32(c);
Console.WriteLine(n); // Displays "65"
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 6 de 88
Tradução: Carlos Martins 12. Working with Text
Characters (6)
// This demonstrates Convert's range checking
try {
c = Convert.ToChar(70000); // Too big for 16 bits
Console.WriteLine(c); // Doesn't execute
}
catch (OverflowException) {
Console.WriteLine("Can't convert 70000 to Char.");
}
// Convert number <-> character using IConvertible
c = ((IConvertible) 65).ToChar(null);
Console.WriteLine(c); // Displays "A"
n = ((IConvertible) c).ToInt32(null);
Console.WriteLine(n); // Displays "65"
}
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 7 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (1)
• Um dos tipos mais usados em qualquer aplicação é seguramente o tipo System.String.
Um string representa uma cadeia de caracteres imutável. O tipo String deriva
imediatamente de Object, sendo, por isso, um tipo referência. O tipo String também
implementa várias interfaces: IComparable, ICloneable, IConvertible e IEnumerable.
Constructing Strings
• Muitas linguagens de programação (incluindo o C#) consideram o tipo String como tipo
primitivo – isto é, o compilador permite exprimir literais string directamente no código
fonte. O compilador coloca estes strings literais na metadata do módulo, sendo acedidos
em tempo de execução através de um mecanismo designado por string interning
(explicado a seguir).
• Em C#, não se pode usar o operador new para construir um objecto do tipo String:
using System;
class App {
static void Main() {
String s = new String("Hi there."); // <-- Error
}
}
• Deverá ser usada a seguinte sintaxe especial simplificada:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 8 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (2)
using System;
class App {
static void Main() {
String s = "Hi there.";
}
}
• Se compilar este código e analisar o IL, usando o ILDasm.exe, verá o seguinte:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 7 (0x7)
.maxstack 1
.locals (string V_0)
IL_0000: ldstr "Hi there."
IL_0005 stdloc.0
IL_0006: ret
} // end of method App:Main
• A instrução IL que constrói uma nova instância de um objecto é newobj. No entanto, não
aparece nenhuma instrução newobj no código IL mostrado acima. Em alternativa, vemos
a instrução especial «ldstr» (load string), que constrói um objecto String a partir de um
string literal obtido a partir da metadata. Na realidade existe uma forma especial de
construir as instâncias do tipo String.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 9 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (3)
• Em casos raros, pode ser necessário uma instância de String que não seja construída a
partir de um string literal contido na metadata. Para tal, deverá usar o operador «new»
do C# invocando um dos construtores definidos pelo tipo String. Os construtores que
tomam como parâmetros «Char*» ou «SByte*» foram concebidos para serem invocados
a partir de código escrito em MC++. Estes construtores criam objectos string, a partir de
um array de Char ou de signed bytes. Os outros construtores, que não têm parâmetros
do tipo apontador, podem ser invocados com qualquer linguagem managed.
• O C# oferece alguma sintaxe especial que ajuda a incluir strings literais no código fonte.
Para os caracteres especiais, como new line, carriage return e backspace, o C# usa o
mecanismo de evasão familiar aos programadores de C++:
// String containing carriage-return and new line characters
String s = "Hi\r\nthere.";
Importante Embora no exemplo anterior se tenham incluído os caracteres carriage-
return e new line directamente no string, não recomendo esta prática. O tipo
System.Environment define a propriedade read-only Newline que devolve um string com
os caracteres necessários para provocar uma mudança de linha. Esta propriedade é
sensível à plataforma, e devolve sempre o string correcto. Por exemplo, se o CLR for
portado para UNIX, a propriedade Newline devolverá um string apenas com o caracter
«\n». A definição do string anterior de forma correcta será:
String s = "Hi" + Environment.Newline + "there.";
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 10 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (4)
• É possível concatenar vários strings para formar um único string, usando o operador +
do C#, da seguinte forma:
// Three literal strings concatenated to form a single literal string
String s = "Hi" + " " + "there.";
• Neste código, como todos os strings são literais, o compilador concatena-os em tempo
de compilação, e acaba por colocar na metadata apenas um string «Hi there». Quando se
usa o operador + sobre strings não literais, a concatenação é realizada em tempo de
execução. Para concatenar vários strings em tempo de execução, não se deve usar o
operador + porque este operador conduz à criação vários objectos string no managed
heap. Deve usar-se antes o tipo System.Text.StringBuilder (explicado mais tarde neste
capítulo).
• Finalmente, o C# oferece uma forma especial de declarar um string na qual todos os
caracteres entre aspas são considerados parte do string. Estas declarações especiais são
designados verbatim strings e são tipicamente usados para especificar o path de um
ficheiro, ou directório, ou quando se trabalha com expressões regulares. Exemplo:
// Specifying the pathname of an application
String file = "C:\\Windows\\System32\\Notepad.exe";
// Specifying the pathname of an application using a verbatim string
String file = @"C:\Windows\System32\Notepad.exe";
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 11 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (5)
• Estas duas linhas de código produzem o mesmo resultado. O símbolo @ antes do string
na segunda linha diz ao compilador que se trata de verbatim string, fazendo com que
este trate os caracteres backslash como caracteres backslash e não como caracteres de
evasão, tornando o nome do ficheiro mais legível.
• Agora que vimos como se constrói um string vamos analisar algumas das operações que
podemos realizar sobre objectos string.
Strings Are Immutable
• A coisa mais importante a saber acerca do objecto String é que este é imutável; isto é,
uma vez criado, o string não pode crescer, diminuir ou ver modificado qualquer dos seus
caracteres. O facto dos strings serem imutáveis tem várias vantagens. Primeiro, permite
fazer operações sobre o string sem realmente o alterar:
if (s.ToLower().SubString(10, 20).EndsWith("exe")) {
...
}
• Neste código, ToLower devolve um novo string; não são modificados os caracteres do
string «s». O método SubString opera sobre o string devolvido por ToLower, e devolve
um novo string, que é depois analisado por EndsWith. Os dois strings temporários
criados por ToLower e SubString ficarão de imediato inatingíveis pelo que serão
colectados na próxima vez que for executado o GC.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 12 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (6)
Estes objectos de vida muito curta são colectados rapidamente e, normalmente, não
compensa investir tempo a sofisticar o algoritmo para melhorar o desempenho.
• O facto dos strings serem imutáveis também significa que não se coloca a necessidade
de sincronizar as threads quando são manipulados strings em concorrência. Quando dois
strings são iguais é possível que duas referências se refiram ao mesmo objecto string e
não a strings diferentes. Isto poderá reduzir significativamente o número de strings em
memória - é a isto que se refere a técnica string interning.
• Por razões de performance, o tipo String está fortemente integrado no CLR.
Especificamente, o CLR conhece o layout exacto dos campos definidos pelo tipo String,
e acede directamente a estes tipos. Este acesso directo implica um baixo custo de
desenvolvimento: a classe String é uma classe sealed. Se fosse possível definir uma
classe usando String como classe base, seria possível acrescentar novos campos e
quebrar as assunções feitas pelo CLR. Além disso, seria possível quebrar algumas das
premissas que o CLR assume pelo facto dos objectos String serem imutáveis.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 13 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (7)
Comparing Strings
• A comparação é provavelmente a operação mais comum realizada sobre strings. O tipo
String define vários métodos estáticos e métodos de instância para comparar strings. A
Tabela 12-1 enumera esses métodos. Além destes métodos, tipo String define
sobrecargas para os operadores == e !=. Esses operadores são implementados
internamente usando o método estático String.Equals.
• Existem duas razões para comparar dois strings. A primeira, é para determinar se os dois
strings representam essencialmente o mesmo string. A segunda é para ordenar as
strings, usualmente para apresentar ao utilizador. Para determinar se dois strings são
iguais, pode usar o método CompareOrdinal. Este método é mais rápido porque
compara apenas os valores dos códigos dos caracteres nos strings. No entanto, alguns
strings são logicamente iguais mesmo sem terem os mesmos códigos dos caracteres.
• Para determinar se dois strings são iguais, deve invocar o método Compare. Este
método usa internamente tabelas de ordenação específicas da cultura (como definido
pelo standard Unicode 3.1) que fazem parte do próprio .NET Framework. Como estas
tabelas fazem parte do .NET Framework, todas as versões do .NET –
independentemente da plataforma subjacente – comparam e ordenam os strings da
mesma forma. Quando se comparam dois strings pela igualdade, podemos usar o
método Compare para fazer comparação case-sensitive, ou case-insensitive, consoante
as necessidades da aplicação.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 14 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (8)
Table 12-1 Methods for Comparing Strings
Member Member Description
Type
Compare Static Returns how two strings should be sorted with respect to each other.
method Unlike the CompareTo method, this method gives you control over the
culture used (via a CultureInfo object) and the case sensitivity. If you
need advanced string comparison functionality, see the discussion of the
CompareInfo class later in this section.
CompareTo Instance Returns how two strings should be sorted with respect to each other.
Method Note that this method always uses the CuktureInfo object associated with
the calling thread.
StartsWith Instance Returns true if the string starts/ends with the specified string. The
EndsWith Method comparison is always case sensitive and uses CultureInfo object
associated with the calling thread. To perform the same operations with
case insensitivity, use CompareInfo's IsPrefix e IsSuffix methods.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 15 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (9)
Table 12-1 Methods for Comparing Strings (continued)
Member
Member Type Description
CompareOrdinal Static Returns true if two strings have the same set of characters. Unlike the
method other compare methods, this method simply compares the code values of
the characters in the string; it does not do a culturally aware
comparison, and the comparison is always case sensitive. This method is
faster than other compare methods, but it shouldn't be used for sorting
strings that are destined to be shown to a user since the resulting set of
strings might not be ordered way a user would expect. For example,
CompareOrdinal will sort "a" after "A" since the code value of "a" is a
larger value then the code value for "A"
Equals Static and Returns true if two strings have same set of characters. Internally,
instance Equals calls String's static CompareOrdinal method. The static Equals
methods method first checks whether the two references refer to the same object;
if they do, it returns true instead of comparing the string's individual
characters. Comparing references first improves performance greatly
when using the interned string mechanism, which I'll describe shortly.
GetHashCode Instance Returns a hash code for the string.
method
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 16 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (10)
Importante Quando comparar strings, ou converter para minúsculas ou maiúsculas,
deverá usar InvariantCulture. Por exemplo, deve usar InvariantCulture quando
comparar e converter pathnames e filenames, chaves dos registry e respectivos valores,
reflection strings, marcas XML, nomes de atributos XML, e quaisquer outros strings
programáticos. Na realidade, só deve ser usada cultura específica quando se comparam
e convertem strings que se destinam a mostrar ao utilizador. Neste caso, a cultura que
utiliza deve ser a linguagem escolhida pelo utilizador e, opcionalmente, o país.
• O seguinte código demonstra a diferença entre chamar CompareOrdinal e Compare:
using System;
using System.Globalization;
class App {
static void Main() {
String s1 = "Strass", s2 = "Straβ";
Int32 x;
// CompareOrdinal returns nonzero: strings have different values.
x = String.CompareOrdinal(s1, s2);
Console.WriteLine("CompareOrdinal: '{0}' {2} '{1}'", s1, s2,
(x == 0) ? "equals" : " "does not equal");
// Compare returns zero: strings have the same value.
x = new CultureInfo("de-DE").CompareInfo.Compare(s1, s2);
Console.WriteLine("Compare: '{0}' {2} '{1}'", s1, s2,
(x == 0) ? "equals" : " "does not equal");
}
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 17 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (11)
Construindo e executando este código produz o seguinte output:
CompareOrdinal: 'Strass' does not equal 'Straβ'
Compare: 'Strass' equals 'Straβ'
• Deverá também usar o método Compare para ordenar strings. No entanto, deverá usar o
Compare para realizar comparações case-sensitive quando compara strings para
ordenação. A razão é que dois strings que diferem apenas pelo case são considerados
iguais, e quando ordena strings numa lista, a ordem dos strings iguais pode ser diferente
de cada vez que a aplicação executa; isto pode confundir o utilizador.
• Internamente, o método String.Compare obtém a CurrentCulture associada à thread
invocante e depois acede à propriedade CultureInfo.CompareInfo. Esta propriedade
devolve uma referência para um objecto System.Globalization.CompareInfo. Uma vez
que o objecto CompareInfo encapsula as tabelas de comparação função da cultura, existe
apenas um objecto CompareInfo por cultura.
• O método String.Compare determina o objecto CultureInfo e chama o seu método
Compare. O método String.Compare permite especificar comparação case-sensitive, ou
não. O método CompareInfo.Compare expõe um controlo mais rico sobre a
comparação, que é necessário em algumas aplicações. Essas aplicações terão que obter
uma referência para o objecto CompareInfo desejado e chamar o método Compare.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 18 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (12)
• Especificamente, algumas sobrecargas do método CompareInfo.Compare tomam com
parâmetro enumerado do tipo CompareOptions, que define os seguintes símbolos:
IgnoreCase, IgnoreKanaType, IgnoreNonSpace, IgnoreSymbols, IgnoreWidth, None,
Ordinal e StringSort. Para uma descrição completa destes símbolos, consulte a
documentação do .NET Framework.
• O seguinte código demonstra a utilização da cultura quando se comparam strings:
using System;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using System.Threading;
class App {
static void Main() {
StringBuilder sb = new StringBuilder();
String[] sign = new String[] {"<", "=", ">"};
Int32 x;
// The following code demonstrates how strings compare
// differently for different cultures.
String s1 = "coté", s2 = "côte";
// Sorting strings for French in France
x = new CultureInfo("fr-FR").CompareInfo.Compare(s1, s2);
sb.AppendFormat("fr-FR Compare: {0} {2} {1}", s1, s2, sign[x + 1]);
sb.Append(Environment.Newline);
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 19 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (13)
// Sorting strings for Japanese in Japan
x = new CultureInfo("ja-JP").CompareInfo.Compare(s1, s2);
sb.AppendFormat("ja-JP Compare: {0} {2} {1}",
s1, s2, sign[x + 1]);
sb.Append(Environment.Newline);
// Sorting strings for the thread's culture
x = Thread.CurrentThread.CurrentCulture.CompareInfo.Compare(s1, s2);
sb.AppendFormat("{0} Compare: {1} {3} {2}",
Thread.CurrentThread.CurrentCulture.Name, s1, s2, sign[x + 1]);
sb.Append(Environment.Newline);
sb.Append(Environment.Newline);
// The following code demonstrates how to use
// CompareInfo.Compare's advanced options with two Japanese
// strings. These strings represent the word "shinkansen"
// (the name for the Japanese high-speed train) in both
// hiragana and katakana.
...
MessageBox.Show(sb.ToString(), "StringSorting Results");
}
}
• Além do método Compare, a classe CompareInfo define os métodos IndexOf,
IsLastIndexOf, IsPrefix e IsSuffix. Como estes métodos oferecerem sobrecargas que
tomam como parâmetro um valor do tipo enumerado CompareOptions, oferecem maior
possibilidade de controlo que os métodos correspondentes definidos na classe String.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 20 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (14)
String Interning
• Como disse na secção anterior, a comparação de strings é uma operação comum em
muitas aplicações – é também um processamento que pode afectar significativamente o
desempenho. A razão para este impacte na performance, é que as comparações de
strings obrigam à comparação de cada par de caracteres dos dois strings, até que os
caracteres sejam diferentes. Para determinar se um string contém o valor «Hello», têm
que ser comparados dois caracteres cinco vezes. Além disso, se existirem várias
instâncias do string «Hello» em memória, é efectivamente desperdiçada memória
porque os strings são imutáveis. A utilização da memória será mais racional se
memorizarmos apenas um string «Hello».
• Se uma aplicação compara frequentemente strings para determinar igualdade, ou se
esperamos ter muitos objectos string com o mesmo valor, é possível melhorar
substancialmente o desempenho quando se tira partido do mecanismo string interning
do CLR. Para compreender como funciona este mecanismo, analise o seguinte código:
String s = "Hello";
Console.WriteLine(Object.ReferenceEquals("Hello", s));
• Acha que este código mostra na consola True ou False? Muita gente espera False.
Afinal, existem dois objectos string com o valor «Hello» e ReferenceEquals devolve
true apenas se as duas referência apontarem o mesmo objecto. Construindo e
executando este código, veremos que o mesmo mostra True. Vamos ver porquê.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 21 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (15)
• Quando o CLR inicia, cria uma tabela de hash interna na qual as chaves são strings e os
valores são referências para objectos string no managed heap. Inicialmente a tabela está
vazia. Quando o JIT compiler compila este código, procura os string literais na tabela de
hash. O compilador procura o string literal «Hello», e como não existe nenhum, constrói
um novo objecto String no managed heap e acrescenta a referência para esse objecto à
tabela da hash. Depois, o JIT compiler procura pelo segundo string «Hello» na tabela de
hash. Como este string é localizado, nada acontece. Por não existirem mais literais
string no código, este poderá ser executado.
• Quando o código executa, é necessário uma referência para o string «Hello». O CLR
procura «Hello» na tabela de hash que encontra, pelo que devolve uma referência para o
string anteriormente criado. Esta referência é salva na variável «s». Na segunda linha de
código, o CLR volta a procurar «Hello» na tabela de hash que encontra. É passada a
referência para o mesmo String em conjunto com «s» para o método ReferenceEquals,
que devolve true.
• Todos os literais string embebidos no código fonte são sempre colocados na tabela de
hash interna, no momento em que o método que os referencia é JIT compiled. O que
acontecerá com os strings construídos em tempo de execução? O que espera que seja
mostrado pelo seguinte código?
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 22 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (16)
String s1 = "Hello";
String s2 = "Hel";
String s3 = s2 + "lo";
Console.WriteLine(Object.ReferenceEquals(s1, s3));
Console.WriteLine(s1.Equals(s3));
• Neste código, o string referido por s2 («Hel») e o literal string «lo» são concatenados. O
resultado é um objecto construído de novo, s3, que reside no managed heap.
• Este string criado dinamicamente contém «Hello», mas o string não é colocado na
tabela de hash interna. Em consequência, ReferenceEquals devolve false porque as duas
referências apontam objectos diferentes. No entanto, a chamada a Equals devolve true
porque os strings representam o mesmo conjunto de caracteres. Obviamente,
ReferenecEquals tem melhor desempenho do que Equals e o desempenho da aplicação é
bastante melhorado se as comparações de strings compararem referências em vez de
caracteres. Mais, a aplicação necessitará de menos objectos no heap se existisse uma
forma de partilhar os objectos string que representam o mesmo conjunto de caracteres.
• Felizmente, o tipo String oferece dois métodos estáticos que permitem fazer isso:
public static String Intern(String str);
public static String IsInterned(String str);
• O primeiro método, Intern, toma um String e procura-o na tabela de hash interna. Se o
string existir, é devolvida uma referência para o objecto String existente.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 23 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (17)
Se a aplicação deixar de ter uma referência para o objecto String original, o GC libertará
a memória ocupada por esse string. O código anterior poderá ser rescrito para usar o
método Intern:
String s1 = "Hello";
String s2 = "Hel";
String s3 = s2 + "lo";
s3 = String.Intern(s3);
Console.WriteLine(Object.ReferenceEquals(s1, s3));
Console.WriteLine(s1.Equals(s3));
• Agora ReferenceEquals devolve true e a comparação é muito mais rápida. Ainda, o
objecto String originalmente referido por s3 é libertado e será colectado. Este código
efectivamente executa mais lentamente que a versão anterior devido ao processamento
realizado pelo método String.Intern. Só deverá internar strings quando se pretende
comparar um string várias vezes numa aplicação. Caso contrário, o desempenho piora
em vez de o melhorar.
• Tenha em consideração que o garbage collector não pode libertar os strings contidos na
tabela de hash interna, porque esta tabela mantém referências para os objectos. Os
objectos referidos pela tabela de hash interna só poderão ser libertados quando não
existir nenhum AppDomain no processo que os refira. Tenha também em consideração
que o internamento dos strings ocorre na base do processo, significando que um único
objecto string poderá ser acedido por vários AppDomains, optimizando a utilização da
memória.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 24 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (18)
A capacidade de múltiplos AppDomains acederem ao mesmo objecto string também
melhora o desempenho porque os strings não têm que ser marshaled quando são
passados entre AppDomains do mesmo processo; apenas a referência necessita de ser
marshaled.
• Como referi anteriormente, o tipo String também define o método estático IsInterned.
Tal como o método Intern, o método IsInterned toma um String e procura-o na tabela de
hash interna. Se o string se encontra na tabela de hash, o método IsInterned devolve
uma referência para o objecto string internado. Caso contrário, IsInterned devolve null;
este método não acrescenta o string à tabela de hash interna.
• O compilador de C# usa o método IsInterned para permitir que a instrução switch/case
funcione de forma eficiente com strings. Por exemplo, podemos escrever o seguinte
código C#:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 25 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (19)
using System;
class App {
static void Main() {
Lookup("Jeff", "Richter");
Lookup("Fred", "Flintstone");
}
static void Lookup(String firstName, String lastName) {
switch (firstName + " " + lastName) {
case "Jeff Richter":
Console.WriteLine("Jeff");
break;
default:
Console.WriteLine("Unknown");
break;
}
}
}
• Eu compilei este código e usei o ILDasm.exe para analisar o respectivo código IL, que é
apresentado a seguir. Inseri comentários para explicar o que se está a passar.
.method private hidebysig static void Lookup(string firstName,
string lastName) cil managed
{
// Code size 53 (0x35)
.maxstack 3
.locals (object V_0)
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 26 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (20)
// Concatenate firstName, " ", and lastName into a new String
IL_0000: ldarg.0
IL_0001: ldstr " "
IL_0006: ldarg.1
IL_0007: call string [mscorlib]System.String::Concat(string,
string,
string)
// Duplicate the reference to the concatenated string.
IL_000c: dup
// Store a reference to the string in a temporary stack variable.
IL_000d: stloc.0
// If Concat returns null, branch to IL_002a.
IL_000e: brfalse.s IL_002a
// See if the concatenated string is in the internal hash table.
IL_0010: ldloc.0
IL_0011: call string [mscorlib]System.String::IsInterned(string)
// Overwrite the temporary variable with a reference to the interned
// string. Note that null indicates the string wasn't in the hash table.
IL_0016: stloc.0
// Compare the reference of the interned 'switch' string with a
// reference to the interned "Jeff Richter" string.
IL_0017: ldloc.0
IL_0018: ldstr "Jeff Richter"
// If references refer to different String objects, branch to IL_002a.
IL_001d: bne.us.s IL_002a
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 27 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (21)
// The references do match: display "Jeff" to console and return.
IL_001f: ldstr "Jeff"
IL_0024: call void [mscorlib]System.Console.WriteLine(string)
IL_0029: ret
// Display "Unknown" to the console and return.
IL_001f: ldstr "Unknown"
IL_0024: call void [mscorlib]System.Console.WriteLine(string)
IL_0029: ret
} // end of method App::Lookup
• O aspecto importante a notar neste código é que o código IL invoca o método IsInterned
passando o string especificado na instrução switch. Se este método devolver null, o
string não corresponde a nenhum dos strings case, tendo como consequência a execução
do código associado à label default. Caso contrário, se IsInterned determina que o string
existe na tabela de hash interna, é obtida a referência para o objecto String que se
encontra nesta tabela. Esta referência é depois comparada com os endereços dos literais
string associados às labels da instrução switch. A comparação de endereços é muito
mais rápida do que a comparação dos strings.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 28 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (22)
String Pooling
• Quando o compilador analisa o código fonte, processa cada literal string e emitindo cada
string para a metadata do módulo resultante. Se o mesmo literal string aparecer várias
vezes no código fonte, então a emissão de todos esses strings para a metadata irá fazer
crescer a dimensão do ficheiro resultante.
• Para evitar este crescimento, muitos compiladores (incluindo o de C#) emite apenas uma
vez os literais string para a metadata do módulo. Todo o código que referencia o string
é modificado para se referir ao único string da metadata. Esta capacidade do compilador
tratar as múltiplas ocorrências do mesmo string como uma única instância, pode reduzir
substancialmente a dimensão dos módulos. Este processo não é novo – os compiladores
de C/C++ já vinham fazendo isso há vários anos. (O compilador de C/C++ da Microsoft
designa esta feature por string pooling.) Assim, a técnica de string pooling e mais uma
forma de melhorar a performance dos strings, e mais uma peça de conhecimento que
deve ter no seu repertório.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 29 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (23)
Examining a String's Characters
• Embora a comparação de strings seja importante na detecção da igualdade e na
ordenação, é, muitas vezes, necessário processar os caracteres individuais dos strings. O
tipo String oferece vários métodos que facilitam esta tarefa. A Tabela 12-2 enumera
alguns destes métodos.
• O tipo System.Char representa um 16-bit Unicode code value, o que não corresponde
exactamente a um caracter Unicode abstracto. Por exemplo, alguns caracteres Unicode
abstractos são uma combinação de dois code values. Quando combinados, U+0625
(Arabic letter Alef with Hamz bellow) e U+0650 (Arabic Kasra) formam um único
caracter abstracto.
• Além disso, alguns caracteres abstractos Unicode necessitam mais que um valor de 16
bits para os representar; estes caracteres são representados usando dois valores de 16
bits. O primeiro código é designado por high surrogate e o segundo por low surrogate.
Os valores do high surrogate situam-se na gama U+D800 e U+DBFF e os low surrogate
toma valores na gama U+DC00 e U+DFFF. A utilização de surrogates permite que o
Unicode exprima mais do que um milhão de caracteres.
• Os caracteres surrogates raramente são usados nos Estados Unidos e na Europa, mas são
frequentemente usados na Ásia oriental. Para trabalhar correctamente com caracteres
abstractos Unicode, deve usar o tipo System.Globalization.StringInfo.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 30 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (24)
Table 12-2 Methods for Examining String Characters
Member Member Type Description
Length Instance read- Returns the number of characters in the string.
only property
Chars Instance read- Returns the character at the specified index within the string.
only indexer
property
GetEnumerator Instance method Returns an IEnumerator that can be used to iterate over all the
characters in the string.
ToCharArray Instance method Returns a Char[] that contains a portion of the string.
IndexOf Instance methods Returns the index of the first/last character/string matching a
LastIndexOf specified value.
IndexOfAny Instance methods Returns the index of the first/last character matching an array
LastIndexOfAny of special characters.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 31 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (25)
O método estático GetTextElementEnumerator permite adquirir uma instância do tipo
System.Globalization.TextElementEnumerator que permite enumerar todos os caracteres
abstractos Unicode contidos num String.
• Em alternativa, poderá invocar o método estático de StringInfo,
ParseCombiningCharacters, para obter um array de valores Int32. O comprimento do
array indica o número de caracteres abstractos Unicode contidos no string. Cada
elemento deste array identifica um índice do string onde pode ser encontrado o primeiro
code value do caracter Unicode.
• O seguinte código demonstra como se usam os métodos GetTextElementEnumerator e
ParseCombiningCharacters do tipo StringInfo para manipular um string de caracteres
Unicode abstractos:
using System;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
class App {
static void Main() {
// The following string contains combining characters.
String s = "a\u0304\u0308bc\u0327";
EnumTextElements(s);
EnumTextElementIndexes(s);
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 32 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (26)
static void EnumTextElements(String s) {
StringBuilder sb = new StringBuilder();
TextElementEnumerator charEnum = StringInfo.GetTextElementEnumerator(s);
while (charEnum.MoveNext()) {
sb.AppendFormat("Character at index {0} is '{1}'{2}",
charEnum.ElementIndex, charEnum.GetTextElement(),
Environment.Newline());
}
MessageBox.Show(sb.ToString(), "Result of GetTextElementEnumerator");
}
static void EnumTextElementIndexes(String s) {
StringBuilder sb = new StringBuilder();
Int32[] textElemIndex = StringInfo.ParseCombiningCharacters(s);
for (Int32 i = 0; i < textElemIndex.Length; i++) {
sb.AppendFormat("Character {0} starts at index {1}{2}",
i, textElemIndex[i], Environment.Newline);
}
MessageBox.Show(sb.ToString(), "Result of ParseCombiningCharacters");
}
}
• Neste exemplo, estamos a invocar o método estático StringInfo.GetTextElement-
Enumerator, a que passamos um String para obter um objecto do tipo
TextElementEnumerator. Poderemos depois usar este objecto como se usa qualquer
outro enumerador.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 33 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (27)
• O tipo TextElementEnumerator também define a propriedade read-only ElementIndex
que devolve o índice do código no string original onde o caracter começa; o método
GetTextElement devolve o string que contém todos os códigos que definem um caracter.
• Além disso, StringInfo define o método estático ParseCombiningCharacteres que
analisa o string e devolve um array de Int32. Cada elemento do array é o índice do code
point inicial do caracter abstracto. A documentação do .NET Framework mostra um
exemplo de utilização deste método. Repare que a classe StringInfo define um
construtor público, mas trata-se de um bug; não existe nenhuma razão para construir
uma instância do tipo StringInfo.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 34 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (28)
Other String Operations
• O tipo String também define métodos que permitem copiar um string ou parte dele. A
Tabela 12-3 sumaria esses métodos.
• Além dos métodos mostrados na Tabela 12-3, o tipo String define muitos métodos
estáticos e métodos de instância para manipular o string, como Insert, Remove, PadLeft,
Replace, Split, Join, ToLower, Trim, Concat, Format, etc. O importante a reter acerca de
todos estes métodos é os mesmos devolvem novos strings; como os strings são
imutáveis, uma vez criados não pode ser alterados de forma alguma.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 35 de 88
Tradução: Carlos Martins 12. Working with Text
The System.String Type (29)
Table 12-3 Methods for Copying Strings
Member Member Type Description
Clone Instance Returns a reference to the same object (this). This is OK
because String objects are immutable. This method implements
String's ICloneable interface.
Copy Static Returns a new string that is a duplicate of the specified string.
This method is rarely used and exists to help applications that
treat strings as tokens. Normally, strings with the same set of
characters are interned to a single string. This method creates
a new string object so that the references (pointers) are
different even though the strings contains the same characters.
CopyTo Instance Copies a portion of the string's characters to an array of
characters.
SubString Instance Returns a new string representing a portion of the original
string.
ToString Instance Returns a reference to the same object (this).
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 36 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (1)
• Pelo facto do tipo String representar strings imutáveis, a FCL define o tipo
System.Text.StringBuilder que permite construir strings dinamicamente. Podemos
pensar no tipo StringBuilder como construtor para criar instâncias do tipo String. Em
geral, devemos desenhar métodos que tomem como parâmetros instâncias do tipo
String, não do tipo StringBuilder, a menos que se definam métodos que devolvam
strings construídos dinamicamente pelo próprio método.
• Internamente, o objecto StringBuilder tem um campo que refere um array de estruturas
System.Char. Os membros de StringBuilder permitem manipular este array de
caracteres. Quando o string cresce para além da dimensão do array, o tipo StringBuilder
reserva automaticamente um novo array, copia os caracteres do array anterior, e
começa a usar o novo array. O array anterior é depois colectado.
• Quando se termina a utilização do objecto StringBuilder para construir um string,
converte-se o array de caracteres em string com o método StringBuilder.ToString.
Internamente, este método apenas devolve uma referência para um campo string
mantida pela instância de StringBuilder. Isto torna o método ToString muita rápido,
porque o array de caracteres não é copiado.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 37 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (2)
• O String devolvido pelo método StringBuilder.ToString deve ser imutável. Se após ser
invocado o método StringBuilder.ToString, for invocado um método que modifique o
campo string mantido por StringBuilder, é criado automaticamente outro array de
caracteres.
Constructing a StringBuilder Object
• Ao contrário do que acontece com a classe String, o CLR não tem nenhum
conhecimento especial sobre a classe StringBuilder. Além disso, muitas linguagens
(incluindo o C#) não consideram o tipo StringBuilder como tipo primitivo. As instâncias
deste tipo são construídos da mesma forma como se constróem instâncias dos tipos não
primitivos:
StringBuilder sb = new StringBuilder(...);
• O tipo StringBuilder define vários construtores. A tarefa de cada construtor é reservar e
iniciar os três campos internos mantidos por cada instância de StringBuilder:
Maximum capacity Um campo Int32 que especifica o máximo número de caracteres
que podem ser colocados no string. O valor por omissão é Int32.MaxValue (2 biliões). É
pouco comum alterar este valor. Contudo, poderá especificar a capacidade máxima para
garantir que nunca se cria um string acima de certa dimensão. Uma vez construído o
StringBuilder, a sua capacidade máxima não pode ser alterada.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 38 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (3)
Capacity Um campo Int32 que indica a dimensão do array de caracteres mantido pelo
StringBuilder. O valor por omissão é 16. Quando temos alguma ideia sobre o número de
caracteres que vamos colocar na instância de StringBuilder, devemos usar este membro
para definir a capacidade quando constrói a instância de StringBuilder.
Quando se acrescentam caracteres ao array interno, o StringBuilder detecta se o string
cresce para além da capacidade do array. Quando isto acontece, o StringBuilder duplica
o valor do campo capacidade, reserva um novo array (com uma dimensão igual à nova
capacidade) e copia os caracteres do array original para o novo array. O array original
é, depois, garbage collected. O crescimento dinâmico do array tem um impacte
negativo no desempenho; isto pode ser evitado definindo correctamente a capacidade
inicial.
Character array Um array de estruturas System.Char que mantém o conjunto de
caracteres que fazem parte do string. O número de caracteres é sempre menor ou igual
ao valor dos campos capacidade e capacidade máxima. Poderá usar a propriedade
StringBuilder.Length para obter o número de posições usadas no array. O valor da
propriedade Length é sempre menor ou igual ao valor do campo capacidade. Quando se
constrói uma instância de StringBuilder, é possível passar um String para iniciar o array
de caracteres. Se for especificado um string – o array não contém inicialmente nenhum
caracter – isto é, a sua propriedade Length devolve 0.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 39 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (4)
StringBuilder's Members
• Ao contrário do tipo String, as instâncias de StringBuilder não representam strings
imutáveis. Isto significa que a maioria dos membros de StringBuilder alteram o
conteúdo do array de caracteres, não obrigando ao alojamento de novos objectos no
managed heap. O StringBuilder aloja novos objectos em apenas duas situações:
Quando se faz crescer o string para além da capacidade corrente.
Quando se tenta modificar o array depois de ter sido invocado o método
StringBuilder.ToString.
• É também importante saber que por razões de desempenho, os métodos de StringBuilder
não são thread safe. Isto é razoável, porque é pouco frequente que as instâncias de
StringBuilder sejam acedidas por múltiplas threads. Quando for necessário fazer a
manipulação thread-safe de um objecto StringBuilder, tem que se explicitar o código de
sincronização.
• A Tabela 12-4 sumaria os membros de StringBuilder.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 40 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (5)
Table 12-4 StringBuilder's Members
Member
Member Type Description
MaxCapacity Read-only Returns the largest number of characters that can be placed in the
property string.
Capacity Read/write Gets or sets the size of the character array. Trying to set the capacity
property smaller than the string's length throws an ArgumentOutOfRange-
Exception exception..
EnsureCapacity Method Guarantees that the character array is at least the size specified to this
method. If the value passed is larger than the StringBuilder's current
capacity, the current capacity gets bigger. If the capacity is already
bigger than the value passed to this method, no change occurs.
Length Read-only Returns the number of characters in the "string". This is likely be
property smaller then the character array's current capacity.
ToString Method The parameterless version of this method returns a String representing
the StringBuilder's character array field. This method is efficient
because it doesn't create a new String object. Any attempt to modify the
StringBuilder's array causes the StringBuilder to allocate and use a
new array (initializing it from the old array). The version of ToString
that takes startIndex and length parameters creates a new String
representing the desired portion of the StringBuilder's string.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 41 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (6)
Table 12-4 StringBuilder's Members (continued)
Member
Member Type Description
Chars Read/write Gets or sets the character at the specified index into the character
indexer array. In C#, this is an indexer (parameterful property) that you access
property using the array syntax ([]).
AppendInsert Method Appends or inserts a single object into the character array, growing the
array if necessary. The object is converted to strings using the general
format and culture associated with the calling thread.
AppendFormat Method Appends the specified objects into the character array, growing the
array if necessary. The objects are converted to strings using the
formatting and culture information provided by the caller.
AppendFormat is one of the most common methods used with
StringBuilder objects.
Replace Method Replaces one character with another or one string with another from
within the character array.
Remove Method Removes a range of characters from the character array.
Equals Method Returns true only if both StringBuilder object have the same maximum
capacity, capacity, and characters in the array.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 42 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (7)
• Uma coisa importante a reter acerca dos métodos de StringBuilder é que a maioria dos
métodos devolvem uma referência para o próprio objecto. Isto permite uma sintaxe
conveniente para encadear várias operações:
StringBuilder sb = new StringBuilder();
String s = sb.AppendFormat("{0} {1}", "Jeffrey", "Richter").Replace(' ', '-').
Remove(4, 3).ToString();
Console.WriteLine(s); // Jeff-Richter
• Poderá constatar que os tipos String e StringBuilder não têm paridade completa de
métodos; isto é, o tipo String define os métodos ToLower, ToUpper, EndsWith, PadLeft,
Trim, etc. A classe StringBuilder não define nenhum destes métodos. Por seu turno, a
classe StringBuilder define métodos Replace que permitem substituir caracteres, ou
strings, numa parte do string. O facto de não existir paridade completa entre os métodos
destas duas classes, torna necessária a conversão entre String e StringBuilder para
realizar certas operações. Por exemplo, para construir um string, converter todos os
caracteres para maiúsculas e depois inserir um string, requer código como o seguinte:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 43 de 88
Tradução: Carlos Martins 12. Working with Text
Dynamically Constructing a String Efficiently (8)
// Construct a StringBuilder to do string manipulations
StringBuilder sb = new StringBuilder();
// Perform some string manipulations using the StringBuilder.
sb.AppendFormat("{0} {1}", "Jeffrey", "Richter").Replace(' ', '-');
// Convert the StringBuilder to a String in order
// to uppercase all the characters.
String s = sb.ToString().ToLower();
// Clear the StringBuilder (allocates a new Char array).
sb.Length = 0;
// Load the uppercase String into the StringBuilder.
sb.Append(s).Insert(8, "Marc-");
// Convert the StringBuilder back to a String.
s = sb.ToString();
// Display the String to the user.
Console.WriteLine(s); // "JEFFREY-Marc-RICHTER"
• É necessário escrever este código apenas porque o tipo StringBuilder não define todas as
operações que o tipo String define., Espero que no futuro a Microsoft acrescente ao tipo
StringBuilder mais métodos de string, tornando esta classe mais completa.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 44 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (1)
• É frequente ser necessário obter a representação string dos objectos. Isto é normalmente
necessário quando se pretendem mostrar ao utilizador tipos numéricos (como Byte,
Int32, Single, etc.). Como o .NET Framework é uma plataforma orientada por objectos,
cada tipo é responsável por produzir o equivalente string do valor das respectivas
instâncias. Os projectistas da FCL inventaram um padrão que os utilizadores deverão
seguir para fazer este processamento. Nesta secção, iremos descrever este padrão.
• É possível obter a representação string de qualquer objecto com o método ToString.
ToString é um método sem parâmetros definido na classe System.Object e, por isso,
passível de ser invocado sobre uma referência para uma instância de qualquer tipo.
Semanticamente, ToString devolve a representação string do valor corrente do objecto, e
este string deve ser formatado de acordo com a cultura corrente da thread invocante;
isto é, a representação string de um número deve usar o separador decimal e o símbolo
de agrupamento de caracteres, tendo em consideração a informação de cultura da thread
invocante.
• A implementação de ToString em System.Object devolve simplesmente o nome
completo do tipo do objecto. Este valor não é particularmente útil, mas é um valor
razoável por omissão para muitos tipos que não podem oferecer um string com sentido.
Por exemplo, qual deverá ser a representação de um objecto FileStream ou de um
objecto Hashtable?
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 45 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (2)
• Todos os tipos que pretendam oferecer outra forma de representação string deverão
sobrepor o método ToString. Para todos os tipos base da FCL (Byte, Int32, UInt64,
Double, etc.), a Microsoft já redefiniu implementações dependentes da cultura.
Specific Format and Cultures
• O método ToString sem parâmetros tem dois problemas. Primeiro, o caller não tem
controlo sobre a formatação do string. Por exemplo, uma aplicação pode pretender
formatar um número em string exprimindo moeda, um número, ou um número
hexadecimal. Segundo, o caller não pode escolher formatar o string de acordo com uma
cultura específica. Este segundo problema é mais incómodo para código de aplicação do
lado do servidor do que do lado do cliente. Em raras ocasiões, uma aplicação necessita
de formatar um string usando uma cultura diferente daquela associada com a thread
invocante. Para ter maior controlo sobre a formatação da string, será necessário dispor
de um método ToString que permita especificar informação de formatação e cultura.
• Os tipos que pretendem oferecer ao caller a possibilidade de especificar a formatação ou
a cultura devem implementar a interface System.IFormattable:
public interface IFormattable {
String ToString(String format, IFormatProvider formatProvider);
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 46 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (3)
• Na FCL, todos os tipos primitivos (Byte, SByte, Int16/UInt16, Int32/UInt32,
Int64/UInt64, Single, Double, Decimal e DateTime) implementam esta interface. Além
destes tipos, outros, como GUID, também implementam a interface IFormattable.
Finalmente, todos os tipos enumerados implementam automaticamente essa interface,
permitindo obter o string com significado para o valor numérico armazenado na
instância do tipo.
• O método IFormatable.ToString tem dois parâmetros. O primeiro parâmetro, format, é
um string que especifica como deve ser formatado o objecto, O segundo parâmetro,
formatProvider, é uma instância de um tipo que implemente a interface
System.IFormatProvider. Este tipo fornece informação específica da cultura ao método
ToString da forma como vamos discutir a seguir.
• O tipo que implementa o método ToString da interface IFormattable determina quais os
strings de formatação que pretende reconhecer. Quando se passa um formato que o tipo
não reconheça, este deve lançar a excepção System.FormatException.
• Muitos dos tipos que a Microsoft definiu na FCL reconhecem vários formatos. Por
exemplo, o tipo DateTime suporta «d» para apresentar a data compactada, «D» para
apresentar a data completa, «g» para formato geral, «M» para mês/dia, «s» para data
ordenável, «T» para hora, «u» para tempo universal em formato ISO 8601, «U» para
tempo universal em formato longo, «Y» para ano/mês, e ainda outros.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 47 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (4)
Todos os tipos enumerados suportam «G» para geral, «F» para flags, «D» para decimal
e «X» para hexadecimal. Discutirei a formatação dos tipos enumerados em maior
detalhe no Capítulo 13.
• Também, todos os tipos numéricos built-in suportam «C» para moeda, «D» para
decimal, «E» para notação científica, «F» para vírgula fixa, «G» para geral, «N» para
número, «P» para percentagem, «R» para round-trip e «X» para hexadecimal. De facto,
os tipos numéricos também suportam picture format strings nas situações em que a
formatação simples não permitem aquilo que pretendemos. Os picture format strings
contêm caracteres especiais que dizem ao método ToString exactamente quantos dígitos
colocar depois do separador decimal, onde colocar exactamente o separador decimal,
quantos dígitos a colocar depois do separador decimal, etc. Para informação completa
acerca dos strings de formatação consulte Formatting Strings no .NET Framework
SDK.
• Invocar ToString passando null no string de formatação é a mesma coisa que invocar
ToString passando «G» no string de formatação. Por outras palavras, os objectos usam,
por omissão, o formato geral. Quando implementar um tipo, escolha o formato que
pense ser o formato mais usado; este formato será o formato geral. A propósito, o
método ToString que não tem parâmetros assume que se pretende o formato geral.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 48 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (5)
• Agora que discutimos os strings de formatação, vamos analisar a informação sobre a
cultura. Por omissão, os strings são formatados de acordo com a informação de cultura
associada à thread invocante. Este é o comportamento do método ToString sem
parâmetros e do método IFormatable.ToString quando se passa null no parâmetro
formatProvider.
• A informação de cultura aplica-se apenas quando estamos a formatar números
(incluindo moeda, inteiros, vírgula flutuante, e percentagens), datas e horas. Um tipo
que represente um GUID tem um método ToString que apenas devolve um string
representando o valor do GUID. Neste caso, não há necessidade de ter em consideração
a cultura para gerar o string GUID.
• Quando se formata um número, o método ToString testa se foi passado o argumento
formatProvider. Se for passado null, o método ToString determina a cultura associada à
thread invocante acedendo à propriedade System.Threading.Thread.CurrentThread.-
CurrentCulture. Esta propriedade devolve uma instância do tipo
System.Globalzation.CultureInfo.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 49 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (6)
• Usando este objecto, ToString acede às propriedades NumberFormat ou
DateTimeFormat, consoante está a formatar um número ou uma data. Essas
propriedades devolvem uma instância de System.Globalization.NumberFormatInfo ou
System.Globalization.DateTimeInformation, respectivamente. O tipo
NumberFormatInfo define um grupo de propriedades, como
CurrencyDecimalSeparator, CurrencySymbol, NegativeSign, NumberGroupSeparator e
PercentSymbol. Do mesmo modo, o tipo DateTimeFormatInfo define um conjunto de
propriedades, como Calendar, DateSeparator, DayNames, LongDatePattern,
ShortTimePatterns e TimeSeparator. O método ToString acede a estas propriedades
quando constrói o string resultado.
• Quando se invoca o método IFormattable.ToString, podemos passar uma referência para
uma instância de qualquer tipo que implemente a interface IFormatProvider:
public interface IFormatProvider {
Object GetFormat(Type formatType);
}
• A ideia básica que suporta a interface IFormatProvider é a seguinte: quando um tipo
implementa esta interface, está a indicar que sabe providenciar informação de
formatação específica da cultura, pelo que deve ser ignorada a informação de cultura da
thread invocante.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 50 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (7)
• O tipo System.Globalization.CultureInfo é um dos poucos tipos definidas na FCL que
implementa a interface IFormatProvider. Se pretender formatar um string para o
Vietnam deve construir uma instância de CultureInfo e passar essa instância no
parâmetro formatProvider de ToString. O seguinte código obtém uma representação
string de valores numéricos Decimal formatado em moeda usada no Vietnam:
Decimal price = 123.54M;
String s = price.ToString("C", new CultureInfo("vi-VN"));
System.Windows.Forms.MessageBox.Show(s);
• Internamente, o método ToString do tipo Decimal determina que o argumento
formatProvider não é null e chama o método GetFormat como se segue:
NumberFormatInfo nfi = (NumberFormatInfo)
formatProvider.GetFormat(typeof(NumberFormatInfo));
• Esta é a forma como ToString questiona o objecto (CultureInfo) para obter a informação
necessária para a formatação dos números. Os tipos número (como Decimal) solicitam
apenas a informação de formatação de números. Mas, outros tipos – com DateTime –
invocam GetFormat da seguinte forma:
DateTimeFormatInfo dtfi = (DateTimeFormatInfo)
formatProvider.GetFormat(typeof(DateTimeFormatInfo));
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 51 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (8)
• Como o parâmetro de GetFormat pode identificar qualquer tipo, este método é
suficientemente flexível para permitir que seja solicitada qualquer tipo de informação de
formato. Os tipos da versão 1 do .NET Framework apenas chamam GetFormat para
obter informação sobre a formatação de números e de data/hora; no futuro, poderá vir a
ser solicitada outros tipos de informação.
• A propósito, se pretender obter o string para um objecto cuja formatação não depende
de qualquer cultura específica, devemos invocar a propriedade estática
System.Globalization.CultureInfo.InvariantCulture e passar o objecto devolvido no
parâmetro formatProvider de ToString:
Decimal price = 123.54M;
String s = price.ToString("C", CultureInfo.InvariantCulture);
System.Windows.Forms.MessageBox.Show(s);
• Normalmente, não se pretende mostrar ao utilizador strings formatados usando cultura
invariante. Tipicamente, pretende-se armazenar este tipo de strings em ficheiros para
posterior análise.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 52 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (9)
• Na FCL apenas três tipos implementam a interface IFormatProvider. O primeiro é o
tipo CultureInfo, que explicámos anteriormente. Os outros dois tipos são
NumberFormatInfo e DateTimeFormat. Quando é invocado GetFormat sobre uma
instância de NumberFormatInfo, o tipo testa se está a ser solicitado o tipo
NumberFormatInfo; em caso afirmativo, devolve this; caso contrário, devolve null. Do
mesmo modo, ao invocar GetFormat sobre uma instância de DateTimeFormat é
devolvido this se for solicitado o tipo DateTimeFormat; caso contrário, é devolvido null.
Estes dois tipos implementam esta interface apenas por conveniência de programação.
• Quando tentamos obter a representação string de um objecto, é comum especificar um
formato e usar a cultura associada à thread invocante. Por esta razão, é frequente chamar
ToString passando um string para especificar a formatação, e null no parâmetro
formatProvider. Para tornar as chamadas a ToString mais fáceis, muitos tipos oferecem
várias sobrecargas do método ToString. Por exemplo, o tipo Decimal oferece os
seguintes métodos ToString:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 53 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (10)
// This version calls ToString(null, null).
// Meaning: General format, thread's culture information.
String ToString();
// This version is where the actual implementation of ToString goes.
// This version implements IFormattable's ToString method.
// Meaning: Caller-specified format and format culture information.
String ToString(String format, IFormatProvider formatProvider);
// This version simply calls ToString(format, null).
// Meaning: Caller-specified format, thread's culture information.
String ToString(String format);
// This version simply calls ToString(null, formatProvider).
// Meaning: General format, caller-specified culture information.
String ToString(IFormatProvider formatProvider);
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 54 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (11)
Formatting Multiple Objects into a Single String
• Atrás expliquei como um tipo individual pode formatar os seus próprios objectos. As
vezes, contudo, pretendemos construir strings que consistam de muitos objectos
formatados. Por exemplo, o seguinte string tem uma data, o nome de uma pessoa e uma
idade:
String s = String.Format("On {0}, {1} is {2} years old.",
DateTime.Now, "Wallace", 35);
Console.WriteLine(s);
• Se construir e executar este código em 23 de Janeiro de 2002, às 4:37 PM, verá o
seguinte output:
On 1/23/2002 $:37:37 PM, Wallace is 35 years old.
• O método estático String.Format toma um string de formatação que identifica
parâmetros substituíveis, usando números entre chavetas. O string deste exemplo, indica
ao método Format para substituir «{0}» com o primeiro parâmetro após o string de
formatação (a data/hora corrente), substituir «{1}» como o segundo parâmetro
(«Wallace») e substituir «{2}» com o terceiro parâmetro («35»).
• Internamente, o método Format chama o método ToString de cada objecto para obter a
respectiva representação string. Depois, os strings devolvidos são inseridos nas
respectivas posições, sendo, finalmente, devolvido o string completo.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 55 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (12)
Isto funciona bem, mas também significa que todos os objectos são formatados usando o
formato geral e a informação de cultura da thread invocante.
• Poderá ter mais controlo na formatação do objecto se especificar informação de
formatação entre chavetas. Por exemplo, o seguinte código é idêntico ao anterior
excepto que foi adicionada informação de formatação nos parâmetros substituíveis 0 e 2:
String s = String.Format("On {0:D}, {1} is {2:E} years old.",
DateTime.Now, "Wallace", 35);
Console.WriteLine(s);
• Se construir e executar este código em 23 de Janeiro de 2002, às 4:37 PM, verá o
seguinte output:
On Wednesday, January 23, 2003, Wallace is 35 years old.
• Quando o método Format analisa o string de formatação, constata que para o parâmetro
0 deve ser chamado o método IFormattable.ToString passando «D» no primeiro
parâmetro e null no segundo parâmetro. Para o parâmetro 2, deverá ser invocado
IFormattable.ToString passando «E» no string de formatação e null no segundo
parâmetro. Se o tipo não implementar a interface IFormattable, o Format chama o
método ToString sem parâmetros, obtendo-se assim o formato geral.
• A classe String oferece várias sobrecargas do método estático Format. Uma versão toma
um objecto que implemente a interface IFormatProvider de forma a que seja possível
formatar os parâmetros substituíveis usando informação de cultura passada pelo caller.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 56 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (13)
Providing Your Own Custom Formatter
• Até agora deve ter ficado claro que as possibilidades de formatação no .NET Framework
foram concebidas por forma a oferecer um elevado grau de flexibilidade e controlo.
Contudo, ainda não terminei. É possível definir um método que seja chamado pelo
método StringBuilder.AppendFormat quando um objecto estiver a ser formatado num
string. Por outras palavras, em vez de chamar ToString para cada objecto, o
AppendFormat pode chamar uma função que podemos definir, permitindo formatar um
ou todos os objectos de forma específica. Aquilo que vamos descrever apenas funciona
com o método AppendFormat do tipo StringBuilder. O método Format do tipo String
não suporta este mecanismo.
• Vou usar um exemplo para explicar este mecanismo. Consideremos que estamos a
formatar texto HTML que o utilizador deverá ver usando um Internet browser.
Pretende-se que todos os valores Int32 sejam mostrados em bold. Para obter este efeito,
sempre que um valor Int32 for formatado em string, será necessário envolver o string
com as marcas HTML <B> e </B>. O seguinte código demonstra quão simples é obter
este efeito:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 57 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (14)
using System;
using System.Text;
using System.Globalization;
using System.Threading;
class BoldInt32s : IFormatProvider, ICustomFormatter {
public Object GetFormat(Type formatType) {
if (formatType == typeof(ICustomFormatter)) return this;
return Thread.CurrentThread.CurrentCulture.GetFormat(formatType);
}
public String Format(String format, Object arg,
IFormatProvider formatProvider) {
String s;
IFormattable formattable = arg as IFormattable;
if (formattable == null)
s = arg.ToString();
else
s = formattable.ToString(format, formatProvider);
if (arg.GetType() == typeof(Int32))
return "<B>" + s + "</B>";
return s;
}
}
class App {
static void Main() {
StringBuilder sb = new StringBuilder();
sb.AppendFormat(new BoldInt32s(), "{0} {1} {2:M}",
"Jeff", 123, DateTime.Now);
Console.WriteLine(sb);
}
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 58 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (15)
• Quando compila e executa este código, é gerado o seguinte output:
Jeff <B>123</B> January 23
• Em Main, é construído um objecto StringBuilder vazio onde é acrescentado depois o
string formatado. Quando se invoca AppendFormat, o primeiro parâmetro é uma
instância da classe BoldInt32s. Esta classe implementa a interface IFormatProvider.
Além disso, esta classe implementa a interface ICustomFormatter:
public interface ICustomFormatter {
String Format(String format, Object arg, IFormatProvider formatProvider);
}
• O método Format desta interface é chamado sempre que o método AppendFormat de
StringBuilder necessita de obter um string para representar um objecto. É possível usar
o código deste método para intervir na formatação do string. Vamos analisar o
pseudocódigo do método AppendFormat:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 59 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (16)
public StringBuilder AppendFormat(IFormatProvider formatProvider,
String format, params Object[] args) {
// If an IFormatProvider was passed, find out whether it
// offers an ICustomFormatter object.
ICustomFormatter cf = null;
if (formatProvider != null)
cf = formatProvider.GetFormat(typeof(ICustomFormatter));
// Keep appending literal characters (not shown in this pseudocode)
// and replaceable parameters to the StringBuilder's character array.
while (MoreReplaceableArgumentsToAppend) {
String argStr;
// If a custom formatter is available, let it format the argument.
if (cf != null) argStr = cf.Format(argFormat, argObj, formatProvider);
// If there is no custom formatter or if it didn't format the
// argument, try something else.
try {
// Does the argument's type support rich formatting?
IFormattable formattable = arg as IFormattable;
if (formattable != null) {
// Yes: pass the format string and provider to
// the type's IFormattable ToString method.
argStr = formattable.ToString(argFormat, formatProvider);
} else {
// No: get the general format using the
// the thread's culture information.
argStr = (arg != null) ? arg.ToString(): String.Empty;
}
}
// Append argStr's characters to the character array field member.
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 60 de 88
Tradução: Carlos Martins 12. Working with Text
Obtaining a String Representation for an Object (17)
• Quando Main chama AppendFormat, este método chama o método GetFormat do nosso
format provider, passando o tipo ICustomFormatter. O método GetFormat definido pelo
tipo BoldInt32s verifica que está a ser solicitado ICustomFormatter e devolve uma
referência para o próprio objecto; quando é solicitado qualquer outro tipo invocamos o
método GetFormat sobre a instância de CultureInfo associado à thread invocante.
• Quando AppendFormat necessita de formatar um parâmetro substituível, chama o
método ICustomFormatter.Format. Neste exemplo, será chamado o método Format
definido pelo tipo BoldInt32s. Neste método, é testado se o objecto a formatar suporta
formatação enriquecida por via da interface IFormattable. Se o objecto não suporta
aquela interface, é chamado o método ToString sem parâmetros; caso contrário, é
chamado o método ToString enriquecido passando o string de formatação e o format
provider.
• Depois de ter o string formatado, é testado se o tipo do objecto é Int32; em caso
afirmativo envolve-se esse string com as marcas HTML <B> e </B>, e devolve-se o
novo string; caso contrário, é devolvido o string formatado sem qualquer
processamento.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 61 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (1)
• Na secção anterior, expliquei como se obtém a representação string a partir de um
objecto. Nesta secção, iremos discutir o oposto: como se obtém a representação objecto
a partir do string. A obtenção de objectos a partir de strings não é uma operação muito
frequente, mas, por vezes, é conveniente. A Microsoft sentiu necessidade de formalizar
um mecanismo através do qual os strings poderão dar origem a objectos.
• Qualquer tipo que saiba construir instâncias a partir de strings deve definir um método
estático público chamado Parse, que tome como parâmetro um String e devolva uma
instância do tipo. Deste ponto de vista, o método Parse comporta-se como construtor.
Na FCL, o método Parse está definido em todos os tipos numéricos, nos tipos
DateTime e TimeSpan, e em alguns outros tipos (como os tipos de dados SQL).
• Vamos analisar como se transforma um string num tipo numérico. Todos os tipos
numéricos (Byte, SByte, Int16/UInt16, Int64/UInt64, Single, Double e Decimal) definem
pelo menos um método Parse. A seguir mostra-se o método Parse definido pelo tipo
Int32. (Os métodos dos outros tipos numéricos são idênticos.)
public static Int32 Parse(String s, NumberStyles style,
IFormatProvider provider);
• Apenas por olhar para este protótipo deverá ser fácil de adivinhar como trabalha este
método. O parâmetro String, s, identifica a representação string especificando o número
que pretendemos converter numa instância do tipo Int32.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 62 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (2)
O parâmetro System.Globalization.NumberStyles, style, especifica o conjunto de bit
flags que identificam os caracteres que o método Parse deve esperar encontrar no string.
E o parâmetro IFormatProvider, provider, identifica o objecto que o método Parse
poderá usar para obter informação específica de cultura, como discutimos
anteriormente.
• Por exemplo, o seguinte código faz com que o método Parse lance a excepção
System.FormatException porque o string contém um caracter branco no início:
Int32 x = Int32.Parse(" 123", NumberStyles.None, null);
• Para que o método Parse ignore o espaço inicial, é necessário especificar o style do
seguinte modo:
Int32 x = Int32.Parse(" 123", NumberStyles.AllowLeadingWhite, null);
• A Tabela 12-5 mostra os símbolos definidos pelo tipo NumberStyles.
• Além dos símbolos definidos na Tabela 12-5, o tipo enumerado NumberStyles define
também alguns símbolos que representam combinações comuns dos bits individuais. A
Tabela 12-6 mostra essas combinações.
• O seguinte segmento de código faz parse de um número hexadecimal:
Int32 x = Int32.Parse("1A", NumberStyles.HexNumber, null);
Console.WriteLine(x); // Displays "26"
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 63 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (3)
Table 12-5 Bit Symbols Defined by the NumberStyles Type
Symbol Value Description
None 0x00000000 None of the special characters represented by any of the bits in the
remaining rows in this table are allowed in the string.
AllowLeadingWhite 0x00000001 The string can contain leading/trailing white-space characters
AllowTrailingWhite 0x00000002 (identified by the following Unicode code points: 0x0009, 0x000A,
0x000B, 0x000C, 0x000D e 0x0020).
AllowLeadingSign 0x00000004 The string can contain a valid leading/trailing sign character.
AllowTrailingSign 0x00000008 NumberFormatInfo's PositiveSign e NegativeSign properties
determine valid leading-sign characters.
AllowParentheses 0x00000010 The string can contain parentheses.
AllowDecimalPoint 0x00000020 The string can contain a valid decimal-separator character.
NumberFormatInfo's NumberDecimalSeparator and
CurrencyDecimalSeparator properties determine valid-decimal
separator characters.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 64 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (4)
Table 12-5 Bit Symbols Defined by the NumberStyles Type (continued)
Symbol Value Description
AllowThousands 0x00000040 The string can contain a valid grouping separator character.
NumberFormatInfo's NumberGroupSeparator and
CurrencyGroupSeparator properties determine valid grouping-
separator characters. NumberFormatInfo's NumberGroupSizes
e CurrencyGroupSizes properties determine the number of
digits in the group.
AllowCurrencySymbol 0x00000100 The string can contain a valid currency symbols, which
NumberFormatInfo's CurrencySymbol property determines..
AllowHexSpecifier 0x00000200 The string can contain hex digits (0-9, A-F) and the string is
considered to be a hex value
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 65 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (5)
Table 12-6 Symbols for NumericStyles' s Bit Combinations
Symbol Bit Set
Integer AllowLeadingWhite | AllowTrailingWhite |
AllowLeadingSign
Number AllowLeadingWhite | AllowTrailingWhite |
AllowLeadingSign | AllowTrailingSign |
AllowDecimalPoint | AllowThousands
Float AllowLeadingWhite | AllowTrailingWhite |
AllowLeadingSign | AllowDecimalPoint |
AllowExponent
Currency AllowLeadingWhite | AllowTrailingWhite |
AllowLeadingSign | AllowTrailingSign |
AllowParentheses | AllwDecimalPoint |
AllowThousands | AllowCurrencySymbol
HexNumber AllowLeadingWhite | AllowTrailingWhite |
AllowHexSpecifier
Any AllowLeadingWhite | AllowTrailingWhite |
AllowLeadingSign | AllowTrailingSign |
AllowParentheses | AllwDecimalPoint |
AllowThousands | AllowCurrencySymbol |
AllowExponent
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 66 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (6)
• O método Parse aceita três parâmetros. Por conveniência, muitos tipos oferecem
sobrecargas adicionais do método Parse, para que não seja necessário passar tantos
argumentos. O tipo Int32 oferece quatro sobrecargas do método Parse:
// Passes NumberStyles.Integer for style
// and null for provider parameters.
public static Int32 Parse(String s);
// Passes null for the provider parameter.
public static Int32 Parse(String s, NumberStyles styles);
// Passes NumberStyles.Integer for style parameter.
public static Int32 Parse(String s, IFormatProvider provider);
// This is the method I've been talking about in this section.
public static Int32 Parse(String s, NumberStyles styles,
IFormatProvider provider);
• O tipo DateTime também define um método Parse:
public static DateTime Parse(String s, DateTimeStyles styles,
IFormatProvider provider);
• Este método funciona como o método Parse definido para os tipos numéricos excepto
no facto de aceitar o conjunto de bit-flags definido pelo tipo enumerado DateTimeStyles.
A Tabela 12-7 mostra o símbolos definidos pelo tipo DateTimeStyles.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 67 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (7)
Table 12-7 Bit Symbols Defined by the DateTimeStyles Type
Symbol Value Description
None 0x00000100 None of the special characters represented by the bits in the
remaining rows in this table are allowed in the string.
AllowLeadingWhite 0x00000001 The string can contain leading/trailing/inner white-space
AllowTrailingWhite 0x00000002 characters (identified by the following Unicode code points:
AllowInnerWhite 0x00000004 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x020).
NoCurrentDateDefault 0x00000008 When parsing a string that contains only a time (no date) set
the date to January, 1, 0001 instead of the current time.
AdjustToUniversal 0x00000010 When parsing a string that contains a time-zone specifier
("GMT", "Z", "+xxxx", "-xxxx"), adjust the parsed time base to
Greenwich Mean Time.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 68 de 88
Tradução: Carlos Martins 12. Working with Text
Parsing a String to Obtain an Object (8)
• Além desses símbolos, o tipo enumerado DateTimeStyles também define o símbolo
AllowWhiteSpaces que representa um OR de todos os símbolos White
(AllowLeadingWhite | AllowInnerWhite | AllowTrailingWhite).
• Por conveniência, tipo DateTime oferece três sobrecargas do método Parse:
// null for formatProvider and DateTimeStyles.None.
public static DateTime Parse(String s);
// DateTimeStyles.None.
public static DateTime Parse(String s, IFormatProvider provider);
// This is the method I've been talking about in this section.
public static DateTime Parse(String s, DateTimeStyles styles,
IFormatProvider provider);
• A análise de datas e horas é complexa. Muitos projectistas têm achado que o método
Parse do tipo DateTime pode fazer analisar strings que não contém datas nem horas. Por
esta razão, o tipo DateTime também oferece o método ParseExact que aceita um
«picture format string» que indica exactamente como está formatada a informação e
como a mesma deve ser analisada. Para obter mais informação acerca dos «picture
format strings», consulte a documentação da classe DateTime no .NET Framework.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 69 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (1)
• Em Win32 os programadores têm frequentemente que escrever código para converter
caracteres e strings Unicode para caracteres e strings Multi-Byte Character. Eu também
escrevi a minha parte deste tipo de código, que para além de ser maçador a escrever é
também muito propenso a erros. No CLR os caracteres são representados como códigos
Unicode de 16 bits e todos os strings são compostos por caracteres expressos em
Unicode. Isto facilita o processamento de caracteres e de strings em tempo de execução.
• Contudo, por vezes, é necessário guardar string em ficheiros ou enviá-los através da
rede. Se os strings forem maioritariamente compostos por caracteres legíveis por
pessoas que falam inglês, a transmissão de códigos de 16 bits não é eficiente, porque
metade dos bytes envolvidos nas transferências contém zero. Seria mais eficiente
codificar os valores de 16 bits num array comprimido de bytes e, depois, descodificar o
array de bytes para obter os valores de 16 bits.
• A codificação também permite que as aplicações managed interajam com strings
criados por sistemas que não usam Unicode. Por exemplo, se pretender produzir um
ficheiro legível por uma aplicação que executa sobre a versão japonesa do Windows 95,
terá de salvar o texto Unicode usando a codificação Shift-JIS (code page 932). Do
mesmo modo, terá que usar a codificação Shitf-JIS para ler um ficheiro de texto
produzido com o Windows 95 usando a versão japonesa do CLR.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 70 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (2)
• A codificação é feita, tipicamente, quando se pretende enviar um string para um ficheiro
ou para a rede, usando os tipos System.IO.BinaryWriter ou System.IO.StreamWriter. A
descodificação é feita, tipicamente, quando se pretende ler um string de um ficheiro, ou
da rede, usando os tipos System.IO.BinaryReader ou System.IO.StreamWriter. Se não se
explicitar uma forma de codificação, todos esses tipos usam por omissão UTF-8. (UTF é
o acrónimo de Unicode Transformation Format). No entanto, por vezes, poderá ser
necessário codificar ou descodificar strings.
• Formalmente, a FCL oferece alguns tipos para facilitar a codificação de strings. Os tipos
usados com maior frequência são UTF-8 e UTF-16.
UTF-16 codifica cada caracter expresso a 16 bits como 2 bytes. Os caracteres não
são afectados e não resulta qualquer compressão – a performance é excelente. A
codificação UTF-16 é também conhecida como Unicode encoding. Repare também
que o UTF-16 poderá ser usado para converter de little endian para big endian, ou
vice-versa.
UTF-8 codifica alguns caracteres com 1 byte, outros caracteres com 2 bytes, alguns
caracteres com 3 bytes e outros ainda com 4 bytes. Os caracteres com valores
abaixo de 0x80 são comprimidos para 1 byte, o que funciona muito bem com os
caracteres usados nos Estados Unidos.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 71 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (3)
Os caracteres com códigos entre 0x0080 e 0x07ff são convertidos para 2 bytes, o
que funciona bem para os caracteres usados na Europa e no Médio Oriente.
Caracteres de 0x0800 e acima são convertidos para 3 bytes o que funciona bem
com as linguagens da Ásia Oriental. Finalmente, os pares de caracteres surrogate
são expressos com 4 bytes. O UTF-8 é uma codificação extremamente popular, mas
é menos eficiente que UTF-16 quando se codificam muitos caracteres com códigos
de 0x0800 e acima.
• Embora as codificações UTF-8 e UTF-16 sejam de longa as mais comuns, a FCL
também suporta outras codificações, que são usadas com menor frequência:
A codificação UTF-7 é usada tipicamente com os sistemas antigos que trabalham
com caracteres expressos com 7 bits. Deverá evitar esta forma de codificação,
porque o resultado líquido é a expansão dos dados, e não a sua compressão. O
Unicode Consortium descontinuou esta forma de codificação no standard Unicode
3.0.
A codificação ASCII codifica os caracteres de 16 bits em caracteres ASCII; isto é,
cada caracter de 16 bits com um valor abaixo de 0x80 é convertido para um único
byte. Qualquer caracter com uma valor maior do que 0x7f não pode ser convertido,
e perde-se o caracter. Para os strings compostos de caracteres na gama ASCII
(0x00 a 0x7f) esta codificação comprime os dados para metade, sendo muito
rápida. Quando se usam caracteres fora da gama ASCII são perdidos caracteres.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 72 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (4)
• Finalmente, a FCL também permite codificar caracteres de 16 bits para uma code page
arbitrária. Tal como a codificação ASCII, a codificação para code page é perigosa
porque se perdem todos os caracteres cujos valores não possam ser expressos na code
page usada. Deverá usar sempre a codificação UTF-8 ou UTF-16, a menos que use
ficheiros ou aplicações antigas que já usem um dos outros tipos de codificação.
• Quando necessitar de codificar ou descodificar um conjunto de caracteres, deverá obter
uma instância de uma classe derivada de System.Text.Encoding, que é uma classe base
abstracta que define várias propriedades estáticas. Cada uma desta propriedades devolve
uma instância de uma classe derivada de Encoding. (Cada classe de codificação é
basicamente um wrapper sobre as funções Win32 WideCharToMultiByte e
MultibyteToWideChar.)
• A seguir apresentamos um exemplo de codificação e descodificação de caracteres
Unicode usando UTF-8:
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 73 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (5)
using System;
using System.Text;
class App {
static void Main() {
// This is the string I'm going to encode.
String s = "Hi there.";
// Obtain an Encoding-derived object that knows
// how to encode/decode using UTF-8.
Encoding encodingUTF8 = System.Text.Encoding.UTF8;
// Encode a string into an array of bytes.
Byte[] encodedBytes = encodingUTF8.GetBytes(s);
// Show the encoded byte values.
Console.WriteLine("Encoded bytes: " +
BitConverter.ToString(encodedBytes));
// Decode the byte array back to a string.
String decodedString = encodingUTF8.GetString(encodedBytes);
// Show the decoded string.
Console.WriteLine("Decoded string: " + decodedString);
}
}
• Este código produz o seguinte output:
Encoded bytes: 48-69-20-74-68-65-72-65-2E
Decode string: Hi there.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 74 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (6)
• Além da propriedade estática UTF8, a classe Encoding oferece as seguintes
propriedades estáticas: Unicode, BigEndianUnicode, UTF7, ASCII e Default. A
propriedade Default devolve um objecto que sabe como codificar/descodificar usando a
code page do utilizador conforme a especificação feita através da applet «Language
Options» do Control Panel do Windows. (Consulte a função GetACP do Win32 para
obter mais informação.)
• Além destas propriedades, o tipo Encoding também define o método estático
GetEncoding. Este método permite especificar uma code page – usando um inteiro ou
um string – e devolve um objecto codificador que usa essa code page. Pode invocar
GetEncoding passando, por exemplo, «Shift-JIS» ou 932.
• Quando se solicita pela primeira vez um objecto codificador à classe Encoding, a
propriedade, ou o método GetEncoding, constrói um objecto e devolve esse objecto. Se
um objecto já construído for solicitado no futuro, a classe Encoding devolve o objecto
previamente construído. Este comportamento reduz o número de objectos no sistema e
pressiona menos o managed heap.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 75 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (7)
• Em alternativa a invocar as propriedades estáticas de Encoding ou a invocar o método
GetEncoding, é possível construir instâncias das seguintes classes:
System.Text.UnicodeEncoding, System.Text.UTF8Encoding,
System.Text.UTF7Encoding, ou System.Text.ASCIIEncoding. Este procedimento cria
sempre novos objectos no managed heap, o que prejudica o desempenho.
• Três destas classes, UnicodeEncoding, UTF8Encoding e UTF7Encoding definem vários
construtores que permitem maior controlo sobre a codificação e as byte order marks
(BOMs). Poderá pretender construir explicitamente instâncias destes tipos de
codificadores quando trabalha com os tipos BinaryWriter ou StreamWriter. A classe
ASCIIEncoding tem um único construtor, e por isso não permite nenhum controlo
adicional sobre a codificação. Se necessitar de um objecto do tipo ASCIIEncoding
obtenha-o sempre usando a propriedade Encoding.ASCII; nunca construa
explicitamente instâncias da classe ASCIIEncoding.
• Uma vez obtido o objecto derivado de Encoding, pode converter um array de caracteres
num array de bytes, invocando o método GetBytes. (Existem várias sobrecargas deste
método.) Para converter um array de bytes para um array de caracteres, use o método
GetChars ou o método GetString. (Existem várias sobrecargas de cada um destes
métodos.) O código anterior demonstra a chamada aos métodos GetBytes e GetString.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 76 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (8)
• Os tipos derivados de Encoding definem também o método GetByteCount, que devolve
o número de bytes necessários para codificar um conjunto de caracter, sem proceder
efectivamente à codificação. Poderá usar este método para determinar o número de
elementos do array de bytes a reservar. Existe também o método GetCharCount que
devolve o número de caracteres resultado de uma descodificação, sem proceder à
descodificação. Estes métodos têm utilidade quando pretender poupar memória e
reutilizar os arrays.
• Os métodos GetByteCount/GetCharCount não são muito rápidos, pois têm que analisar
o array de caracteres/bytes para devolver um resultado preciso. Se preferir velocidade a
um resultado exacto, poderá, em alternativa, usar os métodos GetMaxByteCount ou
GetMaxCharCount. Ambos os métodos são parametrizados pelo número de caracteres
ou pelo número de bytes, e devolvem o valor correspondente à situação mais
desfavorável.
• Cada classe derivada de Encoding define um conjunto de propriedades públicas read-
only que permitem obter informação detalhada acerca da codificação. A Tabela 12-8
descreve resumidamente essas propriedades.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 77 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (9)
Table 12-8 Properties of Encoding-Derived Classes
Property Type Description
EncodingName String Returns the encoding's human-readable name..
CodePage Int32 Returns the encoding's code page.
WindowsCodePage String Returns the encoding's closest Windows code page.
WebName String Returns the IANA-registered name. (IANA stands for Internet
Assigned Numbers Authority) For more information, go to
http://www.iana.org.
HeaderName String Returns mail agent header tag.
BodyName String Returns the mail agent body tag.
IsBrowserDisplay Boolean Returns true if browser clients can use encoding for display
purposes.
IsBrowserSave Boolean Returns true if browser clients can use encoding for saving
purposes.
IsMailNewsDisplay Boolean Returns true if mail and news clients can use encoding for
display purposes.
IsMailNewsSave Boolean Returns true if mail and news clients can use encoding for
saving purposes.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 78 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (10)
• Para ilustra a utilização destas propriedades e o seu significado, considere o seguinte
programa que mostra o valor destas propriedades para diferentes codificadores:
using System;
using System.Text;
class App {
static void Main() {
Show(Encoding.Unicode);
Show(Encoding.BigEndianUnicode);
Show(Encoding.UTF8);
Show(Encoding.UTF7);
Show(Encoding.ASCII);
Show(Encoding.GetEncoding(0)); // Same as default
Console.WriteLine();
Console.WriteLine("Below are some specific code pages:");
Show(Encoding.GetEncoding(437));
Show(Encoding.GetEncoding(28595));
Show(Encoding.GetEncoding(54936));
Show(Encoding.GetEncoding(874));
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 79 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (11)
static void Show(Encoding e) {
Console.WriteLine(
"{1} {0}" +
"\tCodePage={2}, WindowsCodePage={3}{0}" +
"\tWebName={4}, HeaderName={5}, BodyName={6}{0}" +
"\tIsBrowserDisplay={7}, IsBrowserSave={8}{0}" +
"\tIsMailNewsDisplay={9}, IsMailNewsSave={10}{0}",
Environment.NewLine,
e.EncodingName,
e.CodePage, e.WindowsCodePage,
e.WebName, e.HeaderName, e.BodyName,
e.IsBrowserDisplay, e.IsBrowserSave,
e.IsMailNewsDisplay, e.IsMailNewsSave);
}
}
• Executando este programa obtemos o seguinte output:
Unicode
CodePage=1200, WindowsCodePage=1200
WebName=utf-16, HeaderName=utf-16, BodyName=utf-16
IsBrowserDisplay=False, IsBrowserSave=True
IsMailNewsDisplay=False, IsMailNewsSave=False
Unicode (Big-Endian)
CodePage=1201, WindowsCodePage=1200
WebName=unicodeFFFE, HeaderName=unicodeFFFE, BodyName=unicodeFFFE
IsBrowserDisplay=False, IsBrowserSave=False
IsMailNewsDisplay=False, IsMailNewsSave=False
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 80 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (12)
Unicode (UTF-8)
CodePage=65001, WindowsCodePage=1200
WebName=utf-8, HeaderName=utf-8, BodyName=utf-8
IsBrowserDisplay=True, IsBrowserSave=True
IsMailNewsDisplay=True, IsMailNewsSave=True
Unicode (UTF-7)
CodePage=65000, WindowsCodePage=1200
WebName=utf-7, HeaderName=utf-7, BodyName=utf-7
IsBrowserDisplay=False, IsBrowserSave=False
IsMailNewsDisplay=True, IsMailNewsSave=True
US-ASCII
CodePage=20127, WindowsCodePage=1252
WebName=us-ascii, HeaderName=us-ascii, BodyName=us-ascii
IsBrowserDisplay=False, IsBrowserSave=False
IsMailNewsDisplay=True, IsMailNewsSave=True
Western European (Windows)
CodePage=1252, WindowsCodePage=1252
WebName=Windows-1252, HeaderName=Windows-1252, BodyName=iso-8859-1
IsBrowserDisplay=True, IsBrowserSave=True
IsMailNewsDisplay=True, IsMailNewsSave=True
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 81 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (13)
Below are some specific code pages:
OEM United States
CodePage=437, WindowsCodePage=1252
WebName=IBM437, HeaderName=IBM437, BodyName=IBM437
IsBrowserDisplay=False, IsBrowserSave=False
IsMailNewsDisplay=False, IsMailNewsSave=False
Cyrillic (ISO)
CodePage=28595, WindowsCodePage=1251
WebName=iso-8859-5, HeaderName=iso-8859-5, BodyName=iso-8859-5
IsBrowserDisplay=True, IsBrowserSave=True
IsMailNewsDisplay=True, IsMailNewsSave=True
Chinese Simplified (GB18030)
CodePage=54936, WindowsCodePage=936
WebName=GB18030, HeaderName=GB18030, BodyName=GB18030
IsBrowserDisplay=True, IsBrowserSave=True
IsMailNewsDisplay=True, IsMailNewsSave=True
Thai (Windows)
CodePage=874, WindowsCodePage=874
WebName=windows-874, HeaderName=windows-874, BodyName=windows-874
IsBrowserDisplay=True, IsBrowserSave=True
IsMailNewsDisplay=True, IsMailNewsSave=True
• A Tabela 12-9 completa a discussão dos métodos oferecidos por todas as classes
derivadas de Encoding.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 82 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (14)
Table 12-9 Methods of the Encoding-derived Classes
Method Description
GetPreamble Returns an array of bytes indicating what should be written to a stream before
writing any encoded bytes. Frequently, these bytes are referred as the byte order
mark (BOM) byte. When you start reading from a stream, the BOM bytes
automatically help detect what encoding was used when the stream was written so
that the correct decoder can be used. For most Encoding-derived classes, this
method returns an array of 0 bytes – that is, no preamble bytes. A UTF8Encoding
object cam be explicitly constructed so that this method returns a 3-byte array of
0xFE, 0xBB, 0xBF. A UnicodeEncoding object cam be explicitly constructed so
that this method returns a 2-byte array of 0xFE, 0xFF for big endian encoding or
a 2-byte array of 0xFF, 0xFE for little endian encoding.
Convert Converts an array of bytes specified in a source encoding to an array of bytes
specified by a destination encoding. Internally, this static method calls the source
encoding object's GetChars method and passes the result to the destination
encoding object's GetBytes method. The resulting byte array is returned to the
caller.
Equals Returns true if two Encoding-derived objects represent the same code page and
preamble string.
GetHashCode Returns the encoding object's code page.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 83 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (15)
Encoding/Decoding Streams of Characters and Bytes
• Imagine que está a ler um string codificado em UTF-16 usando uma instância do tipo
System.Net.Sockets.NetworkStream, os bytes do string irão surgir em vários grupos. Por
outras palavras, o primeiro grupo poderá ter 5 bytes, seguindo-se um outro grupo com 7
bytes. Em UTF-16, cada caracter é codificado com de 2 bytes. Assim, a invocação de
Encoding.GetString passando o grupo de 5 bytes devolve um string com dois caracteres.
A segunda invocação com o grupo de 7 bytes , devolve um string com três caracteres,
onde todos os code points terão valores errados!
• Este problema da corrupção de dados ocorre porque nenhuma das classes derivadas de
Encoding mantém estado entre chamadas aos seus métodos. Se estivermos a codificar
ou a descodificar bytes em grupos, e necessário algum processamento adicional para
manter estado entre chamadas aqueles métodos, evitando perda de informação.
• Para descodificar grupos de bytes, deverá obter uma referência para um objecto de um
tipo derivado de Encoding (como descrevemos na secção anterior), e chamar o método
GetDecoder. Este método devolve uma referência para uma instância de um tipo
derivado de System.Text.Decoder. Tal como a classe Encoding, a classe Decoder é uma
classe base abstracta. Se procurar na documentação do .NET Framework SDK, não
encontrará nenhuma classe que seja uma implementação concreta da classe Decoder.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 84 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (16)
No entanto, a FCL define um grupo de classes derivadas de Decoder, como é o caso de
UTF8Decoder. Estas classes são todas internas à FCL, mas o método GetDecoder
permite obter instâncias destas classes.
• Todas as classes derivadas de Decoder oferecem dois métodos: GetChars e
GetCharCount. Estes métodos são usados para descodificação, e funcionam de forma
idêntica aos métodos GetChars e GetCharCount de Encoding. Quando chamamos um
destes métodos, os mesmos descodificam o array de bytes tanto quanto possível. Se o
array não contiver bytes suficientes para completar um caracter, os bytes não
descodificados são memorizados pelo objecto descodificador. Da próxima vez que for
chamado um daqueles métodos, o objecto descodificador usa os bytes previamente
memorizados antes de processar o array passado ao método – isto garante que os grupos
de dados são descodificados adequadamente..
• Os tipos derivados de Encoding podem ser usados para codificação e descodificação
sem estado. Por seu lado, os tipos derivados de Decoder apenas podem ser usados na
descodificação. Se pretender codificar strings em diferentes grupos de bytes, use
GetEncoder, em vez de GetDecoder. O método GetEncoder devolve uma instância de
um tipo derivado de System.Text.Encoder, que é uma classe base abstracta.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 85 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (17)
De novo, se consultar a documentação do .NET Framework SDK não encontra nenhuma
classe que seja uma implementação concreta do tipo Encoder. No entanto, a FCL define
algumas classes derivadas de Encoder, como é o caso de UTF8Decoder. Como acontece
com as classes derivadas de Decoder, estas classes são todas internas à FCL, podendo as
respectivas instâncias ser obtidas com o método GetDecoder.
• Todas as classes derivadas de Encoder definem dois métodos: GetBytes e GetByteCount.
Tal com acontece com as instâncias dos tipos derivados de Decoder, as instâncias dos
tipos derivados de Encoder mantêm estado para ter em consideração os caracteres não
considerados na codificação do grupo de caracteres anterior.
Base-64 String Encoding and Decoding
• Actualmente as codificações UTF-16 e UTF-8 estão a tornar-se consideravelmente
populares. Está também a ganhar popularidade a capacidade de codificar uma sequência
de bytes num string base-64. A FCL oferece métodos para codificação e descodificação
em base-64, e seria de esperar que essa funcionalidade fosse exposta através de um tipo
derivado de Encoding. Contudo, por alguma razão, a codificação e descodificação em
base-64 é exposta através de métodos estáticos do tipo System.Convert.
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 86 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (18)
• Para codificar um string base 64 para um array de bytes, usamos os métodos estáticos
de Convert.FromBase64String ou Convert.FromBase64CharArray. Similarmente, para
descodificar um array de bytes para um string base-64, usamos os métodos estáticos
Convert.ToBase64String ou Convert.ToBase64CharArray. O seguinte código demonstra
a utilização destes métodos:
using System;
class App {
static void Main() {
// Get a set of 10 randomly generated bytes.
Byte[] bytes = new Byte[10];
new Random().NextBytes(bytes);
// Display the bytes.
Console.WriteLine(BitConverter.ToString(bytes));
// Decode the bytes into a base-64 string, and show the string.
String s = Convert.ToBase64String(bytes);
Console.WriteLine(s);
// Encode the base-64 string back to bytes, and show the bytes.
bytes = Convert.FromBase64String(s);
Console.WriteLine(BitConverter.ToString(bytes));
}
}
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 87 de 88
Tradução: Carlos Martins 12. Working with Text
Encodings: Converting Between Characters and Bytes (19)
• Compilando este código e promovendo a sua execução obtemos o seguinte output.
34-24-99-7A-66-BF-D1-5F-41-1C
NCSZema/0V9BHA==
34-24-99-7A-66-BF-D1-5F-41-1C
Autor: Jeffrey Richter Applied Microsoft .NET Framework Programming 88 de 88
Tradução: Carlos Martins 12. Working with Text
Get documents about "