Mostrando postagens com marcador Expressão Regular. Mostrar todas as postagens
Mostrando postagens com marcador Expressão Regular. Mostrar todas as postagens

quarta-feira, 9 de novembro de 2016

Executando um case insensitive match em strings

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

Algumas vezes já estive em situações em que precisava aplicar uma expressão regular em textos que desconhecia como era o emprego de letras maiúsculas e minúsculas, ou seja, não era possível determinar a forma do texto. Então, cheguei a um impasse: o que fazer ?

Eis aqui algumas opções que pensei:
  1. Transformar todas as letras do texto em minúsculas.
  2. Transformar todas as letras do texto em maiúsculas.
  3. Modificar minha expressão regular para ser case-insensitive.
Mas o que é case-insensitive ? É tudo aquilo que não diferencia maiúscula de minúscula, e vice-versa. Então, neste tipo de análise "PRECISO ESTUDAR SEMPRE" é igual a "preciso estudar sempre", que é igual a "Preciso Estudar Sempre" e assim por diante. Já o seu caso contrário, o case-sensitive, diferencia. Então, no nosso exemplo todas as palavras seriam consideradas diferentes umas das outras.

Case-sensitive = Diferencia maiúsculo de minúsculo
Case-insensitive = Não diferencia maiúsculo de minúsculo

Transformar todas as letras do texto em maiúsculas ou minúsculas me obrigaria a mapear todos os pontos que a leitura do texto era feita. Se houvessem muitos pontos, isso exigiria muitas modificações e muitas modificações exigem muitos testes. Então no final, o que era para ser algo fácil se tornou algo, talvez, um pouco complicado.

A única opção que resta é modificar a expressão regular, mas como fazer isso ? Mapear todos os intervalos de letras com maiúsculas e minúsculas, por exemplo [a-zA-Z], não é a melhor solução, pois se a expressão for muito grande com muitos intervalos podemos ter o mesmo problema que teríamos adotando a primeira ou segunda opção. Muitas modificações e talvez alguns erros.

Através de pesquisa consegui chegar a melhor solução. Em Java, assim como em outras linguagens, a respectiva API de expressões regulares oferece uma flag para case-insensitive. Como já citei em outros posts que possuo mais intimidade com a linguagem Java, mostrarei a aplicação dessa solução nesta plataforma, mas nada impede que você ache a mesma solução na linguagem de sua preferência.

1:  import java.util.regex.Pattern;  
2:    
3:  class CaseInsensitivePattern {  
4:       public static void main(String[] args) {  
5:            //Primeira forma de aplicação através da constante provida pela própria API.  
6:            System.out.println("Case insensitive match via constant: " + Pattern.compile("^([a-z]*\\s*)*$",Pattern.CASE_INSENSITIVE).matcher("PrEcIsO eStUdAr SeMpRe").matches());  
7:            System.out.println("Case sensitive match via constant: " + Pattern.compile("^([a-z]*\\s*)*$").matcher("PrEcIsO eStUdAr SeMpRe").matches());  
8:    
9:            //Segunda forma de aplicação através de uma flag na própria regex.  
10:            System.out.println("Case insensitive match via String: " + "PrEcIsO eStUdAr SeMpRe".matches("(?i)^([a-z]*\\s*)*$"));  
11:            System.out.println("Case sensitive match via String: " + "PrEcIsO eStUdAr SeMpRe".matches("^([a-z]*\\s*)*$"));  
12:       }  
13:  }  

Note que forneci através do exemplo duas formas de executar uma expressão regular em case-insensitive. Nas linhas 6 e 7, apresento abordagens de uso da constante Pattern.CASE_INSENSITIVE, da classe Pattern, a qual simboliza que a dada expressão deve ser executada na forma configurada. Respectivamente, uma executa um case insensitive match e a outra um case sensitive match.

Nas linhas 10 e 11, utilizo uma outra abordagem. É possível embutir na própria expressão regular uma flag sinalizando que esta deve realizar uma case insensitive match. Tal é atingido através da flag (?i).

