Pergunta

Eu estou looping através de um grande arquivo de texto e mensagens instantâneas à procura de linhas que não contenham mais de 3 caracteres diferentes (aqueles personagens, no entanto, pode ser repetido indefinidamente). Estou assumindo a melhor maneira de fazer isso seria algum tipo de expressão regular.

Toda a ajuda é apreciada.

(Eu estou escrevendo o script em PHP, se isso ajuda)

Foi útil?

Solução

Talvez isso vai funcionar:

preg_match("/^(.)\\1*(.)?(?:\\1*\\2*)*(.)?(?:\\1*\\2*\\3*)*$/", $string, $matches);
// aaaaa:Pass
// abababcaaabac:Pass
// aaadsdsdads:Pass
// aasasasassa:Pass
// aasdasdsadfasf:Fail

Explaination:

/
 ^                 #start of string
 (.)               #match any character in group 1
 \\1*              #match whatever group 1 was 0 or more times
 (.)?              #match any character in group 2 (optional)
 (?:\\1*\\2*)*     #match group 1 or 2, 0 or more times, 0 or more times 
                   #(non-capture group)
 (.)?              #match any character in group 3 (optional)
 (?:\\1*\\2*\\3*)* #match group 1, 2 or 3, 0 or more times, 0 or more times
                   #(non-capture group)
 $                 #end of string
/

Um acrescentou benifit, $matches[1], [2], [3] irá conter os três caracteres que você deseja. Os olhares expressão regular para o primeiro caractere, em seguida, armazena e jogos-lo até que algo diferente do que o caráter é encontrado, as capturas que, como um segundo personagem, combinando qualquer um desses personagens tantas vezes quanto ele pode, pega o terceiro personagem, e combina todos os três até o jogo falhar ou as extremidades da corda e o teste passa.

Editar

Esta expressão regular irá ser muito mais rápido por causa da forma como o motor de análise e obras backtracking, a resposta de bobince leitura para a explicação:

/^(.)\\1*(?:(.)(?:\\1|\\2)*(?:(.)(?:\\1|\\2|\\3)*)?)?$/

Outras dicas

divertido otimização Regex exercício tempo para as crianças! Tomando regex de gnarf como ponto de partida:

^(.)\1*(.)?(?:\1*\2*)*(.)?(?:\1*\2*\3*)*$

eu notei que não foram encaixados e seqüencial * s aqui, o que pode causar uma série de retrocesso. Por exemplo, em 'abcaaax' ele vai tentar igualar essa última série de 'uma de como um único \ 1 * de comprimento 3, a \ 1 * de comprimento dois seguido por um único \ 1, a \ 1 seguido por um 2-length \ 1 *, ou três single-match \ 1s. Esse problema fica muito pior quando você tem cordas mais longas, especialmente quando, devido à regex não há nada que impeça \ 1 de ser o mesmo personagem como \ 2.

^(.)\1*(.)?(?:\1|\2)*(.)?(?:\1|\2|\3)*$

Este foi mais de duas vezes mais rápido que o original, testando em correspondência PCRE do Python. (É mais rápido do que a sua criação em PHP, desculpe.)

Este ainda tem um problema em que (.)? pode combinar nada, e depois continuar com o resto do jogo. \1|\2 ainda irá corresponder \ 1, mesmo se não houver \ 2 para combinar, resultando em potencial retrocesso tentando introduzir as cláusulas \1|\2 e \1|\2|\3 antes, quando eles não podem resultar em uma partida. Isso pode ser resolvido movendo a optionalness ? ao redor do conjunto das cláusulas de arrasto:

^(.)\1*(?:(.)(?:\1|\2)*(?:(.)(?:\1|\2|\3)*)?)?$

Este foi duas vezes mais rápido novamente.

Há ainda um problema em potencial em que qualquer um de \ 1, \ 2 e \ 3 pode ser o mesmo personagem, potencialmente causando mais retrocesso quando a expressão não corresponde. Isto pará-lo usando um lookahead negativo para não coincidir com um caractere anterior:

^(.)\1*(?:(?!\1)(.)(?:\1|\2)*(?:(?!\1|\2)(.)(?:\1|\2|\3)*)?)?$

No entanto, em Python com meus dados de teste aleatório eu não notar uma aceleração significativa deste. Sua milhagem pode variar em PHP dependente de dados de teste, mas pode ser bom o suficiente. Possessivo-matching (* +) poderia ter ajudado se isso fosse disponível aqui.

No regex desempenho melhor do que a alternativa Python mais fácil de ler:

len(set(s))<=3

O método análogo em PHP provavelmente seria com count_chars :

strlen(count_chars($s, 3))<=3

Eu não testei a velocidade, mas eu gostaria muito de esperar que este seja mais rápido do que regex, além de ser muito, muito mais agradável de ler.

Então, basicamente, eu desperdicei apenas totalmente minha mexer tempo com expressões regulares. Não desperdice seu tempo, olhar para métodos de string simples primeiro antes de recorrer a regex!

Com o risco de ficar downvoted, vou sugerir expressões regulares não são destinadas a lidar com esta situação.

Você pode combinar um personagem ou um conjunto de caracteres, mas você não pode tê-lo lembrar do que personagens de um conjunto já foram encontrados para excluir aqueles de mais jogo.

Eu sugiro que você manter um conjunto de caracteres, você redefini-la antes que você comece com uma nova linha, e você adicionar lá elementos, indo ao longo da linha. Assim que a contagem de elementos no conjunto excede 3, você deixa cair a linha atual e avançar para a próxima.

para mim - como um programador, com conhecimento expressão regular fair-suficiente este sons não como um problema que você pode resolver usando Regexp única

.

mais provável que você vai precisar para construir uma chave de estrutura de dados hashmap / array: valor de caractere: contagem e repita o arquivo de texto grande, reconstruir o mapa para cada linha. a cada nova verificação personagem, se a contagem de caracteres já encontrado é 2, em caso afirmativo, pule linha atual.

mas im ansioso para ser surpreendido se um hacker de regexp louco vai chegar a uma solução.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top