Benchmark de Expressões Regulares

regexRecentemente eu li o fantástico Expressões Regulares – Uma Abordagem Divertida de Aurélio Marinho Jargas (@oreio e http://www.aurelio.net), buscando mais informações a respeito do uso de Regular Expressions em parsers HTML (como os que uso para criar mecanismos de busca). Não posso dar mais detalhes sobre o projeto, mas o que originou este post foi a dúvida que o livro me deixou: até que ponto vale a pena utilizar expressões regulares em um sistema para realizar parsing de HTML ao invés dos métodos existentes na classe string, como Replace, IndexOf, Split etc?

Este post relata algumas experiências que tive durante os testes, informações estas que não encontrei na net e tive de fazer por mim mesmo!

O que são Expressões Regulares?

Para quem não sabe do que se trata, segue um trecho retirado do próprio livro, que a meu ver, é uma excelente definição:

Bem resumido, uma expressão regular é um método formal de se especificar um padrão de texto.

Mais detalhadamente, é uma composição de símbolos, caracteres com funções especiais, que, agrupados entre e si e com caracteres literais, formam uma sequência, uma expressão. Essa expressão é interpretada como uma regra, que indicará sucesso se uma entrada de dados qualquer casar (match) com essa regra, ou seja, obedecer exatamente a todas suas condições.

O que é um benchmark?

Este é o primeiro de muitos benchmarks que pretendo fazer. Sempre gostei do assunto de desempenho computacional e gosto de criar códigos que realmente utilizem todo o poder de uma linguagem e de uma máquina. Benchmarks nada mais são do que testes de performance entre soluções concorrentes.

Benchmarks de peças de hardware existem aos montes pela Internet, incluindo o fantástico site Tom’s Hardware que é fonte recomendadíssima de consulta para quem quer comprar uma nova placa de vídeo ou processador. Porém benchmarks de código são mais difíceis de achar, principalmente em língua portuguesa. Espero agregar valor aos visitantes do blog com conteúdo exclusivo do que eu chamo de C# Tuning!

Let’s do it!

Todos os testes foram feitos sobre um arquivo HTML contendo 206KB em 2073 linhas. A máquina onde rodaram os testes é meu PC comum, um Athlon X2 2.2GHz com 2GB RAM. O projeto era um Console Application utilizando o .NET Framework 4.0.

Vamos ao que interessa, os testes realizados foram:

  1. Busca por palavras
  2. Troca de palavras
  3. Quebra de palavras

1º Cenário: Busca por Palavras

No primeiro embate da noite tivemos uma busca por uma determinada palavra dentro do HTML. Esta palavra estava na (teoricamente) pior posição do arquivo: na última linha, última coluna. Foi feito um código utilizando os métodos tradicionais de busca por string, Substring e IndexOf. O IndexOf me dava a posição daquela palavra para poder retirá-la de dentro do HTML usando o substring. Ou seja, você tem a palavra e quer encontrá-la no texto (igual aos que os editores de texto fazem). Já o Regex usa apenas seu comando Match passando por parâmetro a entrada (o HTML) e o padrão (a expressão regular). Vitória esmagadora da Regex: 24.316 Ticks no método tradicional contra 3.278 Ticks da Regex. Quase 10x mais rápido.

Segue o código:

2º Cenário: Troca de Palavras

No 2º round temos um desafio um pouco maior: o usuário quer encontrar determinada palavra no texto e trocá-la por outra palavra. Mais uma vez, a palavra que se busca estará no final do HTML, para forçar os dois métodos a usarem o máximo de seu potencial, enfrentando o pior caso possível. No método tradicional temos o uso do método String.Replace, e no córner azul temos o Regex.Replace. Tim-tim! Empate técnico: o Replace tradicional obteve um tempo de 7753 Ticks, contra 7859 Ticks do seu rival mais elaborado.

Segue o código:

3º Cenário: Quebra de Palavras

No último embate da noite, temos o duelo de Splits. Quem sairá melhor: String.Split ou Regex.Split? Dessa vez a palavra que será usada como padrão para a “quebra” estará uma linha antes da última linha do HTML, para exigir ao máximo dos métodos. Ready…Go! Nocaute da Regex: o comando Split da Regex conseguiu a façanha de quebrar o texto em três pedaços (sim, a Regex inclui a palavra buscada dentro do array de retorno) em apenas 5.354 Ticks, contra 13.351 Ticks do Split tradicional, que traz apenas 2 posições no array.

Segue o código:

Ok, você pode achar que o método Split da Regex não corresponde de forma idêntica ao Split da String, porém, note que o Split da Regex te dá outros incríveis recursos como delimitar o número máximo de posições no array, iniciar a quebra do texto a partir de uma posição específica, iniciar da direita para a esquerda, entre outras. Sem contar que a versatilidade de descrever o seu separador usando um expressão regular ao invés d euma palavra literal não tem preço.

Conclusões

Regex na veia!

Ok, se escreve mais linhas e os ganhos são imperceptíveis em aplicações tradicionais, mas não se esqueça de usar Regex caso suas aplicações processem texto pesado (como parsers), mesmo que você não esteja buscado uma palavra que atenda a uma regra elaborada, mas uma simples palavra literal como utilizada nos exemplos.

Você fez um código mais otimizado para tentar provar que os métodos tradicionais são mais rápidos que a Regex? Mande pra mim!

Você tem outra idéia de embate entre String vs Regex que gostaria de ver publicado aqui? Mande pra mim também!

Usando cookies com WebClient

WebClient é uma classe muito útil quando você precisa fazer download ou upload de dados da ou para a web. Entretanto, quando você precisa fazer uma sequência de chamadas você vai perceber que o WebClient não preserva cookies atribuídos pelo servidor entre as requisições. Fezlimente, WebClient lhe dá a oportunidade para você mesmo gerenciar seus cookies.

Uma solução muito simples e que aprendi no blog It Could Be Done! é sobrescrever o método GetWebRequest da classe WebClient e atribuir a propriedade CookieContainer. Abaixo segue a minha implementação:

Eu testei na prática e funciona perfeitamente, por isso não resisti e tive de dar um re-post em pt-BR. Enjoy it!

Migrando MySql para Sql Server com Entity Framework

Eu até diria que este é um post extra de Natal, se eu não tivesse acabado de olhar no relógio e visto que já passou da meia-noite. De qualquer forma aqui estou eu com mais um post que espero que seja útil para mais alguém do que eu.

Desta vez criei um tutorial para ajudar o pessoal que tem uma base de dados MySql e quer migrá-la para SQL Server, ou vice-versa. Na verdade este post deveria se chamar “migre sua base de dados de um banco para outro” porque o que você vai ver aqui se aplica para Oracle, MySql, PostGre, Sql Server e até mesmo XML (como visto mais explicitamente no post anterior).

Veremos como se conectar no MySql usando o fantástico Entity Framework, que apesar de não oferecer nativamente suporte ao famoso banco open-source da Sun, digo, Oracle, se mostra bem compatível quando utilizamos o MySql Connector .NET, desenvolvido em Mono (versão open-source do framework .NET) e disponibilizado gratuitamente.

Mas chega de blá, blá, blá…

MySql com .NET?

É isso mesmo. Muitos devem ter lido a introdução e pensado com seus botões: “O que esse cara ‘tá falando? MySql com .NET?” e a resposta é: “YES, WE HAVE MYSQL!”.

Ok, eu nem sou tão fã assim do MySql, mas vamos concordar que é uma excelente notícia saber que temos acesso a um banco 100% free (pelo menos por enquanto) com nossa linguagem de programação favorita (não, não estou falando de VB.NET). Neste mercado de hosting brasileiro onde um banco Sql Server não sai por menos de vinte mangos mensais, é bom saber que podemos dividir nosso banco do WordPress com outras aplicações de graça, hehehehehe.

Mas nem tudo são flores, afinal MySql não é mantido pela Microsoft e portanto o Visual Studio não oferece suporte nativo ao mesmo. Para solucionar esse problema, a galera da Sun (sim o conector já é velho, da época da Sun) desenvolveu um conector MySql em .NET, usando o framework “pau-pra-toda-obra” Mono, que nada mais é do que uma versão open-source do .NET Framework desenvolvido pela comunidade e recentemente auxiliada pela Microsoft (sim, eu ouvi isso da boca do responsável pelo Mono Brasil no TechEd 2010).

Para adquirir seu conector basta usar este link e baixar a versão Windows (cuidado para não baixar os fontes, mas sim o executável que está zipado). Na data deste post a versão mais atual é 6.3.5 que juram de pé junto que está 100% compatível com o Visual Studio 2010. Para quem não sabe, as versões anteriores funcionavam somente no VS2008 e eu fui um dos que tiveram muitos problemas com esta transição…

Bom, instale a versão Full/Complete do conector e vamos prosseguir com nosso tutorial.

* OBS: além do conector MySql também vamos utilizar o Visual Studio 2010 (gratuito neste link) e o Sql Server 2008 R2 (gratuito neste link).

Let’s Get Started!

Iniciamos o projeto de hoje abrindo nosso VS2010 >File > New Project > Visual C# > Console Application. Sim, eu gosto de Consoles Applications, e daí?

Vamos dar o nome de MySql2SqlServer pois no meu exemplo quero migrar uma base de dados minha que está em um banco MySql para um novo banco Sql Server.

Eu sei que não devia mas vou dizer o motivo: eu não me adaptei com o MySql. Já tenho alguns anos de experiência com SQL Server, desde a versão 2000 e me mostro relutante em trocar de banco. Mais uma de minhas manias.

De qualquer forma você pode estar querendo fazer isso pelos seus motivos próprios ou de sua empresa, talvez você queira apenas pegar alguns registros de uma base existente e jogar em outra, talvez para fazer o upgrade de banco em uma aplicação legado, whatever.

Agora adicione um novo item em seu projeto, escolhendo na categoria Data, o item ADO.NET Entity Data Model, que será o nosso contexto de dados MySql via Entity Framework. Eu expliquei como funciona (por cima) o EF no último tuto, fora que no Google tem informação à beça, então não me aterei a ele aqui. Coloque o nome de ContextoMySql e manda ver no “Add”.

Na tela seguinte, “Entity Data Model Wizard”, clique em Next deixando a opção default marcada (Generate from database) e em seguida clique em “New Connection” para criarmos uma conexão com nossa base MySql.

A imagem abaixo mostra a janela de configuração da conexão, já preparada para configurar uma base MySql. Se no seu VS2010 não aparecer “MySql Database” na opção “Data Source”, clique em “Change” para escolhê-la. Se ainda assim não aparecer, experimente desinstalar o Conector, reiniciar o PC e depois instalar novamente (comigo fiz isso e funcionou). N

esta janela coloque os dados para acesso ao seu banco MySql, teste a conectividade e voltando à tela anterior não esqueça de marcar a segunda opção do RadioButton (“Yes, include sensitive…”) para guardar todos os dados da conexão no App.config (é apenas um projeto de teste, não se preocupe com segurança aqui). Next.

Na próxima tela (“Choose your database objects”) selecione todas as tabelas de seu banco MySql que deseje migrar, que no meu caso é apenas uma tabela contendo todas as marcas de carros existentes (Fiat, Ford, VW, etc). Finish.

Será adicionado o arquivo ContextoMySql.edmx à sua aplicação, bem como referências aos namespaces necessários. Para quem não possui uma base MySql para teste, foi incluído no fonte deste projeto uma pasta SQL contendo o script para criação desse banco MySql, já com a inserção dos dados contendo todas as marcas de carros, basta rodar esse script em um front-end MySql como o excelente SqlYog que ele criará a base, a tabela Marca e irá inserir os dados também.

Para testar seu contexto de dados com o MySql, digite o seguinte código dentro do método Main do arquivo Program.cs de sua Console Application:

O código é auto-explicativo e como resultado você deve ver impressa uma lista contendo todas as marcas de carros atualmente comercializados no Brasil (cortesia da tabela FIPE). Note como o Entity Framework abstrai o banco que está sendo utilizado fazendo com que você manipule o MySql da mesma forma como faria com o Sql Server.

Não é feitiçaria, é tecnologia!

Com o mapeamento objeto-relacional do banco MySql pronto, o segundo passo é criar o relacionamento com seu banco de destino, que no meu caso é o SQL Server. Note que usarei o mesmo banco Sql Server do tutorial anterior, chamado de Xml2Bd.

Para quem não possui tal banco, incluí um script SQL no fonte do projeto para criação do referido banco somente com a tabela Marca. Para quem já possui o banco, apenas exclua as linhas do script que criam a base, deixando somente a criação da tabela Marca e rode no Sql Server Management Studio. Os passos são os mesmos, com a exceção que o Data Source deve ser Sql Server e não MySql e chame este arquivo de ContextoSqlServer.

Se tudo der certo, mande compilar e você receberá como prêmio um monte de erros, como mostrados na figura abaixo.

Sim, é isso mesmo: existem nomes duplicados na sua aplicação. Isso acontece quando se adiciona mais de um ADO.NET Entity Data Model à sua aplicação com tabelas de nomes iguais (mesmo que em bancos diferentes).

Solução? Basta renomear a classe Marca de um dos arquivos .EDMX.

Eu escolhi renomear a classe Marca do arquivo ContextoSqlServer para MarcaSql, clicando sobre a classe e apertando F2. Após salvar o arquivo e compilar novamente, você verá que todos os erros se foram. Mas lembre-se: a classe Marca está associada à tabela Marca do banco MySql e a classe MarcaSql está associada à tabela Marca do banco Sql Server.

Você viu no trecho de código anterior como é fácil listar todas as Marcas de carros existentes no seu banco MySql. Agora, ao invés de imprimir cada um dos resultados da consulta, basta inseri-los no banco de destino, conforme segue abaixo:

Pronto!

Note como é simples manipular dois bancos ao mesmo tempo e até mesmo compartilhar dados entre os dois, mesmo em um ambiente heterogêneo como MySql com Sql Server. Recomendo que baixem os fontes e testem em suas máquinas. Quaisquer dúvidas, estou à disposição.

Conclusão

Espero que este pequeno tutorial tenha sido bom o bastante para mostrar que apesar da dobradinha .NET + SQL Server funcionar perfeitamente, é extremamente viável a utilização de outros bancos de dados junto à plataforma de desenvolvimento da Microsoft. Neste exemplo utilizamos MySql, mas sei de desenvolvedores que estão utilizando conectores Oracle e em meu trabalho já utilizamos o conector de PostGre SQL.

Este tutorial também serviu para mostrar como efetuar com sucesso uma tarefa que até pouco tempo atrás era impossível para mim: migrar dados entre bancos heterogêneos através de um script simples e de forma rápida.

Espero que tenham gostado e até a próxima!