JavaRush /Blogue Java /Random-PT /RegEx: 20 passos curtos para dominar expressões regulares...
Artur
Nível 40
Tallinn

RegEx: 20 passos curtos para dominar expressões regulares. Parte 1

Publicado no grupo Random-PT
O original deste artigo está aqui . Provavelmente não existe teoria demais, e fornecerei vários links para material mais detalhado sobre regex no final do artigo. Mas me pareceu que começar a se aprofundar em um tema como expressões regulares seria muito mais interessante se houvesse a oportunidade não apenas de estudar, mas também de consolidar imediatamente o conhecimento, completando pequenas tarefas ao longo do caminho. RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 1Vamos começar. Normalmente, os oponentes do uso de expressões regulares ('RegEx' ou simplesmente 'regex') em programação citam a seguinte citação, atribuída a Jamie Zawinski: "Algumas pessoas, quando confrontadas com um problema, pensam: 'Eu sei, vou usar expressões regulares .'" Agora eles tem dois problemas". Na verdade, usar expressões regulares ainda não é uma boa ou má ideia. E isso por si só não acrescentará problemas e não resolverá nenhum deles. É apenas uma ferramenta. E como você o usa (certo ou errado) determina quais resultados você verá. Se você tentar usar regex, por exemplo, para criar um analisador HTML, provavelmente sentirá problemas . Mas se você quiser apenas extrair, por exemplo, carimbos de data/hora de algumas strings, provavelmente não terá problema. Para facilitar o domínio das expressões regulares, elaborei esta lição que o ajudará a dominar as expressões regulares do zero em apenas vinte passos curtos. Este tutorial concentra-se principalmente nos conceitos básicos de expressões regulares e se aprofunda em tópicos mais avançados apenas quando necessário.

Etapa 1: por que usar expressões regulares

RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 2Expressões regulares são usadas para procurar correspondências no texto usando padrões (padrões) especificados. Usando regex, podemos extrair palavras do texto de maneira fácil e simples, bem como caracteres literais e metacaracteres individuais e suas sequências que atendem a determinados critérios. Aqui está o que a Wikipedia nos diz sobre eles : Expressões regulares são uma linguagem formal para pesquisar e manipular substrings em texto, com base no uso de metacaracteres (caracteres curinga). Para a pesquisa, é usada uma string de amostra (padrão em inglês, em russo é frequentemente chamado de “modelo”, “máscara”), consistindo de símbolos e meta-símbolos e definindo a regra de pesquisa. Para manipular o texto, uma string de substituição é especificada adicionalmente, que também pode conter caracteres especiais. O padrão pode ser tão simples quanto a palavra dognesta frase:
A rápida raposa marrom salta sobre o cachorro preguiçoso.
Esta expressão regular se parece com isto:
cachorro
...Fácil, não é? O padrão também pode ser qualquer palavra que contenha a letra o. Uma expressão regular para encontrar esse padrão pode ser assim:
\ uau * _
( Você pode tentar esta expressão regular aqui .) Você notará que à medida que os requisitos de “correspondência” se tornam mais complexos, a expressão regular também se torna mais complexa. Existem formas adicionais de notação para especificar grupos de caracteres e combinar padrões repetidos, que explicarei abaixo. Mas, assim que encontramos uma correspondência com um padrão em algum texto, o que podemos fazer com isso? Os mecanismos modernos de expressões regulares permitem extrair caracteres ou sequências de caracteres (substrings) do texto contido, ou removê-los ou substituí-los por outro texto. Em geral, expressões regulares são usadas para analisar e manipular texto. Podemos extrair, por exemplo, substrings que se parecem com endereços IP e depois tentar verificá-las. Ou podemos extrair nomes e endereços de email e armazená-los em um banco de dados. Ou use expressões regulares para encontrar informações confidenciais (como números de passaporte ou números de telefone) em e-mails e alertar o usuário de que ele pode estar se colocando em risco. Regex é realmente uma ferramenta versátil, fácil de aprender, mas difícil de dominar: “Assim como há uma diferença entre tocar bem uma peça musical e criar música, há uma diferença entre conhecer expressões regulares e compreendê-las”. - Jeffrey E. F. Friedl, Dominando Expressões Regulares

Etapa 2: colchetes[]

