Expressão regular para palavras duplicadas

114

Sou um novato em expressões regulares e não consigo descobrir como escrever uma única expressão regular que "corresponda" a quaisquer palavras consecutivas duplicadas, como:

Paris em a da primavera.

Não que isso esteja relacionado.

Por que você está rindo? São minhas minhas expressões regulares que ruim ??

Existe uma única expressão regular que corresponderá a TODAS as strings em negrito acima?

Joshua
fonte
4
@poly: Não foi uma "acusação", mas uma pergunta calma e normal que pode perfeitamente aceitar um "não" como resposta. @Joshua: Sim, algumas pessoas (não poucas) permitem que este site faça o dever de casa por elas. Mas fazer perguntas sobre o dever de casa não é uma coisa ruim de se fazer no SO, quando eles são marcados como tal. Normalmente, o estilo das respostas muda de "aqui está a solução" para "aqui estão algumas coisas nas quais você não pensou", e isso é uma coisa boa. Alguém tem que tentar manter a distinção, no caso dele fui eu, e em outros lugares "outras pessoas" fazem a mesma coisa. Isso é tudo.
Tomalak
13
Espero nunca ver uma pergunta como "Isso parece um pouco com uma pergunta sobre o local de trabalho. Não é?" e então as pessoas vão argumentar se o estouro de pilha está fazendo o trabalho de alguém.
marcio
@Joshua +1 com relação à solução regex que você aceitou, você poderia me dizer como eu poderia substituir as correspondências (duplicatas) por um elemento do par (por exemplo, not that that is related-> not that is related)? Agradecemos antecipadamente
Antoine,
@Joshua Acho que encontrei a solução: devo substituir por \1!
Antoine de
2
@DavidLeal Que tal \b(\w+)\s+(\1\s*)+\b?
ytu

Respostas:

141

Experimente esta expressão regular:

\b(\w+)\s+\1\b

Aqui \bestá um limite de palavra e faz \1referência à correspondência capturada do primeiro grupo.

quiabo
fonte
1
Me faz pensar; é possível fazer \0também? (Onde \0está toda a regex, até o ponto atual OU onde \0se refere a toda a regex)
Pindatjuh
@Pindatjuh: Não, acho que não porque essa sub-partida também faria parte da partida inteira.
Gumbo de
Pelo menos funciona no mecanismo regex usado na caixa de diálogo de pesquisa / substituição do Eclipse.
Chaos_99
3
Apenas um aviso, isso não trata palavras com apóstrofos ou (como Noel menciona) hifens. A solução de Mike funciona melhor nesses casos
3
Além disso, ele não pegará triplicados (ou mais), não quando um dos duplicados / triplicados estiver no final da string
Nico
20

Acredito que esta regex lida com mais situações:

/(\b\S+\b)\s+\b\1\b/

Uma boa seleção de strings de teste pode ser encontrada aqui: http://callumacrae.github.com/regex-tuesday/challenge1.html

Mike Viens
fonte
Ótimo, funciona com apóstrofos / hífens / etc. também - obrigado!
para o link challenge1, o que você coloca na área de substituição para usar a palavra agrupada? Tentei, <strong>\0</strong>mas não funcionou.
Uptownhr
2
Ele não pegará triplicados (ou mais), não quando um dos duplicados / triplicados estiver no final da string
Nico
@uptownhr que você deseja usar $1 <strong>$2</strong>. Mas também use regex diferente /\b(\S+) (\1)\b/gi. Aqui está um link: callumacrae.github.io/regex-tuesday/…
dsalaj
e se eu quiser encontrar todas as palavras consecutivas de uma tag específica, <p class="bebe">bla bla</p>como posso integrar esta fórmula regex?
Just Me
7

Tente isso com RE abaixo

  • \ b início da palavra limite da palavra
  • \ W + qualquer caractere de palavra
  • \ 1 mesma palavra já combinada
  • \ b fim da palavra
  • () * Repetindo novamente

    public static void main(String[] args) {
    
        String regex = "\\b(\\w+)(\\b\\W+\\b\\1\\b)*";//  "/* Write a RegEx matching repeated words here. */";
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE/* Insert the correct Pattern flag here.*/);
    
        Scanner in = new Scanner(System.in);
    
        int numSentences = Integer.parseInt(in.nextLine());
    
        while (numSentences-- > 0) {
            String input = in.nextLine();
    
            Matcher m = p.matcher(input);
    
            // Check for subsequences of input that match the compiled pattern
            while (m.find()) {
                input = input.replaceAll(m.group(0),m.group(1));
            }
    
            // Prints the modified sentence.
            System.out.println(input);
        }
    
        in.close();
    }
