O que é uma expressão regular (regex)?
Na verdade, uma expressão regular é um padrão para encontrar uma string no texto. Em Java, a representação original desse padrão é sempre uma string, ou seja, um objeto daString
classe. No entanto, não é qualquer string que pode ser compilada em uma expressão regular — apenas strings que obedecem às regras de criação de expressões regulares. A sintaxe é definida na especificação da linguagem. As expressões regulares são escritas usando letras e números, bem como metacaracteres, que são caracteres que têm um significado especial na sintaxe da expressão regular. Por exemplo:
String regex = "java"; // The pattern is "java";
String regex = "\\d{3}"; // The pattern is three digits;
Criando expressões regulares em Java
A criação de uma expressão regular em Java envolve duas etapas simples:- escrevê-lo como uma string que esteja em conformidade com a sintaxe de expressão regular;
- compilar a string em uma expressão regular;
Pattern
objeto. Para fazer isso, precisamos chamar um dos dois métodos estáticos da classe: compile
. O primeiro método usa um argumento — uma string literal contendo a expressão regular, enquanto o segundo usa um argumento adicional que determina as configurações de correspondência de padrão:
public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
A lista de valores potenciais do flags
parâmetro é definida na Pattern
classe e está disponível para nós como variáveis de classe estáticas. Por exemplo:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE); // Pattern-matching will be case insensitive.
Basicamente, a Pattern
classe é um construtor de expressões regulares. Nos bastidores, o compile
método chama o Pattern
construtor privado da classe para criar uma representação compilada. Esse mecanismo de criação de objetos é implementado dessa maneira para criar objetos imutáveis. Quando uma expressão regular é criada, sua sintaxe é verificada. Se a string contiver erros, um PatternSyntaxException
será gerado.
Sintaxe de expressão regular
A sintaxe da expressão regular depende dos<([{\^-=$!|]})?*+.>
caracteres, que podem ser combinados com letras. Dependendo de sua função, eles podem ser divididos em vários grupos:
Metacaractere | Descrição |
---|---|
^ | início de uma linha |
$ | fim de uma linha |
\b | limite de palavras |
\B | fronteira não-palavra |
\A | início da entrada |
\G | final da partida anterior |
\Z | fim da entrada |
\z | fim da entrada |
Metacaractere | Descrição |
---|---|
\d | dígito |
\D | não dígito |
\s | caractere de espaço em branco |
\S | caractere sem espaço em branco |
\c | caractere alfanumérico ou sublinhado |
\C | qualquer caractere, exceto letras, números e sublinhado |
. | qualquer personagem |
Metacaractere | Descrição |
---|---|
\t | caractere de tabulação |
\n | caractere de nova linha |
\r | retorno de carruagem |
\f | caractere de avanço de linha |
\u0085 | caractere da próxima linha |
\u2028 | separador de linha |
\u2029 | separador de parágrafo |
Metacaractere | Descrição |
---|---|
[abc] | qualquer um dos caracteres listados (a, b ou c) |
[^abc] | qualquer caractere diferente dos listados (não a, b ou c) |
[a-zA-Z] | intervalos mesclados (caracteres latinos de a a z, sem distinção entre maiúsculas e minúsculas) |
[anúncio[mp]] | união de caracteres (de a a d e de m a p) |
[az&&[def]] | interseção de caracteres (d, e, f) |
[az&&[^bc]] | subtração de caracteres (a, dz) |
Metacaractere | Descrição |
---|---|
? | um ou nenhum |
* | zero ou mais vezes |
+ | uma ou mais vezes |
{n} | n vezes |
{n,} | n ou mais vezes |
{n,m} | pelo menos n vezes e não mais que m vezes |
quantificadores gananciosos
Uma coisa que você deve saber sobre quantificadores é que eles vêm em três variedades diferentes: gananciosos, possessivos e relutantes. Você torna um quantificador possessivo adicionando um+
caractere " " após o quantificador. Você o torna relutante adicionando " ?
". Por exemplo:
"A.+a" // greedy
"A.++a" // possessive
"A.+?a" // reluctant
Vamos tentar usar esse padrão para entender como funcionam os diferentes tipos de quantificadores. Por padrão, os quantificadores são gananciosos. Isso significa que eles procuram a correspondência mais longa na string. Se executarmos o seguinte código:
public static void main(String[] args) {
String text = "Fred Anna Alexander";
Pattern pattern = Pattern.compile("A.+a");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(text.substring(matcher.start(), matcher.end()));
}
}
obtemos esta saída:
Anna Alexa
Para a expressão regular " A.+a
", a correspondência de padrões é executada da seguinte forma:
-
O primeiro caractere no padrão especificado é a letra latina
A
.Matcher
compara-o com cada caractere do texto, começando do índice zero. O caractereF
está no índice zero em nosso texto, portantoMatcher
itera pelos caracteres até que corresponda ao padrão. Em nosso exemplo, esse caractere é encontrado no índice 5. -
Uma vez encontrada uma correspondência com o primeiro caractere do padrão,
Matcher
procura uma correspondência com seu segundo caractere. No nosso caso, é o.
caractere " ", que representa qualquer caractere.O personagem
n
está na sexta posição. Certamente se qualifica como uma correspondência para "qualquer personagem". -
Matcher
prossegue para verificar o próximo caractere do padrão. Em nosso padrão, ele está incluído no quantificador que se aplica ao caractere precedente: ".+
". Como o número de repetições de "qualquer caractere" em nosso padrão é uma ou mais vezes,Matcher
repetidamente pega o próximo caractere da string e o compara com o padrão, desde que corresponda a "qualquer caractere". Em nosso exemplo — até o final da string (do índice 7 ao índice 18).Basicamente,
Matcher
engole a string até o fim - é exatamente isso que significa "ganancioso". -
Após o Matcher atingir o final do texto e concluir a verificação da
A.+
parte " " do padrão, ele começa a verificar o restante do padrão:a
. Não há mais texto avançando, então a verificação prossegue "recuando", começando pelo último caractere: -
Matcher
"lembra" o número de repetições na.+
parte " " do padrão. Nesse ponto, ele reduz o número de repetições em um e verifica o padrão maior no texto até encontrar uma correspondência:
quantificadores possessivos
Os quantificadores possessivos são muito parecidos com os gulosos. A diferença é que quando o texto foi capturado até o final da string, não há correspondência de padrão durante o "recuo". Em outras palavras, os três primeiros estágios são os mesmos dos quantificadores gulosos. Depois de capturar a string inteira, o matcher adiciona o restante do padrão ao que está considerando e o compara com a string capturada. Em nosso exemplo, usando a expressão regular "A.++a
", o método main não encontra nenhuma correspondência.
quantificadores relutantes
-
Para esses quantificadores, assim como para a variedade gananciosa, o código procura uma correspondência com base no primeiro caractere do padrão:
-
Em seguida, ele procura uma correspondência com o próximo caractere do padrão (qualquer caractere):
-
Ao contrário da correspondência de padrão gulosa, a correspondência mais curta é procurada na correspondência de padrão relutante. Isso significa que após encontrar uma correspondência com o segundo caractere do padrão (um ponto, que corresponde ao caractere na posição 6 no texto,
Matcher
verifica se o texto corresponde ao restante do padrão — o caractere "a
" -
O texto não corresponde ao padrão (ou seja, contém o caractere "
n
" no índice 7), entãoMatcher
adiciona mais um "qualquer caractere", porque o quantificador indica um ou mais. Em seguida, compara novamente o padrão com o texto nas posições 5 a 8:
No nosso caso, foi encontrada uma correspondência, mas ainda não chegamos ao final do texto. Portanto, o pattern-matching recomeça a partir da posição 9, ou seja, o primeiro caractere do pattern é procurado por meio de um algoritmo semelhante e isso se repete até o final do texto.
main
método obtém o seguinte resultado ao usar o padrão " A.+?a
": Anna Alexa Como você pode ver em nosso exemplo, diferentes tipos de quantificadores produzem resultados diferentes para o mesmo padrão. Portanto, tenha isso em mente e escolha a variedade certa com base no que você está procurando.
Caracteres de escape em expressões regulares
Como uma expressão regular em Java, ou melhor, sua representação original, é uma string literal, precisamos levar em consideração as regras de Java relacionadas a strings literais. Em particular, o caractere de barra invertida "\
" em strings literais no código-fonte Java é interpretado como um caractere de controle que informa ao compilador que o próximo caractere é especial e deve ser interpretado de maneira especial. Por exemplo:
String s = "The root directory is \nWindows"; // Move "Windows" to a new line
String s = "The root directory is \u00A7Windows"; // Insert a paragraph symbol before "Windows"
Isso significa que strings literais que descrevem expressões regulares e usam \
caracteres " " (ou seja, para indicar metacaracteres) devem repetir as barras invertidas para garantir que o compilador de bytecode Java não interprete mal a string. Por exemplo:
String regex = "\\s"; // Pattern for matching a whitespace character
String regex = "\"Windows\""; // Pattern for matching "Windows"
Barras invertidas duplas também devem ser usadas para escapar de caracteres especiais que queremos usar como caracteres "normais". Por exemplo:
String regex = "How\\?"; // Pattern for matching "How?"
Métodos da classe Pattern
APattern
classe tem outros métodos para trabalhar com expressões regulares:
-
String pattern()
‒ retorna a representação de string original da expressão regular usada para criar oPattern
objeto:Pattern pattern = Pattern.compile("abc"); System.out.println(pattern.pattern()); // "abc"
-
static boolean matches(String regex, CharSequence input)
– permite verificar a expressão regular passada como regex em relação ao texto passado comoinput
. Retorna:true – se o texto corresponder ao padrão;
falso – se não;Por exemplo:
System.out.println(Pattern.matches("A.+a","Anna")); // true System.out.println(Pattern.matches("A.+a","Fred Anna Alexander")); // false
-
int flags()
‒ retorna o valor doflags
parâmetro do padrão definido quando o padrão foi criado ou 0 se o parâmetro não foi definido. Por exemplo:Pattern pattern = Pattern.compile("abc"); System.out.println(pattern.flags()); // 0 Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE); System.out.println(pattern.flags()); // 2
-
String[] split(CharSequence text, int limit)
– divide o texto passado em umaString
matriz. Olimit
parâmetro indica o número máximo de correspondências pesquisadas no texto:- se
limit > 0
‒limit-1
corresponder; - if
limit < 0
‒ todas as correspondências no texto - se
limit = 0
‒ todas as correspondências no texto, strings vazias no final do array são descartadas;
Por exemplo:
public static void main(String[] args) { String text = "Fred Anna Alexa"; Pattern pattern = Pattern.compile("\\s"); String[] strings = pattern.split(text,2); for (String s : strings) { System.out.println(s); } System.out.println("---------"); String[] strings1 = pattern.split(text); for (String s : strings1) { System.out.println(s); } }
Saída do console:
Fred Anna Alexa --------- Fred Anna Alexa
A seguir, consideraremos outro método da classe usado para criar um
Matcher
objeto. - se
Métodos da classe Matcher
As instâncias daMatcher
classe são criadas para realizar a correspondência de padrões. Matcher
é o "mecanismo de busca" para expressões regulares. Para realizar uma pesquisa, precisamos fornecer duas coisas: um padrão e um índice inicial. Para criar um Matcher
objeto, a Pattern
classe fornece o seguinte método: рublic Matcher matcher(CharSequence input)
O método recebe uma sequência de caracteres, que será pesquisada. Esta é uma instância de uma classe que implementa a CharSequence
interface. Você pode passar não apenas a String
, mas também a StringBuffer
, StringBuilder
, Segment
ou CharBuffer
. O padrão é um Pattern
objeto no qual o matcher
método é chamado. Exemplo de criação de um correspondente:
Pattern p = Pattern.compile("a*b"); // Create a compiled representation of the regular expression
Matcher m = p.matcher("aaaaab"); // Create a "search engine" to search the text "aaaaab" for the pattern "a*b"
Agora podemos usar nosso "mecanismo de busca" para procurar correspondências, obter a posição de uma correspondência no texto e substituir o texto usando os métodos da classe. O boolean find()
método procura a próxima correspondência no texto. Podemos usar esse método e uma instrução de loop para analisar um texto inteiro como parte de um modelo de evento. Em outras palavras, podemos realizar as operações necessárias quando ocorre um evento, ou seja, quando encontramos uma correspondência no texto. Por exemplo, podemos usar os métodos int start()
e dessa classe int end()
para determinar a posição de uma correspondência no texto. E podemos usar os métodos String replaceFirst(String replacement)
e String replaceAll(String replacement)
para substituir correspondências pelo valor do parâmetro de substituição. Por exemplo:
public static void main(String[] args) {
String text = "Fred Anna Alexa";
Pattern pattern = Pattern.compile("A.+?a");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
int start=matcher.start();
int end=matcher.end();
System.out.println("Match found: " + text.substring(start, end) + " from index "+ start + " through " + (end-1));
}
System.out.println(matcher.replaceFirst("Ira"));
System.out.println(matcher.replaceAll("Mary"));
System.out.println(text);
}
Saída:
Match found: Anna from index 5 through 8
Match found: Alexa from index 10 through 14
Fred Ira Alexa
Fred Mary Mary
Fred Anna Alexa
O exemplo deixa claro que os métodos replaceFirst
e replaceAll
criam um novo String
objeto — uma string na qual correspondências de padrão no texto original são substituídas pelo texto passado para o método como um argumento. Além disso, o replaceFirst
método substitui apenas a primeira correspondência, mas o replaceAll
método substitui todas as correspondências no texto. O texto original permanece inalterado. As operações regex mais frequentes das classes Pattern
e Matcher
são construídas diretamente na String
classe. Esses são métodos como split
, matches
, replaceFirst
e replaceAll
. Mas sob o capô, esses métodos usam as classes Pattern
e Matcher
. Portanto, se você deseja substituir texto ou comparar strings em um programa sem escrever nenhum código extra, use os métodos doString
aula. Se você precisar de recursos mais avançados, lembre-se das classes Pattern
e Matcher
.
Conclusão
Em um programa Java, uma expressão regular é definida por uma cadeia de caracteres que obedece a regras específicas de correspondência de padrões. Ao executar o código, a máquina Java compila essa string em umPattern
objeto e usa um Matcher
objeto para localizar correspondências no texto. Como eu disse no início, as pessoas costumam deixar as expressões regulares para depois, por considerá-las um assunto difícil. Mas se você entender a sintaxe básica, os metacaracteres e o escape de caracteres e estudar exemplos de expressões regulares, descobrirá que eles são muito mais simples do que parecem à primeira vista.
Mais leitura: |
---|
GO TO FULL VERSION