As expressões regulares mais simples e fáceis de entender são aquelas que simplesmente procuram uma correspondência caractere por caractere entre o padrão da expressão regular e a string de destino. Vamos, por exemplo, tentar encontrar um gato: RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 3
padrão: gato
string: O gato foi cortado quando correu para baixo do carro.
correspondências:      ^^^
( Como funciona na prática - veja aqui ) Atenção! Todas as soluções são apresentadas aqui apenas como soluções possíveis. Nas expressões regulares, como na programação em geral, é possível resolver os mesmos problemas de maneiras diferentes. No entanto, além de uma comparação estrita caractere por caractere, também podemos especificar correspondências alternativas usando colchetes:
padrão: ca[rt]
string: O gato foi cortado quando correu para baixo do carro.
correspondências:      ^^^ ^^^
( Como funciona ) Abrir e fechar colchetes informa ao mecanismo de expressão regular que ele deve corresponder a qualquer um dos caracteres especificados, mas apenas a um. A expressão regular acima não encontrará, por exemplo, a cartpalavra inteira, mas encontrará apenas parte dela:
padrão: ca[rt]
string: O gato foi cortado quando correu para baixo da carroça.
correspondências:      ^^^ ^^^
( Como funciona ) Ao usar colchetes, você diz ao mecanismo de expressão regular para corresponder apenas a um dos caracteres contidos entre colchetes. O mecanismo encontra o caractere c, depois o caractere a, mas se o próximo caractere não for rou t, então esta não é uma correspondência completa. Se encontrar ca, e então r, ou t, ele para. Ele não tentará corresponder mais caracteres porque os colchetes indicam que apenas um dos caracteres contidos precisa ser correspondido. Quando encontra ca, ele encontra ra palavra next carte para porque já encontrou uma correspondência para a sequência car.

Objetivos de Treinamento:

Escreva uma expressão regular que corresponda a todos os 10 padrões hadneste Hadtrecho de trocadilhos intraduzíveis no dialeto local:
padrão:
string: Jim, onde Bill tinha "tinha" , tinha "tinha tido" . "Tinha tido" estava correto.
correspondências:                  ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^
( Veja a solução possível aqui ) E quanto a todos os nomes de animais na frase a seguir?
padrão:
string: Um morcego, um gato e um rato entraram em um bar...
correspondências:    ^^^ ^^^ ^^^
( Solução possível ) Ou ainda mais simples: encontre as palavras barou bat:
padrão:
string: Um morcego, um gato e um rato entraram em um bar...
correspondências:    ^^^ ^^^
( Solução possível ) Agora já aprendemos como escrever expressões regulares mais ou menos complexas e estamos apenas no passo 2! Vamos continuar!

Etapa 3: sequências de escape

RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 4Na etapa anterior, aprendemos sobre colchetes []e como eles nos ajudam a encontrar correspondências alternativas usando o mecanismo regex. Mas e se quisermos encontrar correspondências na forma de colchetes abertos e fechados []? Quando queríamos encontrar uma correspondência caractere por caractere da palavra cat, fornecemos ao mecanismo regex esta sequência de caracteres ( cat). Vamos tentar encontrar colchetes []da mesma maneira:
padrão: [] 
string: Você não pode combinar [] usando regex! Você vai se arrepender disso!
partidas: 
( Vamos ver o que aconteceu ) Algo não funcionou, entretanto... Isso ocorre porque os caracteres de colchetes agem como caracteres especiais de mecanismo regex que geralmente são usados ​​para indicar outra coisa e não são um padrão literal para combiná-los. Como lembramos da etapa 2, eles são usados ​​para encontrar correspondências alternativas para que o mecanismo regex possa corresponder a qualquer um dos caracteres contidos entre eles. Se você não colocar nenhum caractere entre eles, poderá causar um erro. Para corresponder a esses caracteres especiais, devemos escapá-los precedendo-os com uma barra invertida \. Barra invertida (ou barra invertida) é outro caractere especial que diz ao mecanismo regex para procurar o próximo caractere literalmente, em vez de usá-lo como um metacaractere. O mecanismo regex procurará apenas caracteres [e ]literalmente se ambos forem precedidos por uma barra invertida:
padrão: \[\]
string: Você não pode combinar [] usando regex! Você vai se arrepender disso!
correspondências:                  ^^ 
( Vamos ver o que aconteceu desta vez ) OK, e se quisermos encontrar a própria barra invertida? A resposta é simples. Como a barra invertida \também é um caractere especial, ela também precisa ser escapada. Como? Barra invertida!
padrão: \\
string: C:\Users\Tanja\Pictures\Dogs
correspondências:    ^ ^ ^ ^
( Mesmo exemplo na prática ) Apenas caracteres especiais devem ser precedidos por uma barra invertida. Todos os outros caracteres são interpretados literalmente por padrão. Por exemplo, a expressão regular tcorresponde literalmente apenas ta letras minúsculas:
padrão: t
string: tttt
correspondências: ^ ^ ^ ^
( Exemplo ) No entanto, esta sequência \tfunciona de forma diferente. É um modelo para procurar um caractere de tabulação:
padrão: \t
string: tttt
correspondências:   ^ ^ ^
( Exemplo ) Algumas sequências de escape comuns incluem \n(quebras de linha no estilo UNIX) e \r(usado em quebras de linha no estilo Windows \r\n). \ré um caractere de "retorno de carro" e \num caractere de "alimentação de linha", ambos definidos junto com o padrão ASCII quando as máquinas de teletipo ainda eram amplamente utilizadas. Outras sequências de escape comuns serão abordadas posteriormente neste tutorial.

