Por Fabio Camara
fabio.camara@csharpbr.com.br
MCP, MCSA, MCAD Charter, MCDBA e MCSD.NET - Trabalha na Architettura Soluções em Tecnologia como Diretor de Operações. Escreveu os livros "Projetos com Windows DNA e .NET" e "Orientação a Objeto com .NET" dentre outros, editados pela Visual Books Editora. Pode ser contatado pela home page C# Br ( http://www.csharpbr.com.br ).

Retirar acentos de strings


Umas das grandes funcionalidades úteis da Internet, desde o seu nascimento, é a possibilidade de acesso fácil a informações. Seria como ter uma gigantesca biblioteca particular, muito completa, dentro de seu micro computador.

A evolução da disponibilidade destas informações pela Internet, é possivelmente o fato de não precisarmos procurar determinado assunto específico - simplesmente perguntar em um "local" apropriado e aguardar pela resposta. A primeira vista isso pode parecer uma excelente oportunidade de negócios, entretanto gratuitamente já existem milhões de grupos de pessoas dispostas a fazer isso. São os chamados grupos de discussões ou listas de discussões.

No Brasil isso funciona com magnífica maestria, até mesmo para o C# que é uma linguagem nova. Depois do nascimento do SharpShooters, diversas outras comunidades incentivadas pelo Ineta (www.ineta.org) estão rapidamente se espalhando pelo nosso território e proporcionando a facilidade de acesso as informações. Eu, muito particularmente, aprendo muito participando destes grupos e atualmente sou membro de 3 deles.

O assunto que me inspirou esta matéria foi uma pergunta postada no grupo CSharpBR (www.csharpbr.com.br) e respondida pelo nosso amigo Claudinei Rodrigues do The Club (nei@activenet.com.br).

Com a proposta de resolver o problema descrito no título, crie um programa Windows Form e coloque os mesmos objetos sugeridos na imagem a seguir.

Nosso amigo Nei escreveu o seguinte código:

              string tira_acentos(string texto)

              {

                     string ComAcentos = "ÄÅÁÂÀÃäáâàãÉÊËÈéêëèÍÎÏÌíîïìÖÓÔÒÕöóôòõÜÚÛüúûùÇç";

                     string SemAcentos = "AAAAAAaaaaaEEEEeeeeIIIIiiiiOOOOOoooooUUUuuuuCc";

                     for (int i = 0; i < ComAcentos.Length; i++)

                     {

                           texto = texto.Replace(ComAcentos[i].ToString(),SemAcentos[i].ToString());

                     }

                     return texto;

              }

Se criarmos uma chamada a esta função no evento Click do botão "Processar" com o código abaixo, teremos eficientemente resolvido nossa questão.

              private void button1_Click(object sender, System.EventArgs e)

              {

                     txbSAcento.Text = tira_acentos(txbCAcento.Text);

              }

Nota do Autor: Sempre que trabalho diretamente com objetos, eu procuro alterar seu nome (do objeto) com a finalidade de tornar intuitivo qual devo utilizar para determinada finalidade. Imagine o form com 20 textbox sem seus nomes alterados, seria terrível exigir da sua memória saber qual o textbox que deve receber um tratamento de máscara específico para CEP.

Poderíamos simplesmente terminar esta matéria aqui, afinal já resolvemos o problema proposto. Entretanto, uma lista de discussão lhe proporciona diversas surpresas,  e foi exatamente uma destas surpresas que me deixou intrigado.

Aconteceu que outro participante da lista enviou uma nova proposta para resolver este mesmo problema e justificou que sua proposta era melhor porque construía menos objetos, eliminando assim trabalho para o Garbage Collector.

Para pesquisar a veracidade desta afirmação, vamos criar uma nova função com o nome tira_acentos_gc em nosso projeto conforme o código abaixo:

readonly char[] CAcentos = "ÄÅÁÂÀÃäáâàãÉÊËÈéêëèÍÎÏÌíîïìÖÓÔÒÕöóôòõÜÚÛüúûùÇç".ToCharArray();

readonly char[] SAcentos = "AAAAAAaaaaaEEEEeeeeIIIIiiiiOOOOOoooooUUUuuuuCc".ToCharArray();

 

string tira_acentos_gc(string texto)

{

       for (int i = 0; i < CAcentos.Length; i++)

       {

              texto = texto.Replace(CAcentos[i], SAcentos[i]);

       }

       return texto;

}

É importante entender que o modificador que usamos, readonly, não é igual ao modificador const - em outras palavras não é uma constante.  A regra de utilização do modificador readonly é a seguinte: Campos com este modificador só podem receber valores em sua própria declaração ou no construtor da classe. Já uma constante somente pode receber valor em sua própria declaração. Esta diferença permite ao modificador readonly uma maior flexibilidade, pois não é tão "livre" como uma variável e nem tão "presa" como uma constante.