Faakhir
fonte
5

A biblioteca PCRE amplamente usada pode lidar com tais situações (você não conseguirá o mesmo com motores regex compatíveis com POSIX, no entanto):

(\b\w+\b)\W+\1
soulmerge
fonte
Você precisa de algo para combinar os caracteres entre as duas palavras, como \W+. \bnão vai fazer isso, porque não consome nenhum personagem.
Alan Moore
Isso potencialmente resultará em correspondência falso-positiva em casos como ... the these problems.... Esta solução não é tão confiável quanto a estrutura geral do padrão de Gumbo, que implementa suficientemente os limites das palavras.
mickmackusa
e se eu quiser encontrar todas as palavras consecutivas de uma tag específica, <p class="bebe">bla bla</p>como posso integrar esta fórmula regex?
Just Me
4

Este é o regex que uso para remover frases duplicadas em meu bot do twitch:

(\S+\s*)\1{2,}

(\S+\s*) procura por qualquer sequência de caracteres que não seja um espaço em branco, seguido de um espaço em branco.

\1{2,}em seguida, procura mais de 2 ocorrências dessa frase na string para corresponder. Se houver 3 frases idênticas, ela corresponde.

Neceros
fonte
Essa resposta é enganosa. Ele não caça duplicatas, ele caça substrings com 3 ou mais ocorrências. Também não é muito robusto por causa do \s*grupo de captura. Veja esta demonstração: regex101.com/r/JtCdd6/1
mickmackusa
Além disso, casos extremos (texto de baixa frequência) produziriam correspondências positivas falsas. Por exemplo, I said "oioioi" that's some wicked mistressship!em oioioiesss
mickmackusa
4

A expressão abaixo deve funcionar corretamente para encontrar qualquer número de palavras consecutivas. A correspondência pode não fazer distinção entre maiúsculas e minúsculas.

String regex = "\\b(\\w+)(\\s+\\1\\b)*";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

Matcher m = p.matcher(input);

// Check for subsequences of input that match the compiled pattern
while (m.find()) {
     input = input.replaceAll(m.group(0), m.group(1));
}

Amostra de entrada: Goodbye goodbye GooDbYe

Saída de amostra: adeus

Explicação:

A expressão regex:

\ b: Início de um limite de palavra

\ w +: qualquer número de caracteres de palavras

(\ s + \ 1 \ b) *: Qualquer número de espaço seguido pela palavra que corresponda à palavra anterior e termine o limite da palavra. Tudo envolvido em * ajuda a encontrar mais de uma repetição.

Agrupamento:

m.group (0): Deve conter o grupo combinado no caso acima Goodbye goodbye GooDbYe

m.group (1): Deve conter a primeira palavra do padrão correspondente no caso acima. Adeus

O método Replace deve substituir todas as palavras consecutivas correspondidas pela primeira instância da palavra.

Akriti
fonte
3

Não. Essa é uma gramática irregular. Pode haver expressões regulares específicas do mecanismo / linguagem que você pode usar, mas não há nenhuma expressão regular universal que possa fazer isso.

Ignacio Vazquez-Abrams
fonte
12
Apesar de estar correto em um sentido estrito, acredito que não haja mais nenhum mecanismo regex em uso sério que não ofereça suporte a agrupamento e referências anteriores.
Tomalak
3

Aqui está um que captura várias palavras várias vezes:

(\b\w+\b)(\s+\1)+
sinaptikon
fonte
e se eu quiser encontrar todas as palavras consecutivas de uma tag específica, <p class="bebe">bla bla</p>como posso integrar esta fórmula regex?
Just Me
Acredito que isso exigirá análise de HTML. Para qualquer tag que você deseja pesquisar, encontre todas as ocorrências de tag dentro do HTML e execute esta regex uma a uma em cada uma. Ou se você não se preocupa com onde a repetição ocorre no HTML, concatene todos os atributos de texto da tag e execute a regex na string concatenada
synaptikon
Eu me encontro a resposta<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
Just Me
3

Regex para remover 2+ palavras duplicadas (palavras consecutivas / não consecutivas)

Experimente esta regex que pode capturar 2 ou mais palavras duplicadas e deixar apenas uma única palavra. E as palavras duplicadas nem precisam ser consecutivas .

/\b(\w+)\b(?=.*?\b\1\b)/ig

Aqui, \bé usado para Limite de palavras, ?=é usado para antecipação positiva e \1é usado para referência inversa.

Fonte de exemplo

