Dúvida Regex [RESOLVIDO]

Pessoal,

Eu não consigo compreender o regex… já li vários tutoriais e sites com exemplos e tudo e não consigo fazer algo simples…

tenho que ler um texto e geralmente vem assim, uma pergunta e uma resposta nesse formato:

pergunta
resposta

pergunta
resposta

precisava procurar pela pergunta que é marcada pelo “>” e pegar a resposta
pensei primeiro em verificar se está tendo pergunta, verificando se retornou “>” ai continuar com a leitura

	public void validaResposta(String input) {
		Pattern pattern;
		Matcher matcher;

		pattern = Pattern.compile("NAO SEI COMO FAZER", 32);
		matcher = pattern.matcher(input);
		
		if (matcher.matches()) {
			
			System.out.println("RESPOSTA: " + matcher);
		}
		
	}

A moral do regex é fazer um texto cujos caracteres sejam “genéricos”, de modo a se encaixarem com todas as combinações de textos aceitáveis (e somente com as aceitáveis).

Por exemplo, um possível regex pra ler uma data num formato com 28/05/2014 poderia ser assim:
[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]

Por partes…

[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]
Essa parte em negrito significa: qualquer dígito que vá de 0 até 9.

[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]
Mesma coisa que a anterior.

[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]
Aqui, ele diz: aceitar somente uma barra

E assim sucessivamente.

A tradução para o português seria:
um número, um número, uma barra, um número, um número, uma barra, um número, um número, um número, um número.

É importante frisar que ele aceitaria uma entrada tipo 00/00/0000, ou seja, ele não é um regex muito útil pra validar uma data de verdade :smiley:

No exemplo que você deu, sobre descobrir se é ou não uma pergunta, eu teria o seguinte raciocínio:
Fazer um regex que aceita o primeiro caracter como sendo um sinal de “maior que” (no caso, o >) e qualquer coisa depois disso.

Espero que ajude :smiley:

Você pode testar seus regex aqui nesse site: http://rubular.com/

Você escreve o regex em cima e as opções embaixo. E o resultado vai saindo do lado. Além disso, ele tem uma pequena referência no final da página.

Para esse seu caso você deve reconhecer:

  1. Um sinal de maior ( > ) no início da linha
  2. Qualquer caracter que venha depois disso
  3. Uma quebra de linha
  4. Qualquer caracter que venha até chegar no final da linha

Ficando assim:
1.

"^>"
  1. e 3.
".*?\n"
".*?\n$"

Logo, algo assim resolve:

"^>(.*?)\n(.*?)$"

Os parenteses servem para definir os grupos que você vai selecionar, então o grupo de índice 1 é a pergunta e o grupo de índice 2 é a resposta.
Vale lembrar que o grupo de índice 0 é a regular INTEIRA.

putz eu entendo, mas não estou conseguindo colocar na prática…

por exemplo estou buscando a palavra Password e ver se deu Password OK ou Password NOK e quando encontrar o próximo > terminar… e não está dando…

colocquie assim:

("[Password]*>$", 32);

o que tenho que ler e ir separando é isso…

Password OK

daf hardmodel
8
daf hardversion
0
daf hardtype
7
daf bootversion
1

Consegui pelo site, mas no método não vai…
Quero fazer cada método para pegar um valor desse… pensei em procurar pela “pergunta” e pegar a resposta…

eu usei isso no site (confesso que foi por cagada) e saiu só a resposta do daf hardmodel “8”, mas no método não retorna nada…

Usei o Regex: ^>.hardmodel\n(.?)$

Não conseguiu USAR o site ou não conseguiu fazer a sua regex funcionar?

Você pode fazer isso em 2 passos:

"^Password\\s*OK\\s*\\n((.|\\n)*)"

Depois, você pega o grupo 1 dessa regex e aplica a regular da resposta acima.

Lembre que para o Java, como a regex fica em String, você não pode usar a \ sozinha, tem que ficar sempre 2, assim \.
Então, quando o código for \n, no java vai ficar assim: \n
quando for \s no java fica \s.

Quando você quiser pegar uma aspa dupla ("), você usa UMA barra antes dela, assim:

"minha regex e no meio \" dela tem uma aspa dupla"

No site esqueci de tirar as aspas… eu editei mas acho que vc não leu depois, foi mal por escrever antes de tentar…

Funcionou perfeitamente como vc fez…

só no método que não estou conseguindo separar… como passei o do hardmodel, ele traz certinho o resultado “8” mas no método não sai só o valor, vem todo o escrito junto…

Então poste o método completo aqui…

Valeu Rafael pela ajuda,
as entradas são assim, e tenho que separar cada uma por um método

Password OK

daf hardmodel
8
daf hardversion
0
daf hardtype
7
daf bootversion
1
daf appversion
81280001

[code]

public void validaResposta(String input) {
	Pattern pattern;
	Matcher matcher;

	pattern = Pattern.compile("^Password\\s*OK\\s*\\n((.|\\n)*)" , 32);
	matcher = pattern.matcher(input);
	
	if (matcher.matches()) {
		matcher.replaceAll("$1$2");
		System.out.println("PEGUEI DE UM OUTRO METODO AQUI " + matcher.replaceAll("$1$2"));
	}
	
}


public void validaPassword(String input) {
	Pattern pattern;
	Matcher matcher;
	
	pattern = Pattern.compile("^>.*hardmodel\n(.*?)$");
	matcher = pattern.matcher(input);
	
	if (matcher.matches()) {
		matcher.replaceAll("$1$2");
		System.out.println("PEGUEI DE UM OUTRO METODO AQUI" + matcher.replaceAll("$1$2"));
	}
	
}

}[/code]

O intuito é fazer o que no final desses métodos?

Você quer pegar um Array com a pergunta e password, você quer popular um objeto, você quer devolver só uma String mesmo… Existem algumas coisas erradas ai, mas antes, me explique direitinho o que você precisa fazer com isso:

>daf hardmodel 8
Depois, me explique por que você fez um só para o hardmodel? É só ele que vale ou vc pretende fazer um para cada um (duplicando esse código)?

Quero pegar o resultado de cada pergunta, o resultado vou colocar na tela (com swing no aplicativo) para preencher um campo,

por exemplo,

Hardmodel: [ 8 ]
Version: [ XX ]

eu leio uma porta serial, que me retorna todos esses campos de uma só vez, então vou criar vários métodos para cada item para pegar o valor de cada um.

e para identificar qual campo é o valor, tenho que separar todos e pegar um de cada vez para cada pergunta

entendeu?

assim quero criar métodos para cada pergunta e pegar a resposta e colocar na tela nos seus respectivos campos.

Beleza, vamos lá:

[code] private static final Pattern PATTERN_VALIDA_RESPOSTA = Pattern.compile("^Password\sOK\s\n((.|\n))"); // Cria uma vez só.
private static final Pattern PATTERN_VALIDA_PASSWORD = Pattern.compile("(?<=>)((.
?)([0-9]+))", Pattern.DOTALL); // É importante ter esse parametro

public Map&lt;String, String&gt; validaResposta(String input) {
    Matcher matcher = PATTERN_VALIDA_RESPOSTA.matcher(input);

    if (matcher.matches()) {
    	String passwords = matcher.group(1); // no grupo 1 temos TUDO que vem depois de Password OK
    	System.out.println(&quot;&gt;&gt;&gt;&gt; Passwords ok: &quot; + passwords);

        return buildMap(passwords); // é válida
    }
    
    return null; // é inválida
}

private Map&lt;String, String&gt; buildMap(String passwords) {
	Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();

	Matcher matcher = PATTERN_VALIDA_PASSWORD.matcher(passwords);

	while (matcher.find()) {
		String nome = matcher.group(2);// só o nome;
		String password = matcher.group(3);// só o password;

		map.put(nome, password); // popula o map.
	}

	System.out.println(&quot;&gt;&gt;&gt;&gt; map ok: &quot; + map);

	return map;
}[/code]

O ponto é o seguinte: você usa o find para ir iterando sobre cada pedaço da String (que eu tive que mudar a regex, para atender o caso).

Depois, basta fazer isso:

map.get(&quot;daf hardmodel&quot;); // retorna 8
map.get(&quot;daf appversion&quot;); // retorna 81280001

Valeu rafa pela resposta… mas não posso sair do padrão de métodos :frowning:

O “Dono do sistema” quer que eu faça igual ele fez para outro dispositivo serial… verificando assim, achou a parte do hardmodel? então pega o valor da linha debaixo…

daf hardmodel (procurar por este…)
8 (… e pegar esse valor)

assim em diante… é um cara fechado que acha que só o jeito que ele conhece que é bom… to me matando nesse projeto aqui no trabalho por causa disso

Então está na hora de procurar outro emprego. ¯_( ツ )_/¯
Gente assim só vai fazer você andar para trás. Ele não vai aceitar nada novo/melhor.

Enfim, pode fazer isso, mas vc pode encapsular isso dele. Separe todo código repetido em um método e chame esse método. Não é bonito, mas é muito, mas muito “menos feio”.

É, estou mexendo meus pauzinhos com isso… rsrs… ta tenso,

assim é o código já criado por eles que eu tenho que deixar igual:

[code]
public void validaGPRS(String input) {
Pattern pattern;
Matcher matcher;

	pattern = Pattern.compile(".*There\\sis\\sGPRS\\saccess.*", 32);
	matcher = pattern.matcher(input);
	
	if (matcher.matches()) {
		dispositivos.setGprs("OK");
	
		
		System.out.println("GPRS: " + dispositivos.getGprs());
	}
}