Compreendido o código novo, chegou o momento das comparações. Acredito que a melhor forma de analisar comparativamente os dois códigos é utilizando a ferramenta ILDASM, que vem junto com o Visual Studio.NET.  Em uma instalação típica, você pode encontrar o ILDASM.exe no seguinte path: "C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin".

Se você já executou pelo menos uma vez nosso aplicativo teste, localize seu respectivo assembly (nome do seu projeto + ".exe") pelo ILDASM. Você deve obter uma imagem semelhante a esta:

 

O próximo passo é clicar com o mouse em cima do "quadrado rosa" tira_acentos  (duas vezes) e observar seu código MSIL na nova janela que irá se abrir.

.method private hidebysig instance string

        tira_acentos(string texto) cil managed

{

  // Code size       75 (0x4b)

  .maxstack  5

  .locals init ([0] string ComAcentos,

           [1] string SemAcentos,

           [2] int32 i,

           [3] string CS$00000003$00000000,

           [4] char CS$00000002$00000001,

           [5] char CS$00000002$00000002)

  IL_0000:  ldstr      bytearray (C4 00 C5 00 C1 00 C2 00 C0 00 C3 00 E4 00 E1 00

                                  E2 00 E0 00 E3 00 C9 00 CA 00 CB 00 C8 00 E9 00

                                  EA 00 EB 00 E8 00 CD 00 CE 00 CF 00 CC 00 ED 00

                                  EE 00 EF 00 EC 00 D6 00 D3 00 D4 00 D2 00 D5 00

                                  F6 00 F3 00 F4 00 F2 00 F5 00 DC 00 DA 00 DB 00

                                  FC 00 FA 00 FB 00 F9 00 C7 00 E7 00 )

  IL_0005:  stloc.0

  IL_0006:  ldstr      "AAAAAAaaaaaEEEEeeeeIIIIiiiiOOOOOoooooUUUuuuuCc"

  IL_000b:  stloc.1

  IL_000c:  ldc.i4.0

  IL_000d:  stloc.2

  IL_000e:  br.s       IL_003c

  IL_0010:  ldarg.1

  IL_0011:  ldloc.0

  IL_0012:  ldloc.2

  IL_0013:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)

  IL_0018:  stloc.s    CS$00000002$00000001

  IL_001a:  ldloca.s   CS$00000002$00000001

  IL_001c:  call       instance string [mscorlib]System.Char::ToString()

  IL_0021:  ldloc.1

  IL_0022:  ldloc.2

  IL_0023:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)

  IL_0028:  stloc.s    CS$00000002$00000002

  IL_002a:  ldloca.s   CS$00000002$00000002

  IL_002c:  call       instance string [mscorlib]System.Char::ToString()

  IL_0031:  callvirt   instance string [mscorlib]System.String::Replace(string,

                                                                        string)

  IL_0036:  starg.s    texto

  IL_0038:  ldloc.2

  IL_0039:  ldc.i4.1

  IL_003a:  add

  IL_003b:  stloc.2

  IL_003c:  ldloc.2

  IL_003d:  ldloc.0

  IL_003e:  callvirt   instance int32 [mscorlib]System.String::get_Length()

  IL_0043:  blt.s      IL_0010

  IL_0045:  ldarg.1

  IL_0046:  stloc.3

  IL_0047:  br.s       IL_0049

  IL_0049:  ldloc.3

  IL_004a:  ret

} // end of method Form1::tira_acentos

 

Repita a mesma operação para tira_acentos_gc.

.method private hidebysig instance string

        tira_acentos_gc(string texto) cil managed

{

  // Code size       49 (0x31)

  .maxstack  4

  .locals init ([0] int32 i,

           [1] string CS$00000003$00000000)

  IL_0000:  ldc.i4.0

  IL_0001:  stloc.0

  IL_0002:  br.s       IL_0020

  IL_0004:  ldarg.1

  IL_0005:  ldarg.0

  IL_0006:  ldfld      char[] TiraAcentos.Form1::CAcentos

  IL_000b:  ldloc.0

  IL_000c:  ldelem.u2

  IL_000d:  ldarg.0

  IL_000e:  ldfld      char[] TiraAcentos.Form1::SAcentos

  IL_0013:  ldloc.0

  IL_0014:  ldelem.u2

  IL_0015:  callvirt   instance string [mscorlib]System.String::Replace(char,

                                                                        char)

  IL_001a:  starg.s    texto

  IL_001c:  ldloc.0

  IL_001d:  ldc.i4.1

  IL_001e:  add

  IL_001f:  stloc.0

  IL_0020:  ldloc.0

  IL_0021:  ldarg.0

  IL_0022:  ldfld      char[] TiraAcentos.Form1::CAcentos

  IL_0027:  ldlen

  IL_0028:  conv.i4

  IL_0029:  blt.s      IL_0004

  IL_002b:  ldarg.1

  IL_002c:  stloc.1

  IL_002d:  br.s       IL_002f

  IL_002f:  ldloc.1

  IL_0030:  ret

} // end of method Form1::tira_acentos_gc