Enquanto isso, vamos consolidar o material com alguns quebra-cabeças simples:

Tente escrever uma expressão regular para encontrar... uma expressão regular ;) O resultado deve ser algo assim:
padrão:
string: ...combinar esta regex ` \[\] ` com uma regex?
correspondências:                       ^^^^	
( Solução ) Você conseguiu? Bom trabalho! Agora tente criar uma regex para procurar sequências de escape como esta:
padrão:
string: ` \r` , ` \t` e ` \n` são todas sequências de escape regex.
correspondências:   ^^ ^^ ^^
( Solução )

Passo 4: procure por “qualquer” caractere usando um ponto.

RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 5Ao escrever as soluções de correspondência de sequência de escape que vimos na etapa anterior, você deve ter se perguntado: "Posso combinar o caractere de barra invertida e depois qualquer outro caractere que o segue?"... Claro que você pode! Há outro caractere especial que é usado para corresponder a (quase) qualquer caractere - o caractere ponto (ponto final). Aqui está o que ele faz:
padrão: .
string: Sinto muito, Dave. Receio não poder fazer isso.
correspondências: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^	
( Exemplo ) Se você deseja apenas combinar padrões que se parecem com sequências de escape, você pode fazer algo assim:
padrão: \\. 
string: Olá Walmart, meu neto, o nome dele é " \n\r\t ".
correspondências:                                              ^^ ^^ ^^	
( Exemplo ) E, como acontece com todos os caracteres especiais, se você quiser corresponder a um literal ., será necessário precedê-lo com um caractere \:
padrão: \. 
string: Guerra é Paz . Liberdade é escravidão . Ignorância é força . 
correspondências:             ^ ^ ^
( Exemplo )

Etapa 5: intervalos de caracteres

RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 6E se você não precisar de nenhum símbolo, mas quiser apenas encontrar letras no texto? Ou números? Ou vogais? A pesquisa por classes de personagens e seus intervalos nos permitirá conseguir isso.
` \n` , ` \r` e ` \t` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .	
Os caracteres são “espaços em branco” se não criarem uma marca visível no texto. Um espaço " " é um espaço, quebra de linha ou tabulação. Digamos que queremos encontrar sequências de escape que representem apenas caracteres de espaço em branco e \nna passagem acima, mas não outras sequências de escape. Como poderíamos fazer isso? \r\t
padrão: \\[nrt] 
string: ` \n `, ` \r ` e ` \t ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:   ^^ ^^ ^^	
( Exemplo ) Isso funciona, mas não é uma solução muito elegante. E se mais tarde precisarmos combinar a sequência de escape do caractere "feed de formulário", \f? (Este símbolo é usado para indicar quebras de página no texto.)
padrão: \\[nrt] 
string: ` \n `, ` \r `, ` \t ` e ` \f ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:   ^^ ^^ ^^	
( Solução não funcional ) Com esta abordagem, precisamos listar separadamente cada letra minúscula que queremos corresponder, entre colchetes. Uma maneira mais fácil de fazer isso é usar intervalos de caracteres para corresponder a qualquer letra minúscula:
padrão: \\[az] 
string: ` \n `, ` \r `, ` \t ` e ` \f ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:   ^^ ^^ ^^ ^^	
( E isso já funciona ) Os intervalos de caracteres funcionam como você poderia esperar, dado o exemplo acima. Coloque colchetes ao redor da primeira e da última letra que deseja combinar, com um hífen entre eles. Por exemplo, se você quiser encontrar apenas "conjuntos" de barra invertida \e uma letra de ato m, poderá fazer o seguinte:
padrão: \\[am] 
string: ` \n `, ` \r `, ` \t ` e ` \f ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:                         ^^	
( Exemplo ) Se você deseja combinar vários intervalos, basta colocá-los de ponta a ponta entre colchetes:
padrão: \\[a-gq-z] 
string: ` \n `, ` \r `, ` \t ` e ` \f ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:         ^^ ^^ ^^	
( Exemplo ) Outros intervalos de caracteres comuns incluem: A-Ze0-9

Vamos experimentá-los na prática e resolver alguns problemas:

Os números hexadecimais podem conter dígitos 0-9e também letras A-F. Quando usados ​​para especificar cores, os códigos hexadecimais podem conter no máximo três caracteres. Crie uma expressão regular para encontrar códigos hexadecimais válidos na lista abaixo:
padrão:
sequência: 1H8 4E2 8FF 0P1 T8B 776 42B G12
correspondências:      ^^^ ^^^ ^^^ ^^^	
( Solução ) Usando intervalos de caracteres, crie uma expressão regular que selecionará apenas consoantes minúsculas (não vogais, incluindo y) na frase abaixo:
padrão:
string : As paredes do shopping são totalmente , totalmente altas . _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
correspondências:   ^ ^ ^^^ ^ ^^ ^ ^^ ^ ^ ^ ^^^ ^ ^ ^^^ ^ ^^	
( Solução )

Etapa 6: símbolo "não", circunflexo, circunflexo, circunflexo...^

RegEx: 20 passos curtos para dominar expressões regulares.  Parte 1 - 7Na verdade, existem mais de 9.000 nomes para este símbolo :) Mas, para simplificar, talvez nos concentremos no “não”. Minha solução para o último problema é um pouco longa. Foram necessários 17 caracteres para dizer “obtenha o alfabeto inteiro, exceto as vogais”. Claro que existe uma maneira mais fácil de fazer isso. O sinal “não” ^nos permite especificar caracteres e intervalos de caracteres que não devem corresponder aos especificados no padrão. Uma solução mais simples para o último problema acima é encontrar caracteres que não representem vogais:
padrão : [ ^ aeiou ] string 
:   As paredes do shopping são totalmente , totalmente altas . _ _ _ _ _ _ _ _ _ _ _ 
correspondências: ^^ ^^ ^^^^ ^^^^ ^^ ^^^ ^ ^^ ^ ^^^^^^ ^ ^^^^^ ^^^ 	
( Exemplo ) O sinal "não" ^como o caractere mais à esquerda entre colchetes []informa ao mecanismo de expressão regular para corresponder a um (qualquer) caractere que não esteja entre colchetes. Isso significa que a expressão regular acima também corresponde a todos os espaços, pontos ., vírgulas ,e maiúsculas Tno início de uma frase. Para excluí-los, também podemos colocá-los entre colchetes:
padrão : [ ^ aeiou . , T ] string  
: As paredes nele são totalmente , totalmente altas . _ _ _ _ _ _ _ _ _ _ _ _ 
correspondências:   ^ ^ ^^^ ^ ^^ ^ ^^ ^ ^ ^ ^^^ ^ ^ ^^^ ^ ^^	
( Exemplo ) observaçãoque, neste caso, não precisamos escapar do ponto final com barra invertida, como fizemos antes, quando o procuramos sem usar colchetes. Muitos caracteres especiais entre colchetes são tratados literalmente, incluindo o caractere de colchete de abertura [- mas não de fechamento ](você consegue adivinhar por quê?). O caractere de barra invertida \também não é interpretado literalmente. Se quiser corresponder uma barra invertida literal \usando colchetes, você deverá escapar dela precedendo-a com a seguinte barra invertida \\. Este comportamento foi projetado para que os caracteres de espaço em branco também pudessem ser colocados entre colchetes para correspondência:
padrão: [t]
string: tttt
correspondências:   ^ ^ ^
( Exemplo ) O sinal "não" ^também pode ser usado com intervalos. Se eu quisesse capturar apenas os caracteres a, b, c, xe y, zpoderia fazer algo assim:
padrão: [abcxyz] 
string:   abc defghijklmnopqrstuvw xyz 
corresponde: ^^^ ^^^
( Exemplo ) ... ou eu poderia especificar que desejo encontrar qualquer caractere que não esteja entre de w:
padrão: [^dw] 
string:   abc defghijklmnopqrstuvw xyz 
corresponde: ^^^ ^^^
( Exemplo ) No entanto,tome cuidadoCom não" ^. É fácil pensar "bem, eu especifiquei [^ b-f], então devo receber uma letra minúscula aou algo assim depois f. Esse não é o caso. Este regex corresponderá a qualquer caractere que não esteja nesse intervalo, incluindo letras, números, pontuação e espaços.
padrão: [^dw] 
string:   abc defg h . i , j - klmnopqrstuvw xyz 
corresponde: ^^^ ^ ^ ^ ^ ^^^
( Exemplo )

Aumentando o nível de tarefas:

Use o sinal "não" ^entre colchetes para corresponder a todas as palavras abaixo que não terminam em y:
padrão:
string: day dog ​​​​hog hay bog bay ray rub 
correspondências:      ^^^ ^^^ ^^^ ^^^	
( Solução ) Escreva uma expressão regular usando um intervalo e um sinal de "não" ^para encontrar todos os anos entre 1977 e 1982 (inclusive):
padrão:
sequência: 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
correspondências:            ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
( Solução ) Escreva uma expressão regular para encontrar todos os caracteres que não sejam um caractere de sinal "não" ^:
padrão:
string:   abc1 ^ 23*() 
corresponde a: ^^^^ ^^^^^	
( Solução )

Etapa 7: classes de personagens

As classes de personagens são ainda mais simples do que os intervalos de caracteres. Diferentes mecanismos de expressão regular têm diferentes classes disponíveis, então abordarei apenas as principais aqui. (Verifique qual versão de regex você está usando, porque pode haver mais deles - ou podem ser diferentes daqueles mostrados aqui.) As classes de caracteres funcionam quase como intervalos, mas você não pode especificar os valores de 'início' e 'fim':
Aula símbolos
\d "números"[0-9]
\w "símbolos de palavras"[A-Za-z0-9_]
\s "espaços"[ \t\r\n\f]
A classe de caracteres "palavra" \wé especialmente útil porque esse conjunto de caracteres é frequentemente necessário para identificadores válidos (nomes de variáveis, nomes de funções, etc.) em várias linguagens de programação. Podemos usar \wpara simplificar a expressão regular que vimos anteriormente:
padrão: \\[az] 
string: ` \n `, ` \r `, ` \t ` e ` \f ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:   ^^ ^^ ^^ ^^	
Usando \wpodemos escrever assim:
padrão: \\\w 
string: ` \n `, ` \r `, ` \t ` e ` \f ` são caracteres de espaço em branco, ` \. `, ` \\ ` e ` \[ ` não são .
correspondências:   ^^ ^^ ^^ ^^	
( Exemplo )

