Applied Microsoft .NET Framework Programming by JMBWm22

VIEWS: 26 PAGES: 88

									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

								
To top