Entre essas duas abordagens apresentadas. Qual utilizar ?

Como sempre digo aqui, não existem balas de prata. Quem vai determinar a melhor solução é a sua necessidade. Talvez, para alguma situação é melhor transformar o texto para maiúsculo ou minúsculo do que modificar a regex, ou para outras situações é mais vantajoso trabalhar em cima da expressão. Contudo, para modificações na expressão, entre as duas opções apresentadas, eu ficaria com a segunda pelo motivo dessa ser mais desacoplada. Seria possível assim configurar minha expressão em um arquivo externo (txt, properties, etc.) e não precisar modificar meu programa principal. Caso algum erro aconteça, eu sei que o único ponto possível seria neste arquivo.

É possível configurar mais de uma flag nas duas abordagens, por exemplo, se é desejado executar case insensitive match em uma string que possua caracteres acentuados, ou um multiline. Por padrão a flag (?i) não cobre casos de caracteres Unicode. Para tal é necessário utilizar em conjunto a flag (?u), resultando em (?iu).

Deixarei neste post referências para outros dois posts, onde falo sobre a opção multiline e caracteres Unicode, e uma ferramenta muito boa para validação de expressões regulares.

Para baixar o código:
Link do repositório GitHub: https://github.com/PrecisoEstudarSempre/CaseInsensitivePattern.git
Link do GoogleDrive: https://drive.google.com/file/d/0BzDmhBY6luU6V2ZoSjFpUmRCYlk/view?usp=sharing
Link do Dropbox: https://www.dropbox.com/s/uv4vgwxnamteujt/CaseInsensitivePattern.rar?dl=0

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, me mande um e-mail ou, na nossa página do facebook.

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA
Leia Mais ››

quinta-feira, 27 de outubro de 2016

Avaliando blocos de texto com expressões regulares

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

Como todos sabemos, a leitura tradicional de um arquivo em qualquer linguagem de programação é a criação de um ponteiro em memória para uma representação do arquivo. Após isso, é aberta uma conexão de leitura/escrita e a partir daí toda a mágica que já conhecemos acontece. Se o seu programa deseja ler ou escrever informações do ou no arquivo, ele consegue. Contudo, devemos agora nos concentrar mais no processo de leitura de arquivos.

Geralmente, ele acontece linha a linha e a linguagem de programação usada para tal tarefa oferece métodos para recuperar esta em forma de string. Com o dado em nossas mãos podemos fazer o que quiser, por exemplo: aplicar uma expressão regular, concatenar dados, realizar análises e etc. Se concentre no primeiro exemplo, a aplicação de uma expressão regular.

Este assunto aqui no blog não é nenhuma novidade, pois já estudamos em outros post diversas expressões regulares ou regexs para infinitas utilidades. Mas, e se desejássemos capturar um bloco de texto oriundo de um arquivo através delas ? Como faríamos ?

A princípio parece uma tarefa que dá um nó na cabeça, pois como já citamos nos parágrafos acima a leitura acontece linha a linha, e isso não vai mudar. Então, precisamos de uma lógica boa que consiga capturar este bloco através de várias expressões passadas para ele.

Para a solução deste problema, utilizarei mais uma vez a linguagem de programação Java pois possuo um alto nível de afinidade com ela, mas esta solução é estendível para qualquer uma.

Antes de montar o algoritmo precisamos chegar a um acordo de como será nossa estrutura de regexs. Para que um bloco inteiro, onde quem define a quantidade de linhas desse bloco é a sua necessidade, possa ser capturado, precisamos mapear como é cada linha. Então, para fins de estudos utilizaremos um arquivo XML, pois este tipo de arquivo possui uma estrutura hierárquica que neste momento nos facilita a enxergar a solução. Então, no fim teremos.

Arquivo precisoestudarsempre.xml

 precisoestudarsempre.xml  
   
 <blog>  
      <nome>Preciso Estudar Sempre</nome>  
      <email>precisoestudarsempre@gmail.com</email>  
      <descricao>Melhor blog do mundo :)</descricao>  
 </blog>  

Nossas expressões regulares:

 private final String[] arrayOfRegex = {"<nome>([a-zA-Z]+\s*)*</nome>",  
                                              "<email>.*</email>",  
                                              "<descricao>.*</descricao>"};  

IMPORTANTE: É importante deixar claro aqui que neste post não destrincharei cada regex como já fiz em outros post. Nosso foco aqui é o algoritmo e não o estudo de expressões. Deixarei no fim do post vários links para outros post aqui do blog onde já expliquei várias vezes como e o que são as expressões regulares.

Agora que já montamos o nosso arquivo e sua respectiva estrutura de regexs, já temos o necessário para construir nosso algoritmo.

 private void evaluateTextBlockRegex(BufferedReader bf){  
   try{  
     int regexCounter;  
     int lineNumber;  
     regexCounter = lineNumber = 0;  
     boolean isMatchedRegex = true;  
     Pattern pattern = null;  
             
     while (bf.ready()) {  
       String line = bf.readLine();  
       lineNumber++;          
   
       if(regexCounter == arrayOfRegex.length){  
         //só para pular linha  
         System.out.println();  
         regexCounter = 0;            
       }                  
       while (isMatchedRegex) {  
         String regex = arrayOfRegex[regexCounter];  
           
         try {  
           pattern = Pattern.compile(regex);  
           Matcher matcher = pattern.matcher(line);  
           isMatchedRegex = matcher.find();                                          
           if(isMatchedRegex){  
             //exibo a linha capturada  
             System.out.println("Linha " + lineNumber + " capturada: " + line);                
             regexCounter++;  
             break;  
           }  
         } catch (java.util.regex.PatternSyntaxException pse) {  
           //talvez que a regex não seja compilada  
           isMatchedRegex = false;  
         }  
       }          
       if(!isMatchedRegex){            
         isMatchedRegex = true;  
         regexCounter = 0;  
       }          
     }  
   } catch(java.io.IOException e){  
     System.out.println("An I/O error occurs!");  
   }  
 }  

O funcionamento dele é o seguinte: o arquivo é lido linha após linha, e para cada uma é testada sempre a primeira regex. Caso esta tenha sucesso, o contador regexCounter é incrementado e uma nova linha é carregada na variável line, e assim o algoritmo testa a segunda regex, repetindo este processo até o array de regex chegar ao seu fim. Para que um bloco de texto seja capturado a correspondência entre linhas do arquivo e expressões regulares devem ser linha a linha, ou seja, a primeira regex corresponde a primeira linha do bloco e assim sucessivamente. A figura 1 esclarece tal relacionamento.
Figura 1 - Relacionamento linha - regex
Após todas as linhas terem sido relacionados com suas respectivas regexs, regexCounter é zerado para que o aglomerado de regex procure novamente por novas correspondências. Este é necessário para que seja possível acessar a próxima expressão do array. A flag booleana isMatchedRegex é necessária pois podem existir casos onde somente algumas linhas correspondem e outras não. Quando uma que não corresponde for avaliada, a análise precisa ser reiniciada, ou seja, é iniciada mais uma vez a procura por um bloco de texto compatível.

Todo este procedimento citado neste dois últimos parágrafos se repetem até o final do arquivo.

Para um bom conhecedor de Java uma pergunta pode surgir. Porque não usar a opção multiline da classe Pattern ? Sim, é verdade, existe uma opção pronta para este tipo de avaliação. Contudo, como no nosso caso estamos recuperando texto direto de um arquivo seria necessário transformar todo o arquivo em uma única string, para depois poder usar tal artifício. Tal processo pode apresentar problemas de tamanho de memória visto que o arquivo pode ser muito grande. Um outro ponto que torna nosso algoritmo mais interessante é que ele se encaixa mais facilmente em abordagens tradicionais da leitura de arquivos. Através de pesquisa é possível coletar relatos que esta opção traz problemas para programas que operam em multiplataformas, visto que cada plataforma tem um caractere de quebra de linha próprio. Lembra que já falamos disso aqui ?