Niket Pathak
fonte
1
Não consecutivo é uma má ideia: "the cat sat on the mat"->" cat sat on the mat"
Walf
@Walf True. No entanto, existem cenários em que isso se destina. (por exemplo: durante a
coleta de
Por que você quebrou sua regex novamente depois que eu a corrigi ? Você acha que eu mudei sua intenção? Mesmo o exemplo que você vinculou não tem o erro.
Walf
Sim, foi um erro, copiar e colar o material errado. Pretende copiar o do meu exemplo, na verdade. de qualquer maneira, agora funciona! então tudo bem! Obrigado!
Niket Pathak
2

O exemplo em Javascript: As boas partes podem ser adaptadas para fazer isso:

var doubled_words = /([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1(?:\s|$)/gi;

\ b usa \ w para limites de palavras, onde \ w é equivalente a [0-9A-Z_a-z]. Se você não se importa com essa limitação, a resposta aceita está bem.

Daniel
fonte
2

Como alguns desenvolvedores estão vindo para esta página em busca de uma solução que não apenas elimine substrings duplicados consecutivos sem espaço em branco, mas triplicados e além, vou mostrar o padrão adaptado.

Padrão: /(\b\S+)(?:\s+\1\b)+/( Demonstração de padrão )
Substituir: $1(substitui a correspondência de string inteira pelo grupo de captura # 1)

Este padrão corresponde avidamente a uma substring "inteira" sem espaço em branco, então requer uma ou mais cópias da substring correspondida que pode ser delimitada por um ou mais caracteres de espaço em branco (espaço, tabulação, nova linha, etc).

Especificamente:

  • \b (limite de palavra) caracteres são vitais para garantir que palavras parciais não sejam correspondidas.
  • O segundo parênteses é um grupo de não captura, porque essa substring de largura variável não precisa ser capturada - apenas correspondida / absorvida.
  • o +(um ou mais quantificadores) no grupo de não captura é mais apropriado do que *porque *"incomodará" o mecanismo de regex para capturar e substituir ocorrências de singleton - isso é um desperdício de design de padrão.

* observe que se você estiver lidando com sentenças ou strings de entrada com pontuação, o padrão precisará ser mais refinado.

Mickmackusa
fonte
@AdamJones usa este padrão em seu projeto php. A resposta de Nico contém alguma sintaxe desnecessária.
mickmackusa
1

Esta expressão (inspirada em Mike, acima) parece capturar todas as duplicatas, triplicatas, etc, incluindo aquelas no final da string, o que a maioria das outras não:

/(^|\s+)(\S+)(($|\s+)\2)+/g, "$1$2")

Eu sei que a pergunta feita para coincidir com duplicatas apenas, mas um triplicado é apenas 2 duplicatas lado a lado :)

Primeiro, eu coloco (^|\s+)para ter certeza de que começa com uma palavra completa, caso contrário, "bife de criança" iria para "bife de criança" (o "s" iria corresponder). Em seguida, ele corresponde a todas as palavras completas ( (\b\S+\b)), seguidas por um final de string ( $) ou um número de espaços ( \s+), o todo repetido mais de uma vez.

Eu tentei assim e funcionou bem:

var s = "here here here     here is ahi-ahi ahi-ahi ahi-ahi joe's joe's joe's joe's joe's the result result     result";
print( s.replace( /(\b\S+\b)(($|\s+)\1)+/g, "$1"))         
--> here is ahi-ahi joe's the result
Nico
fonte
Estou tendo problemas para reescrever isso em PHP, é vital que eu obtenha uma única cópia da duplicata correspondente substituindo cada ocorrência de duplicatas / triplicatas etc. Até agora eu tenho: preg_replace ('/ (^ | \ s +) (\ S +) ( ($ | \ s +) \ 2) + / im ',' $ 0 ', $ string);
AdamJones
Esta é a melhor resposta. Acabei de fazer um ajuste nisso adicionando \bao final assim: /(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2")Isso funcionará para situações como esta: the the string String string stringing the the along the the stringse tornará the string stringing the along the stringAviso string stringing. Corresponde à sua resposta. Obrigado.
Ste de
-1

Use isto caso você queira uma verificação sem distinção entre maiúsculas e minúsculas para palavras duplicadas.

(?i)\\b(\\w+)\\s+\\1\\b
Neelam
fonte
Usar o modificador de padrão que não diferencia maiúsculas de minúsculas não adianta seu padrão. Não há intervalos de letras para a bandeira impactar.
mickmackusa
Esta é efetivamente uma duplicata da resposta aceita e não agrega valor à página. Considere remover esta resposta para reduzir o inchaço da página.
mickmackusa