public void validaRegistro(String input) {
	Pattern pattern;
	Matcher matcher;

	pattern = Pattern.compile(".*home\\sor\\sroaming.*", 32);
	matcher = pattern.matcher(input);
	
	if (matcher.matches()) {
		dispositivos.setRegistro("OK");
		
		
		System.out.println("Registro: " + dispositivos.getRegistro());
	}
}[/code]

Por isso disse que tenho que fazer um método para cada resposta.

Valeu Rafael, muito obrigado, vou ficar tentando aqui.

Só para saber, você sabe o que significa aquele 32 que está em todos os Pattern.compile?

Esse código está bem ruim… É a mesma coisa se eu te perguntar o que esse código abaixo está fazendo (o pior é que ele funciona):

Calendar a = Calendar.getInstance();
a.add(5, 6);

Não sei bem… porque mal vi regex… Eu peguei isso de gaiato…

Eu queria reformular tudo isso… mas como mexe com receber dados da porta serial, não quis mexer muito por falta de conhecimento, então nem sei como melhorar…

Vou tentar mostrar para eles que o que vc fez é o melhor jeito…

como chamaria esse map??

map.get("daf hardmodel"); // retorna 8  
map.get("daf appversion"); // retorna 81280001  

não consegui entender onde faço essa chamada…

E ta,bém não entra neste if :

if (matcher.matches())

E se vc tiver outra maneira de me ensinar (em cima desse que eles já fizeram) só para tentar melhorar isso eu agradeço muito…

Um bom jeito é pesquisando e testando…

Olhando a javadoc: http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

Você pode criar uma classe com um método main e mandar ele printar os valores, por exemplo.

Funciona assim, se você fizer isso:

public static void main(String[] args) {
	System.out.println(Pattern.UNIX_LINES);
	System.out.println(Pattern.CASE_INSENSITIVE);
	System.out.println(Pattern.COMMENTS);
	System.out.println(Pattern.MULTILINE);
	System.out.println(Pattern.LITERAL);
	System.out.println(Pattern.DOTALL);
	System.out.println(Pattern.UNICODE_CASE);
	System.out.println(Pattern.CANON_EQ);
}

Você vai saber qual o valor de cada propriedade e ainda vai ver que elas seguem as potências de base 2.
Ou seja: 2^0, 2^1, 2^2, 2^3, etc…

Com isso você consegue, por qualquer número positivo informado, saber quais opções você escolheu:
2^0=1
2^1=2
2^2=4
2^3=8

Se eu informar o número 5, significa que eu escolhi o 1 e o 4.
Se eu informar o número 7, significa que eu escolhi o 1, o 2 e o 4.
Se eu informar o número 4, significa que eu escolhi o 4.
etc…

Assim você descobre como tornar seu código melhor…

ok, obrigado,

você saberia me dizer pq ele não entra no if?

if (matcher.matches())

Pq o matches é quando a regex pega a linha toda (ou a string toda, caso só tenha uma linha).

Ou seja, ele basicamente engloba o seu regex entre o ^ (início de linha) e o $ (fim de linha).

Vamos supor:

String test1 = "ab cd ef gh";
Matcher m1 = Pattern.compile("[cd]").match(test1); // Pattern que procura por "c" OU "d"

// System.out.println(m1.matches()); // False pois a string começa com "a" e termina com "h"
// System.out.println(m1.find()); // True pois na string contém "c" ou "d"

while (m1.find())
   System.out.println(m1.group(1)); // primeiro printa "c" e depois printa "d"

Para que isso funcione com o matches, você precisaria pegar a linha inteira com o seu Pattern:

String test1 = "ab cd ef gh";
Matcher m1 = Pattern.compile("^[a].*?([c][d]).*$").match(test1); // Pattern que procura por "c" seguido de "d" depois de ter começado com a letra "a"

// System.out.println(m1.matches()); // True pois a string começa com "a" e no meio dela encontramos um "c" seguido de um "d"

if (m1.matches())
   System.out.println(m1.group(1)); // printa "cd"

Entendi…

Mas no meu caso, que não sei bem como começa e muito menos como termina
Dados:

Password OK

daf hardmodel
8
daf hardversion
0
daf hardtype
7
daf bootversion
1
daf appversion
81280001
param get cmid
Not found
param get devid
devid (u32) = 8012405
param get keyfobid1
keyfobid1 (u32) = 4294967048
param get keyfobid2
keyfobid2 (u32) = 11

pensei que dava para pegar um texto, selecionar a parte dele com o pattern.compile e pegar o valor depois dele… por isso pensei em criar métodos para cada item…

quero saber do hardmodel… procuro pela palavra hardmodel e pego o valor depois dele ( 8 ) e termino quando encontrar um “>”