2 tarefas para dar sorte:

Como você e eu sabemos, em Java, um identificador (nome de uma variável, classe, função, etc.) só pode começar com a letra a- zA- Z, cifrão $ou sublinhado _. ( sublinhar é, obviamente, um estilo ruim, mas o compilador o ignora, nota do tradutor ). O resto dos caracteres devem ser caracteres de "palavra" \w. Usando uma ou mais classes de caracteres, crie uma expressão regular para procurar identificadores Java válidos entre as seguintes sequências de três caracteres:
padrão:
string:   __e $12 .x2 foo Barra 3mm
correspondências: ^^^ ^^^ ^^^ ^^^	
( Solução ) Os Números de Segurança Social (SSN) dos EUA são números de 9 dígitos no formato XXX-XX-XXXX, onde cada X pode ser qualquer dígito [0-9]. Usando uma ou mais classes de caracteres, escreva uma expressão regular para encontrar SSNs formatados corretamente na lista abaixo:
padrão:
sequência: 113-25=1902 182-82-0192 H23-_3-9982 1I1-O0-E38B
correspondências:              ^^^^^^^^^^^
( Solução ) RegEx: 20 passos curtos para dominar expressões regulares. Parte 2. 20 passos curtos para dominar expressões regulares. Parte 3. RegEx: 20 passos curtos para dominar expressões regulares. Parte 4.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION