JavaRush /Blogue Java /Random-PT /Expressões regulares em Java (RegEx)

Expressões regulares em Java (RegEx)

Publicado no grupo Random-PT
Expressões regulares são um tópico que os programadores, mesmo os mais experientes, muitas vezes adiam para mais tarde. No entanto, a maioria dos desenvolvedores Java, mais cedo ou mais tarde, terá que lidar com o processamento de texto. Na maioria das vezes - com operações de pesquisa no texto e edição. Sem expressões regulares, o código de programa produtivo e compacto associado ao processamento de texto é simplesmente impensável. Então pare de adiar, vamos lidar com os “regulares” agora. Esta não é uma tarefa tão difícil.

O que é expressão regular RegEx?

Na verdade, uma expressão regular (RegEx em Java) é um padrão para procurar uma string no texto. Em Java, a representação inicial desse padrão é sempre uma string, ou seja, um objeto da classe String. No entanto, nem qualquer string pode ser compilada em uma expressão regular, apenas aquelas que seguem as regras para escrever uma expressão regular - a sintaxe definida na especificação da linguagem. Para escrever uma expressão regular, são utilizados caracteres alfabéticos e numéricos, bem como metacaracteres - caracteres que possuem um significado especial na sintaxe das expressões regulares. Por exemplo:
String regex = "java"; // string template "java";
String regex = "\\d{3}"; // string template of three numeric characters;

Criando Expressões Regulares em Java

Para criar um RegEx em Java, você precisa seguir duas etapas simples:
  1. escreva-o como uma string usando sintaxe de expressão regular;
  2. compile esta string em uma expressão regular;
Trabalhar com expressões regulares em qualquer programa Java começa com a criação de um objeto de classe Pattern. Para fazer isso, você precisa chamar um dos dois métodos estáticos disponíveis na classe compile. O primeiro método leva um argumento - uma string literal de uma expressão regular, e o segundo - mais outro parâmetro que ativa o modo de comparação do modelo com o texto:
public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
A lista de valores de parâmetros possíveis flagsé definida na classe Patterne está disponível para nós como variáveis ​​​​de classe estática. Por exemplo:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);//searching for matches with the pattern will be done case-insensitively.
Essencialmente, a classe Patterné um construtor de expressões regulares. Nos bastidores, o método compilechama o construtor privado da classe Patternpara criar uma visualização compilada. Este método de criação de uma instância de modelo é implementado com o objetivo de criá-la como um objeto imutável. Ao criar, é realizada uma verificação de sintaxe da expressão regular. Caso haja erros na linha, é gerada uma exceção PatternSyntaxException.

Sintaxe de expressão regular

A sintaxe de expressões regulares é baseada no uso de símbolos <([{\^-=$!|]})?*+.>, que podem ser combinados com caracteres alfabéticos. Dependendo da sua função, eles podem ser divididos em vários grupos:
1. Metacaracteres para combinar limites de linha ou texto
Metacaractere Propósito
^ início da linha
$ fim da linha
\b limite da palavra
\B não é um limite de palavras
\A início da entrada
\G final da partida anterior
\Z fim da entrada
\z fim da entrada
2. Metacaracteres para procurar classes de personagens
Metacaractere Propósito
\d símbolo digital
\D caractere não numérico
\s caractere de espaço
\S caractere sem espaço em branco
\c caractere alfanumérico ou sublinhado
\C qualquer caractere que não seja alfabético, numérico ou sublinhado
. qualquer personagem
3. Metacaracteres para pesquisa de símbolos de edição de texto
Metacaractere Propósito
\t caractere de tabulação
\n caractere de nova linha
\r caractere de retorno de carro
\f ir para a nova página
\u0085 caractere da próxima linha
\u2028 caractere separador de linha
\u2029 símbolo separador de parágrafo
4. Metacaracteres para agrupar caracteres
Metacaractere Propósito
[a B C] qualquer um dos itens acima (a, b ou c)
[^abc] qualquer outro além dos listados (não a, b, c)
[a-zA-Z] mesclagem de intervalos (caracteres latinos de a a z não diferenciam maiúsculas de minúsculas)
[anúncio[mp]] concatenação de caracteres (a a d e m a p)
[az&&[def]] intersecção de símbolos (símbolos d,e,f)
[az&&[^bc]] subtraindo caracteres (caracteres a, dz)
5. Metassímbolos para indicar o número de caracteres - quantificadores. O quantificador sempre vem depois de um caractere ou grupo de caracteres.
Metacaractere Propósito
? um ou faltando
* zero ou mais vezes
+ uma ou mais vezes
{n} n vezes
{n,} n vezes ou mais
{n,m} não menos que n vezes e não mais que m vezes

