CodeGym /Blogue Java /Random-PT /Expressões regulares em Java
John Squirrels
Nível 41
San Francisco

Expressões regulares em Java

Publicado no grupo Random-PT
Expressões regulares é um assunto que os programadores, mesmo experientes, costumam deixar para depois. Mas, mais cedo ou mais tarde, a maioria dos desenvolvedores Java precisa processar informações textuais. Na maioria das vezes, isso significa pesquisar e editar texto. Sem expressões regulares, código de processamento de texto eficaz e compacto é simplesmente impensável. Então pare de procrastinar, vamos lidar com expressões regulares agora mesmo. Não é tão difícil. Expressões regulares em Java - 1

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 da Stringclasse. 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:
  1. escrevê-lo como uma string que esteja em conformidade com a sintaxe de expressão regular;
  2. compilar a string em uma expressão regular;
Em qualquer programa Java, começamos a trabalhar com expressões regulares criando um Patternobjeto. 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 flagsparâmetro é definida na Patternclasse 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 Patternclasse é um construtor de expressões regulares. Nos bastidores, o compilemétodo chama o Patternconstrutor 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 PatternSyntaxExceptionserá 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:
1. Metacaracteres para corresponder aos limites de linhas ou texto
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
2. Metacaracteres para correspondência de classes de caracteres predefinidas
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
3. Metacaracteres para correspondência de caracteres de controle
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
4. Metacaracteres para classes de caracteres
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)
5. Metacaracteres para indicar o número de caracteres (quantificadores). Um quantificador é sempre precedido por um caractere ou grupo de caracteres.
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:
  1. O primeiro caractere no padrão especificado é a letra latina A. Matchercompara-o com cada caractere do texto, começando do índice zero. O caractere Festá no índice zero em nosso texto, portanto Matcheritera pelos caracteres até que corresponda ao padrão. Em nosso exemplo, esse caractere é encontrado no índice 5.

    Expressões regulares em Java - 2
  2. Uma vez encontrada uma correspondência com o primeiro caractere do padrão, Matcherprocura uma correspondência com seu segundo caractere. No nosso caso, é o .caractere " ", que representa qualquer caractere.

    Expressões regulares em Java - 3

    O personagem nestá na sexta posição. Certamente se qualifica como uma correspondência para "qualquer personagem".

  3. Matcherprossegue 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, Matcherrepetidamente 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).

    Expressões regulares em Java - 4

    Basicamente, Matcherengole a string até o fim - é exatamente isso que significa "ganancioso".

  4. 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:

    Expressões regulares em Java - 5
  5. 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:

    Expressões regulares em Java - 6

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. Expressões regulares em Java - 7

quantificadores relutantes

  1. Para esses quantificadores, assim como para a variedade gananciosa, o código procura uma correspondência com base no primeiro caractere do padrão:

    Expressões regulares em Java - 8
  2. Em seguida, ele procura uma correspondência com o próximo caractere do padrão (qualquer caractere):

    Expressões regulares em Java - 9
  3. 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, Matcherverifica se o texto corresponde ao restante do padrão — o caractere " a"

    Expressões regulares em Java - 10
  4. O texto não corresponde ao padrão (ou seja, contém o caractere " n" no índice 7), então Matcheradiciona 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:

    Expressões regulares em Java - 11
  5. 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.

    Expressões regulares em Java - 12
Dessa forma, o mainmé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

A Patternclasse 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 o Patternobjeto:

    
    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 como input. 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 do flagsparâ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 uma Stringmatriz. O limitparâmetro indica o número máximo de correspondências pesquisadas no texto:

    • se limit > 0limit-1corresponder;
    • 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 Matcherobjeto.

Métodos da classe Matcher

As instâncias da Matcherclasse 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 Matcherobjeto, a Patternclasse 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 CharSequenceinterface. Você pode passar não apenas a String, mas também a StringBuffer, StringBuilder, Segmentou CharBuffer. O padrão é um Patternobjeto no qual o matchermé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 replaceFirste replaceAllcriam um novo Stringobjeto — 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 replaceFirstmétodo substitui apenas a primeira correspondência, mas o replaceAllmétodo substitui todas as correspondências no texto. O texto original permanece inalterado. As operações regex mais frequentes das classes Patterne Matchersão construídas diretamente na Stringclasse. Esses são métodos como split, matches, replaceFirste replaceAll. Mas sob o capô, esses métodos usam as classes Patterne Matcher. Portanto, se você deseja substituir texto ou comparar strings em um programa sem escrever nenhum código extra, use os métodos doStringaula. Se você precisar de recursos mais avançados, lembre-se das classes Patterne 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 um Patternobjeto e usa um Matcherobjeto 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:

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION