| 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.