Modo quantificador ganancioso

Uma característica especial dos quantificadores é a capacidade de usá-los de diferentes modos: ganancioso, superganancioso e preguiçoso. O modo extra-ganancioso é ativado adicionando o símbolo “ +” após o quantificador, e o modo preguiçoso adicionando o símbolo “ ?“. Por exemplo:
"A.+a" // greedy mode
"A.++a" // over-greedy mode
"A.+?a" // lazy mode
Usando este modelo como exemplo, vamos tentar entender como os quantificadores funcionam em diferentes modos. Por padrão, o quantificador opera no modo guloso. Isso significa que ele procura a correspondência mais longa possível na string. Como resultado da execução deste código:
public static void main(String[] args) {
    String text = "Egor Alla Alexander";
    Pattern pattern = Pattern.compile("A.+a");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(text.substring(matcher.start(), matcher.end()));
    }
}
obteremos a seguinte saída: Alla Alexa O algoritmo de busca para um determinado padrão " А.+а" é realizado na seguinte sequência:
  1. No padrão fornecido, o primeiro caractere é a letra russa А. Matchercompara-o com todos os caracteres do texto, começando na posição zero. Na posição zero do nosso texto existe um símbolo Е, então Matcherele percorre os caracteres do texto sequencialmente até encontrar uma correspondência com o padrão. No nosso exemplo, este é o símbolo na posição nº 5.

    Expressões regulares em Java - 2
  2. Depois que uma correspondência é encontrada com o primeiro caractere do padrão, Matcherele verifica a correspondência com o segundo caractere do padrão. No nosso caso, este é o símbolo “ .”, que representa qualquer caractere.

    Expressões Regulares em Java - 3

    Na sexta posição está o símbolo da letra л. Claro, corresponde ao padrão "qualquer caractere".

  3. Matcherpassa para a verificação do próximo caractere do padrão. Em nosso modelo, ele é especificado usando o .+quantificador “”. Como o número de repetições de “qualquer caractere” no padrão é uma ou mais vezes, Matcherele pega o próximo caractere da string e verifica sua conformidade com o padrão, desde que a condição “qualquer caractere” seja atendida, no nosso exemplo - até o final da linha ( da posição nº 7 - nº 18 do texto).

    Expressões Regulares em Java - 4

    Na verdade, Matcherele captura toda a linha até o fim - é aí que sua “ganância” se manifesta.

  4. Depois de Matcherchegar ao final do texto e terminar de verificar a А.+parte “ ” do padrão, o Matcher começa a verificar o resto do padrão - o caractere da letra а. Como o texto no sentido direto terminou, a verificação ocorre no sentido inverso, a partir do último caractere:

    Expressões Regulares em Java - 5
  5. Matcher"lembra" o número de repetições no padrão " .+" em que chegou ao final do texto, então reduz o número de repetições em uma e verifica o padrão do texto até encontrar uma correspondência: Expressões Regulares em Java - 6

Modo quantificador ultra-ganancioso

No modo super-ganancioso, o matcher funciona de forma semelhante ao mecanismo do modo ganancioso. A diferença é que quando você pega o texto até o final da linha, não há pesquisa para trás. Ou seja, os três primeiros estágios do modo superganancioso serão semelhantes aos do modo ganancioso. Após capturar a string inteira, o matcher adiciona o restante do padrão e o compara com a string capturada. No nosso exemplo, ao executar o método principal com o padrão " А.++а", nenhuma correspondência será encontrada. Expressões Regulares em Java - 7

Modo quantificador lento

  1. Neste modo, no estágio inicial, assim como no modo ganancioso, busca-se uma correspondência com o primeiro caractere do padrão:

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

    Expressões Regulares em Java - 9
  3. Ao contrário do modo ganancioso, o modo preguiçoso procura a correspondência mais curta no texto, portanto, após encontrar uma correspondência com o segundo caractere do padrão, que é especificado por um ponto e corresponde ao caractere na posição nº 6 do texto, ele Matcherirá verificar se o texto corresponde ao resto do padrão - o caractere “ а” .

    Expressões Regulares em Java - 10
  4. Como não foi encontrada correspondência com o padrão no texto (na posição nº 7 do texto há o símbolo “ л“), Matcherele adiciona outro “qualquer caractere” no padrão, desde que seja especificado como uma ou mais vezes, e novamente compara o padrão com o texto nas posições nº 5 a nº 8:

    Expressões Regulares em Java - 11
  5. No nosso caso, foi encontrada uma correspondência, mas o final do texto ainda não foi alcançado. Portanto, a partir da posição nº 9, a verificação começa buscando o primeiro caractere do padrão usando um algoritmo semelhante e depois se repete até o final do texto.

    Expressões Regulares em Java - 12
Como resultado do método, mainao utilizar o А.+?аtemplate " ", obteremos o seguinte resultado: Alla Alexa Como pode ser visto em nosso exemplo, ao usar diferentes modos de quantificador para o mesmo template, obtivemos resultados diferentes. Portanto, é necessário levar em consideração esta característica e selecionar o modo desejado dependendo do resultado desejado durante a busca.

Escape de caracteres em expressões regulares

Como uma expressão regular em Java, ou mais precisamente sua representação inicial, é especificada usando uma string literal, é necessário levar em consideração as regras da especificação Java que se relacionam com literais de string. Em particular, o caractere de barra invertida " \" em literais de string no código-fonte Java é interpretado como um caractere de escape que alerta o compilador de que o caractere que o segue é um caractere especial e deve ser interpretado de uma maneira especial. Por exemplo:
String s = "The root directory is \nWindows";//wrap Windows to a new line
String s = "The root directory is \u00A7Windows";//insert paragraph character before Windows
Portanto, em literais de string que descrevem uma expressão regular e usam o \caractere " " (por exemplo, para metacaracteres), ela deve ser duplicada para que o compilador de bytecode Java não a interprete de forma diferente. Por exemplo:
String regex = "\\s"; // template for searching for space characters
String regex = "\"Windows\""; // pattern to search for the string "Windows"
O caractere de barra invertida dupla também deve ser usado para escapar de caracteres especiais se planejarmos usá-los como caracteres "regulares". Por exemplo:
String regex = "How\\?"; // template for searching the string "How?"

Métodos da classe Pattern

A classe Patternpossui outros métodos para trabalhar com expressões regulares: String pattern()– retorna a representação em string original da expressão regular a partir da qual o objeto foi criado Pattern:
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 no parâmetro regex em relação ao texto passado no parâmetro input. Retorna: true – se o texto corresponder ao padrão; falso – caso contrário; Exemplo:
System.out.println(Pattern.matches("A.+a","Alla"));//true
System.out.println(Pattern.matches("A.+a","Egor Alla Alexander"));//false
int flags()– retorna os flagsvalores dos parâmetros do modelo que foram definidos quando ele foi criado ou 0 se este parâmetro não foi definido. 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 como parâmetro em um array de elementos String. O parâmetro limitdetermina o número máximo de correspondências pesquisadas no texto:
  • quando limit>0– a busca por limit-1correspondências é realizada;
  • at limit<0– procura todas as correspondências no texto
  • quando limit=0– procura todas as correspondências no texto, enquanto as linhas vazias no final do array são descartadas;
Exemplo:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    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: Egor Alla Anna -------- Egor Alla Anna Consideraremos outro método de classe para criar um objeto Matcherabaixo.

Métodos de classe correspondente

Matcheré uma classe a partir da qual um objeto é criado para procurar padrões. Matcher– este é um “motor de busca”, um “motor” de expressões regulares. Para pesquisar, ele precisa receber duas coisas: um padrão de pesquisa e um “endereço” para pesquisar. Para criar um objeto, Matchero seguinte método é fornecido na classe Pattern: рublic Matcher matcher(CharSequence input) Como argumento, o método leva uma sequência de caracteres na qual a pesquisa será realizada. Estes são objetos de classes que implementam a interface CharSequence. StringVocê pode passar não apenas , mas também , StringBuffere StringBuildercomo argumento . O modelo de pesquisa é o objeto de classe no qual o método é chamado . Exemplo de criação de um matcher: SegmentCharBufferPatternmatcher
Pattern p = Pattern.compile("a*b");// compiled the regular expression into a view
Matcher m = p.matcher("aaaaab");//created a search engine in the text “aaaaab” using the pattern "a*b"
Agora, com a ajuda do nosso “mecanismo de busca”, podemos procurar correspondências, descobrir a posição da correspondência no texto e substituir o texto usando métodos de classe. O método boolean find()procura a próxima correspondência no texto com o padrão. Usando este método e o operador de loop, você pode analisar todo o texto de acordo com o modelo de evento (realizar as operações necessárias quando ocorre um evento - encontrar uma correspondência no texto). Por exemplo, usando os métodos desta classe, int start()você int end()pode determinar as posições da correspondência no texto e, usando os métodos String replaceFirst(String replacement), String replaceAll(String replacement)substituir as correspondências no texto por outro texto de substituição. Exemplo:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    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) + " с "+ start + " By " + (end-1) + "position");
    }
    System.out.println(matcher.replaceFirst("Ira"));
    System.out.println(matcher.replaceAll("Olga"));
    System.out.println(text);
}
Saída do programa: Foi encontrada uma correspondência Alla de 5 a 8 posições Uma correspondência foi encontrada Anna de 10 a 13 posições Egor Ira Anna Egor Olga Olga Egor Alla Anna Pelo exemplo fica claro que os métodos replaceFirstcriam replaceAllum novo objeto String- uma string, que é o texto fonte no qual as correspondências com o modelo são substituídas pelo texto passado ao método como argumento. Além disso, o método replaceFirstsubstitui apenas a primeira correspondência e replaceAlltodas as correspondências no teste. O texto original permanece inalterado. O uso de outros métodos de classe Matcher, bem como exemplos de expressões regulares, pode ser encontrado nesta série de artigos . As operações mais comuns com expressões regulares ao trabalhar com texto são de classes Patterne Matchersão incorporadas ao arquivo String. Esses são métodos como split, matches, replaceFirst, replaceAll. Mas, na verdade, "sob o capô" eles usam o Patterne Matcher. Portanto, se você precisar substituir texto ou comparar strings em um programa sem escrever código desnecessário, use os métodos do String. Se você precisar de recursos avançados, pense em classes Patterne Matcher.

Conclusão

Uma expressão regular é descrita em um programa Java usando strings que correspondem a um padrão definido pelas regras. Quando o código é executado, Java recompila essa string em um objeto de classe Patterne usa o objeto de classe Matcherpara encontrar correspondências no texto. Como falei no início, muitas vezes as expressões regulares são deixadas de lado para depois, por serem consideradas um tema difícil. No entanto, se você entender o básico de sintaxe, metacaracteres, escape e estudar exemplos de expressões regulares, eles serão muito mais simples do que parecem à primeira vista.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION