Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Bruno Augusto

[Estudo de Caso] Função para verificar...

Recommended Posts

AVISO

 

Este é um tópico GRANDE e EXAUSTIVO.Se você tem nervos sensíveis e/ou não quer acabar como Charles Chaplin em "Tempos Modernos" (nussa) nem continue a ler.

 

Esse debate tempor objetivo desenvolver ou aprimorar uma solução eficaz contra um problema no mínimo diferente.

 

Agradeço por antecedência.

===========================

 

Essa eu quero ver.

 

Estava eu testando meu domínio sobre Expressões Regulares com o Guia de Consulta do Aurélio e me perguntei:

 

"Como verificar se uma string é uma Expressão Regular".

 

Acredito que muitos já pensaram nisso, mas nunca vi jeito para descobrir.

 

Fuçando no Google CodeSearch encontrei duas possíveis soluções:

 

Possível Solução #1

function isRegex($var, $regex) {
  return filter_var($var, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $regex)));
}

Passei a mesma variável para os dois parâmetros e ora funcionava, ora não.

 

Ex:

 

$var = '/^(.*?)$/';

Isso retornava uma string (com var_dump)

 

$var = '/^(.*?)foo$/';

Isso retornava FALSE.

 

Resultado: NEGATIVO

 

Possível Solução #2

function isRegex2($value, $pattern = NULL) {
   return (bool) preg_match($pattern, $value);
}

Pelo que consegui entender, essa faz/fazia parte do Zend_Filter.

 

Não funcionou nas mesmas condições que a primeira possível solução.

Porém, o parâmetro $patterné nulo, logo poderia tentar ignorá-lo e, obviamente, preg_match() gerou um erro pois não havia padrão a ser usado.

 

Resultado: NEGATIVO

 

Possível Solução #3

function isRegex3($regexp){
$er   = '/\/(\\[^\x00-\x1f]|\[(\\[^\x00-\x1f]|[^\x00-\x1f\\\/])*\]|[^\x00-\x1f\\\/\[])+\/[gim]*/';

return (bool) preg_match($er,$regexp);
}

Essa eu encontrei por acaso numa palestra em vídeo do Douglas Crockford.

Tive o trabalho de copiar caractere por caractere e testar dentro do preg_match() montado acima.

 

No próprio vídeo, ele menciona que essa ER está bizarra e, justamente por isso, gerou um erro:

 

Warning: preg_match() [function.preg-match]: Unknown modifier ']'(...)

Alguém tentou corrigí-la alterando para isso:

 

$er = '/\/(\[^\x00-\x1f]|[(\[^\x00-\x1f]|[^\x00-\x1f\\/])]|[^\x00-\x1f\\/[])+\/[gim]/';

Mas parece que não o fez direito pois gerou outro erro:

 

Warning: preg_match() [function.preg-match]: Compilation failed: unmatched parentheses at offset 65

Então, eu, morrendo de medo desse bicho cabeludo, tentei re-arrumar:

 

$er = '/\/(\[^\x00-\x1f]|\[(\[^\x00-\x1f]|[^\x00-\x1f\\/])*\]|[^\x00-\x1f\\/\[])+\/[gim]*/';

E para minha supresa funcionou :o

 

Fazendo como que:

 

$var = '/^(.*?)foo$/';

Casasse como sendo uma ER válida.

 

Porém, como nem tudo são flores, fui depurar manualmente e tirei o )foo e continuou casando e retornando TRUE.

Fui além e mudei a ER alvo da verificação para:

 

$var = '(.*?';

E só assim retornou FALSE. Ou seja, só barrou porque tirei as barras delimitadoras e as âncoras de início e de fim.

Felizmente esse casamento incorreto só acontece quando os delimitadores são barras. Tanto é verdade que testei com arrobas e deu certo, continuou retornando FALSE.

 

Resultado: QUASE PERFEITO

 

Antes de ver essas solução de um profissional, encontrei outra, porém em Perl que não entendo lhufas:

 

Possível solução #4

sub is_valid_re {
    eval { qr/$_[0]/ };
    return $@ ? 'NO' : 'YES';
}
Resultado: INCONCLUSIVO

 

Tentei reproduzir em PHP com a ajuda de uma tabela de tradução entre as linguagens criada por Robert Kline

 

Mas não adiantou nada e acabei desisitindo.

 

Então, depois de um super exaustivo tópico, a pergunta:

 

E então? É possível validar uma string verificando se ela é uma ER?

A solução indireta do Douglas Crockford me pareceu mais sensata e funcional, porém tem esse contratempo que pode ocasionar imprevistos e como não tenho certeza se tem a ver com a "portabilização" entre as linguagens (visto que essa ER originalmente é para JavaScript),não pude resolver ainda.

 

Bom, é isso se quiserem (e puderem), comentem para desenvolvermos juntos uma solução para isso.

 

O mais trabalhoso eu fiz: encontrar alternativas e propor um "debate". Agora vamos aperfiçoá-los.

 

Editado por Bruno Augusto

Motivo: Remover BBCode [perl[/perl] que não existe.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Que tal ?

function isRegex($pattern) {

return (preg_quote($pattern) === $pattern)? false : true;

}

 

O único problema que eu notei foi as barras invertidas :(

Tb nem testei direito... ^_^

 

[]'s

Compartilhar este post


Link para o post
Compartilhar em outros sites

"Como verificar se uma string é uma Expressão Regular".

 

Bom amigo, hehehe

Vou tentar expor um problema ainda maior para você, sente-se e cuidado para não cair da cadeira:

Se é uma string é por definição uma expressão regular !!!!

 

Isso porque:

 

$string = "Essa é a minha string de teste";
$regexp  = "string";

if ( preg_match( sprintf( "/%s/" , $regexp ) , $string ) ){
    printf( "%s é uma expressão regular porque o padrão foi encontrado em: %s" , $regexp , $string );
}

As expressões regulares surgiram devido a necessidade de se encontrar um padrão dentro de um texto, esse padrão pode ser uma string, uma frase, alguns pontos, números ou até algumas palavras chave definidas pela expressão regular.

Porém, se você usar uma expressão regular para encontrar palavras chave de uma expressão regular em uma string você irá encontrar padrões que parecem com palavras chave de uma expressão regular mas não são, por exemplo:

 

c:\diretorio

 

\d em uma expressão regular é interpretado como um número, e não existe um número em c:\diretorio.

 

Outro caso:

 

http://umdominio/uma+string+com+espacos

 

Novamente temos um problema,

 

+ em uma expressão regular é um operador de repetição que indica uma ou mais vezes

 

Resumindo, qualquer coisa é uma expressão regular, a questão é se uma determinada expressão regular é regular para um determinado texto.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é como @João Batista Neto disse, é meio inviável checar se há regex em uma string, só da presença de um metacaracter escapado ou não, ela já torna o resultado diferente, foi outro erro que eu pude perceber em minha função criada no post anterior :

 

Que tal ?

function isRegex($pattern) {

return (preg_quote($pattern) === $pattern)? false : true;

}

 

O único problema que eu notei foi as barras invertidas :(

Tb nem testei direito... ^_^

 

[]'s

 

[]'s

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

"Como verificar se uma string é uma Expressão Regular".

 

Bom amigo, hehehe

Vou tentar expor um problema ainda maior para você, sente-se e cuidado para não cair da cadeira:

Se é uma string é por definição uma expressão regular !!!!

Exato. Porém, a primeira coisa que vem à cabeça quando se fala de ER são metacaracteres, grupos, listas e etc.

 

Porém nem toda string pode ser usada como Expressão Regular, pois preg_match() acusaria erro de delimitador caso a string a ser usada como ER possua primeiro e últimos caracteres diferentes.

 

Tanto é verdade que passando um simples foo para a ER que estava quase perfeita retornou FALSE, tanto com como sem delimitadores arroba (por exemplo).

 

Acredito que possível como foi demonstrado, mas precisa de alguns ajustes para, por exemplo, verificar se tem ao menos um metacaractere não escapado na string.

 

Eu particularmente não saberia fazer pois sequer sei o que significa x00 ou xf1, mas desconfio, pois se estão em uma lista, é um intervalo de caracters da tabela ASCII.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Exato. Porém, a primeira coisa que vem à cabeça quando se fala de ER são metacaracteres, grupos, listas e etc.

 

Bom, o fato de você pensar nos metacaracteres não significa necessariamente que essa seja a definição de expressão regular.

 

Porém nem toda string pode ser usada como Expressão Regular, pois preg_match() acusaria erro de delimitador caso a string a ser usada como ER possua primeiro e últimos caracteres diferentes.

 

Claro que pode, imagine que eu tenha um arquivo com o seguinte conteúdo:

 

10 ^ 2 = 100; email@dominio.com; \d não é um número e R$ 30.00 é um valor

 

$str = "10 ^ 2 = 100; email@dominio.com; c:\d não contém um número e R$ 30.00 é um valor";
$mtc = array();

preg_match( sprintf( "/%s/" , preg_quote( $str , "\\" ) ) , $str , $mtc );
var_dump( $mtc );

A saída será:

10 ^ 2 = 100; email@dominio.com; c:\d não contém um número e R$ 30.00 é um valor

 

Tanto é verdade que passando um simples foo para a ER que estava quase perfeita retornou FALSE, tanto com como sem delimitadores arroba (por exemplo).

 

O fato de preg_match ter retornado false apenas significa que a expressão não é regular para o texto que você usou para fazer a comparação e não que ela é inválida.

 

Acredito que possível como foi demonstrado, mas precisa de alguns ajustes para, por exemplo, verificar se tem ao menos um metacaractere não escapado na string.

 

Claro que você conseguirá fazer a verificação, porém a questão é, o resultado será sempre positivo ou você terá tantos falsos positivos que tornará o uso da função inviável ?

 

Eu particularmente não saberia fazer pois sequer sei o que significa x00 ou xf1, mas desconfio, pois se estão em uma lista, é um intervalo de caracters da tabela ASCII.

 

O modificador x indica um número em hexadecimal, ou seja

x00 é um NULL

e xf1 é o decimal 241 que é o ñ (letra n minúscula com o til)

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

Porém nem toda string pode ser usada como Expressão Regular, pois preg_match() acusaria erro de delimitador caso a string a ser usada como ER possua primeiro e últimos caracteres diferentes.

 

Claro que pode, imagine que eu tenha um arquivo com o seguinte conteúdo:

 

10 ^ 2 = 100; email@dominio.com; \d não é um número e R$ 30.00 é um valor

 

$str = "10 ^ 2 = 100; email@dominio.com; c:\d não contém um número e R$ 30.00 é um valor";
$mtc = array();

preg_match( sprintf( "/%s/" , preg_quote( $str , "\\" ) ) , $str , $mtc );
var_dump( $mtc );

A saída será:

10 ^ 2 = 100; email@dominio.com; c:\d não contém um número e R$ 30.00 é um valor

 

Mas perceba que você adicionou manualmente os delimitadores, não sendo eles, potencialmente parte da strng original. Ou não? :unsure:

 

 

Acredito que possível como foi demonstrado, mas precisa de alguns ajustes para, por exemplo, verificar se tem ao menos um metacaractere não escapado na string.

 

Claro que você conseguirá fazer a verificação, porém a questão é, o resultado será sempre positivo ou você terá tantos falsos positivos que tornará o uso da função inviável ?

 

Mas identificar um falso positivo e resolvê-lo não é melhor do ignorá-lo?

 

Você saberia dizer alguns falsos positivos que afetariam negativa e significativamente a função?

 

 

Eu particularmente não saberia fazer pois sequer sei o que significa x00 ou xf1, mas desconfio, pois se estão em uma lista, é um intervalo de caracters da tabela ASCII.

 

O modificador x indica um número em hexadecimal, ou seja

x00 é um NULL

e xf1 é o decimal 241 que é o ñ (letra n minúscula com o til)

 

Sobre que termo eu deveria pesquisar para encontrar mais a respeito?

 

[OFF] Qual será a próxima cor de aninhamento dos comentários :lol:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom, um experimento:

/(?:\/)?(?:\^)?(\\{1,2}(A|b|B|d|D|G|s|S|w|W|z|Z)|.+(\?|\*|\+){1}|\(\?:.+\)|\[(\^.+|.\-.)\]|.+\{\d+(,\d+)?\}|\\(b|B){1}.+\\(b|B){1})(?:$)?(?:\/)?/1

Essa expressão devolveu verdadeiro para:

 

$test = '^(("[\w-\s]+")|([\w-]+(?:\.[\w-]+)*)|("[\w-\s]+")([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)';
$test = '/^(.*?)$/';
$test = '/^(.*?)foo$/';
$test = "[^\d]{1,2}";

e falso para:

$test = "Neto";

Porém houve um falso verdadeiro para:

$test = "c:\diretorio";

Devido ao fato do fórum destruir as expressões regulares o código do experimento está em: http://neto.joaobatista.pastebin.com/f3fcd2f91

 

Sobre que termo eu deveria pesquisar para encontrar mais a respeito?

 

Quando se fala em PCRE o melhor lugar para pesquisa é: http://www.pcre.org/pcre.txt

 

[OFF] Qual será a próxima cor de aninhamento dos comentários :lol:

 

 

 

Sei lá... :P

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

:o

 

Já tô até meio zonzo de tantos metas :lol:

 

Já que, se não impossível, impraticável a criação de UMA ÚNICA ER de detecção, e se fosse construída uma função que levasse em conta diversos algoritmos?

 

Assim, se a string passasse pelo primeiro algoritmo, retornando TRUE, seria associado internamente à função esse resultado e a verificação direcionada ao segundo algoritmo.

 

Assim se repetiria tantas vezes quantos algoritmos puderem ser desenvolvidos. No final, ponderaria-se quantas vezes passou no algoritmo e quantas vezes não até dar um veredicto com pelo menos 75% de certeza.

 

Se praticável, topa participar de mini-projeto?

 

[OFF] Testado. Bege éaúltima :lol:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Se praticável, topa participar de mini-projeto?

 

Existe um campo da computação que se chama análise de expressões. Eu fiz certa vez (apenas por diversão) um analisador onde era possível criar funções, variáveis, métodos e tal. Tudo dentro do PHP com variáveis tipadas e a possibilidade de se estender as classes pré-definidas.

 

var numero:int = 5;

int.soma = function( num:int ):int {
   return( this + num );
}

printf( "%d\n" , numero.soma( 10 ) );

Esse código é passado para minha classe Evaluate da seguinte forma:

 

<?
require( "Evaluate.php" );

$str = <<<STR
 var numero:int = 5;
 
 int.soma = function( num:int ):int {
    return( this + num );
 }
 
 printf( "%d\n" , numero.soma( 10 ) );
STR;

Evaluate::parse( $str ); //15

Desenvolver um analisador 100% eficaz para a PCRE não só é possível como não é tão complicado (uma vez que a documentação é rica em detalhes), porém o uso de uma expressão regular é ineficiente, o caminho é partir para a analise da expressão tal como é feita a analise de uma linguagem interpretada como o PHP, a diferença é que em vez de trabalhar com identificadores e palavras chave, o trabalho seria sobre os metacaracteres e backslashes.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não sabia dessa possibilidade de programação com PHP.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.