Como eu sempre digo, não existem balas de prata. Não existe uma única solução correta. Devemos medir os prós e contras, e assim tomar nossa decisão.

Bem amigos, acabamos mais um post. Espero que vocês tenham gostado.

Baixe o código-fonte
Link do GitHub: https://github.com/PrecisoEstudarSempre/EvaluateTextBlockRegex.git
Link no Dropbox: https://www.dropbox.com/sh/trgchzdjr198ja7/AABCqIPsGPe15OC43IKKxiAga?dl=0
Link no GoogleDrive: https://drive.google.com/drive/folders/0BzDmhBY6luU6ZUR1d0tXRk91QkU?usp=sharing

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, me mande um e-mail ou, na nossa página do facebook.

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA



Non-break space, já ouviu falar ? - Regex em Java - http://precisoestudarsempre.blogspot.com.br/2015/11/non-break-space-ja-ouviu-falar-regex-em.html



Leia Mais ››

segunda-feira, 29 de dezembro de 2014

Regex para validação de moeda

Olá amigos leitores. O tema do post dessa semana foi escolhido por uma necessidade que tive muito tempo atrás. Quando me deparei com o problema aplicar máscaras monetárias (R$) em campos de texto, resolvi construir uma solução própria. Poderia ter usado soluções prontas ? Sim, poderia mas, eu não queria ficar amarrado à soluções de terceiros.

Para resolver o problema, não queria criar uma função grande e de complexidade elevada sendo que, uma regex poderia lidar com esse problema. Então, vamos lá !!

A regex construída:

 ^R\$(\d{1,3}(\.\d{3})*|\d+)(\,\d{2})?$  

Esta regex aceita estes valores:
  • R$2,00
  • R$20,00
  • R$2.000,00
  • R$20.000,00
  • R$200.000,00
  • R$2.000.000,00
Mas, não aceita esses:
  • 2,00
  • 2
  • R$aaa44,00
  • B$20,00
  • $100,00
Importante: Note que o caracter usado para separar os reais dos centavos é o vírgula (,) e não o ponto (.).

Agora, vamos às partes das explicações. Vamos destrinchar a regex.

Os caracteres ^ e $, respectivamente, no início e fim da expressão regular denotam um intervalo fechado, ou seja, o texto que for avaliado só pode conter o dado monetário.

O caracter representa o próprio R do R$. Então, até aí, nenhum mistério. O $ possui significado especial nas regex, logo precisamos de uma barra de escape (\$) para que ela adote a representação de cifrão ($).

Com (\d{1,3}(\.\d{3})*|\d+) definimos que teremos ou algo desse tipo: 2.000 ou 2000. Vamos melhorar isso.

\d{1,3} -> \d representa um dígito. O {1,3} representa no mínimo 1 e no máximo 3 ocorrências. Então, quando temos \d{1,3} estamos querendo dizer que, teremos um dígito que terá no mínimo 1 e no máximo 3 ocorrências.
(....) -> Os parênteses () são usados para definir um subconjunto.
\. -> A barra de escape é usada no ponto (.) para definir o separador de milhar.
\d{3} -> Representa um dígito que será repetido três vezes.
* -> Representa que algo será repetido 0 ou infinitas vezes.
| -> Representa uma operação de ou.
\d+ -> Representa que um dígito será repetido no mínimo 1 e no máximo infinitas vezes.

Agora, que já deciframos uma parte da regex, falta a outra parte: (\,\d{2})?
Essa parte define os centavos logo, algo assim será aceito: ,00.

\, -> Caracter vírgula (,) com barra de escape para assumir a representação de vírgula.
\d{2} -> Ver explicação acima.
(....) -> Ver explicação acima.
? -> Representa que algo será repetido 0 ou 1 vez.

Agora que já esmiuçamos toda a regex, já entendemos como ela funciona e como ela aceita os valores descritos acima. É só pegar e usar.

Sugestões ? Críticas ? Elogios ? Deixe aí embaixo nos comentários ou, na página do facebook.

Página do facebook: https://www.facebook.com/precisoestudarsempre/
Leia Mais ››