Em uma breve observação, a diferença em número de linhas de código MSIL já nos sinaliza que realmente o segundo código esta mais otimizado pelo ponto de vista do compilador Just In Time (JIT).

Como acredito que não terei nenhum leitor que deseje depurar linha a linha o código MSIL gerado (algum estudande de assembler disponível?), vou expor algumas conclusões básicas sobre o que pude analisar:

1-                         Todo método ou função sem um modificador explícito é considerado private, observe no início do method;

2-                         Realmente existem menos objetos para serem destruídos pelo Garbage Collector no segundo código, observe a declaração .local inits( );

3-                         Há um desperdício de memória no primeiro código, pois criamos implicitamente tipos char (quando utilizamos os colchetes em um tipo string) e depois convertemos para string novamente (com o método ToString( ));

4-                         Devido a estes "casts" comentados anteriormente, o compilador necessita  fornecer mais instruções ao JIT. Observe que o primeiro código possui "Code size       75 (0x4b)" e o Segundo código "Code size       49 (0x31)".

5-                         Por último, o ILDASM é uma excelente ferramenta para análise e otimização do seu código fonte.

 

A partir deste ponto, respeito a conclusão de vocês sobre qual dos exemplos utilizar. 

 

Dicas para quem está começando:
Veja os próximos eventos
que você não pode perder :

22/11/2008 SQL Launch -
Linhares - ES
Por : devASPNet


22/11/2008 SQL Launch- SQL Server 2008
Rio Paranaiba - Viçosa - MG
Por : devASPNet


22/11/2008 SQL Launch - SQL Server 2008
Volta Redonda - RJ
Por : devASPNet


22/11/2008 SQL Launch- SQL Server 2008
Franca - SP
Por : devASPNet


22/11/2008 SQL Launch - SQL Server 2008
Canoinhas - SC
Por : devASPNet


22/11/2008 SQL Launch - SQL Server 2008
Tefé - AM
Por : devASPNet


25/11/2008 SQL Launch - SQL Server 2008
Rio de Janeiro - RJ
Por : devASPNet


27/11/2008 SQL Lauch- SQL Server 2008
São Paulo - SP
Por : devASPNet


28/11/2008 SQL Launch - SQL Server 2008
São Paulo - SP
Por : devASPNet


29/11/2008 SQL Launch- SQL Server 2008
Pedro Leopoldo - MG
Por : devASPNet


29/11/2008 SQL Launch- SQL Server 2008
Petropolis - RJ
Por : devASPNet


29/11/2008 SQL Launch
Floriano - PI
Por : devASPNet


29/11/2008 SQL Launch - SQL Server 2008
Rio de Janeiro - RJ
Por : devASPNet


29/11/2008 SQL Launch - SQL Server 2008
Natal - RN
Por : devASPNet


29/11/2008 SQL Launch- SQL Server 2008
Santa Maria - RS
Por : devASPNet


29/11/2008 SQL Launch
Ituiutaba - MG
Por : devASPNet


5/12/2008 Cloud Computing e o Windows Azure
São Paulo - SP
Por : devASPNet


6/12/2008 SQL Server 2008 Community Launch
São Paulo - SP
Por : devASPNet

Leituras imperdíveis para quem está começando:

º Otimizando a performance no ASP.NET::..
º Criando objetos de paginação personalizados na grid::..
º Uma cesta de compras em ASP.NET::..
º Utilizando o Refresh de parâmetros no .NET::..
º ASP.NET FORMS Authentication::..
º Utilizando propriedades dinâmicas no .NET::..
º Corrigindo problemas de deleção em grid com paginação::..
º Cuidado com os componentes de validação::..
º Otimizando o InitializeComponent::..
º Movendo fonte de aplicações entre máquinas::..
º Agilizando a performance da IDE do VS.NET::..
º Utilizando Short Circuit no VB.NET::..


























  Parceiros:
20% de desconto para os membros do grupo na aquisição de livros e inscrição para eventos

Receba dicas de programação e programação .NET:
E-mail:
Incluir Excluir