--Editar-- As respostas atuais têm algumas idéias úteis, mas eu quero algo mais completo que eu possa 100% entender e reutilizar; é por isso que estabeleci uma recompensa. Além disso, ideias que funcionam em qualquer lugar são melhores para mim do que sintaxe padrão como\K
Esta questão é sobre como posso combinar um padrão, exceto em algumas situações s1 s2 s3. Dou um exemplo específico para mostrar o que quero dizer, mas prefiro uma resposta geral que eu possa entender 100% para que possa reutilizá-la em outras situações.
Exemplo
Quero combinar cinco dígitos usando, \b\d{5}\b
mas não em três situações s1 s2 s3:
s1: Não em uma linha que termina com um ponto final como esta frase.
s2: em nenhum lugar dentro dos parênteses.
s3: Não está dentro de um bloco que começa if(
e termina com//endif
Eu sei como resolver qualquer um dos s1 s2 s3 com um lookahead e lookbehind, especialmente em C # lookbehind ou \K
em PHP.
Por exemplo
s1 (?m)(?!\d+.*?\.$)\d+
s3 com C # lookbehind (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
s3 com PHP \ K (?:(?:if\(.*?//endif)\D*)*\K\d+
Mas a mistura de condições faz minha cabeça explodir. Ainda mais uma má notícia é que posso precisar adicionar outras condições s4 s5 em outro momento.
A boa notícia é que não me importo se processo os arquivos usando as linguagens mais comuns, como PHP, C #, Python ou a máquina de lavar do meu vizinho. :) Sou basicamente um iniciante em Python e Java, mas estou interessado em saber se existe uma solução.
Por isso vim aqui ver se alguém pensa em uma receita flexível.
Dicas: você não precisa me fornecer o código completo. :)
Obrigado.
\K
não há sintaxe php especial. Elabore e esclareça o que você quer dizer. Se você pretende nos dizer que não precisa de uma solução "complicada", precisa dizer o que é complicado para você e por quê."if("
open parênteses seja fechado, não com a")"
, mas sim com a"//endif"
:? E se para s3 você realmente quis dizer que a cláusula if deveria ser fechada com"//endif)"
:, então o requisito s3 é um subconjunto de s2.especially in C# lookbehind or \K in PHP
... Mas C # lookbehind não apenas C # é .NET, então você pode reclamar também, eu digo C # não .NET :) E em resposta eu digo Ruby não Onigurama isso também é ruim ... Existe outra linguagem que usa PCRE? Sem falar em Notepad ++ ou ferramentas de servidor, esta é a questão sobre o uso de recurso na linguagem espero explicar e desculpe se parecer erradoRespostas:
Hans, vou morder a isca e detalhar minha resposta anterior. Você disse que queria "algo mais completo", então espero que não se importe com a resposta longa - apenas tentando agradar. Vamos começar com alguns antecedentes.
Em primeiro lugar, esta é uma excelente pergunta. Freqüentemente, há dúvidas sobre como combinar certos padrões, exceto em certos contextos (por exemplo, dentro de um bloco de código ou entre parênteses). Essas perguntas freqüentemente dão origem a soluções bastante estranhas. Portanto, sua pergunta sobre múltiplos contextos é um desafio especial.
Surpresa
Surpreendentemente, existe pelo menos uma solução eficiente que é geral, fácil de implementar e agradável de manter. Ele funciona com todos os sabores de regex que permitem que você inspecione grupos de captura em seu código. E acontece para responder a uma série de perguntas comuns que podem à primeira vista parecer diferentes das suas: "corresponder a tudo, exceto Donuts", "substituir tudo menos ...", "corresponder a todas as palavras, exceto as da lista negra da minha mãe", "ignorar tags "," correspondem à temperatura, a menos que estejam em itálico "...
Infelizmente, a técnica não é bem conhecida: estimo que em vinte perguntas do SO que poderiam usá-la, apenas uma tem uma resposta que a menciona - o que significa talvez uma em cinquenta ou sessenta respostas. Veja minha troca com Kobi nos comentários. A técnica é descrita com alguma profundidade neste artigo, que a chama (otimisticamente) de "melhor truque de regex de todos os tempos". Sem entrar em tantos detalhes, tentarei dar a você uma noção firme de como a técnica funciona. Para obter mais detalhes e exemplos de código em vários idiomas, encorajo você a consultar esse recurso.
Uma variação mais conhecida
Existe uma variação usando sintaxe específica para Perl e PHP que realiza o mesmo. Você o verá no SO nas mãos de mestres de regex, como CasimiretHippolyte e HamZa . Contarei mais sobre isso abaixo, mas meu foco aqui é a solução geral que funciona com todos os tipos de regex (contanto que você possa inspecionar grupos de captura em seu código).
Fato Chave
Na verdade, o truque é combinar os vários contextos que não queremos (encadeando esses contextos usando o
|
OR / alternância) de modo a "neutralizá-los". Depois de combinar todos os contextos indesejados, a parte final da alternância coincide com o que nós não queremos e captura-o ao grupo 1.A receita geral é
Isso vai coincidir
Not_this_context
, mas de certa forma essa partida vai para uma lata de lixo, porque não vamos olhar para as correspondências gerais: olhamos apenas para as capturas do Grupo 1.No seu caso, com seus dígitos e seus três contextos para ignorar, podemos fazer:
Observe que, como realmente combinamos s1, s2 e s3 em vez de tentar evitá-los com lookarounds, as expressões individuais para s1, s2 e s3 podem permanecer claras como o dia. (São as subexpressões de cada lado de a
|
)Toda a expressão pode ser escrita assim:
Veja esta demonstração (mas concentre-se nos grupos de captura no painel inferior direito).
Se você tentar dividir mentalmente esse regex em cada
|
delimitador, na verdade é apenas uma série de quatro expressões muito simples.Para sabores que suportam espaçamento livre, essa leitura é particularmente boa.
Isso é excepcionalmente fácil de ler e manter.
Estendendo o regex
Quando você deseja ignorar mais situações S4 e S5, você as adiciona em mais alternâncias à esquerda:
Como é que isso funciona?
Os contextos que você não quer são adicionados a uma lista de alternâncias à esquerda: eles vão combinar, mas essas combinações gerais nunca são examinadas, então combiná-los é uma maneira de colocá-los em uma "lata de lixo".
O conteúdo que você deseja, no entanto, é capturado para o Grupo 1. Em seguida, você deve verificar programaticamente se o Grupo 1 está definido e não vazio. Esta é uma tarefa de programação trivial (e falaremos mais tarde sobre como ela é feita), especialmente considerando que ela deixa você com uma regex simples que você pode entender rapidamente e revisar ou estender conforme necessário.
Nem sempre sou um fã de visualizações, mas este faz um bom trabalho em mostrar como o método é simples. Cada "linha" corresponde a uma correspondência potencial, mas apenas a linha inferior é capturada no Grupo 1.
Demonstração Debuggex
Variação Perl / PCRE
Em contraste com a solução geral acima, existe uma variação para Perl e PCRE que é freqüentemente vista no SO, pelo menos nas mãos de Deuses de regex como @CasimiretHippolyte e @HamZa. Isto é:
No seu caso:
Esta variação é um pouco mais fácil de usar porque o conteúdo correspondente nos contextos s1, s2 e s3 é simplesmente ignorado, então você não precisa inspecionar as capturas do Grupo 1 (observe que os parênteses sumiram). As partidas contêm apenas
whatYouWant
Note-se que
(*F)
,(*FAIL)
e(?!)
são todos a mesma coisa. Se você quiser ser mais obscuro, pode usar(*SKIP)(?!)
demo para esta versão
Formulários
Aqui estão alguns problemas comuns que essa técnica pode facilmente resolver. Você notará que a escolha de palavras pode fazer alguns desses problemas soarem diferentes, embora na verdade sejam virtualmente idênticos.
<a stuff...>...</a>
?<i>
tag ou snippet de javascript (mais condições)?Como programar as capturas do Grupo 1
Você não gostou do código, mas, para completar ... O código para inspecionar o Grupo 1 obviamente dependerá do idioma de sua escolha. De qualquer forma, ele não deve adicionar mais do que algumas linhas ao código que você usaria para inspecionar correspondências.
Em caso de dúvida, recomendo que você dê uma olhada na seção de exemplos de código do artigo mencionado anteriormente, que apresenta código para algumas linguagens.
Alternativas
Dependendo da complexidade da questão e do mecanismo regex usado, existem várias alternativas. Aqui estão os dois que podem se aplicar à maioria das situações, incluindo várias condições. Em minha opinião, nenhum dos dois é tão atraente quanto a
s1|s2|s3|(whatYouWant)
receita, até porque a clareza sempre vence.1. Substitua e depois Combine.
Uma boa solução que parece hacky, mas funciona bem em muitos ambientes, é trabalhar em duas etapas. Uma primeira regex neutraliza o contexto que você deseja ignorar, substituindo strings potencialmente conflitantes. Se você deseja apenas corresponder, pode substituir por uma string vazia e, em seguida, executar a correspondência na segunda etapa. Se você quiser substituir, você pode primeiro substituir as strings a serem ignoradas por algo distinto, por exemplo, cercar seus dígitos com uma cadeia de largura fixa de
@@@
. Após essa substituição, você está livre para substituir o que realmente deseja e, em seguida, terá que reverter suas@@@
cordas distintas .2. Lookarounds.
Sua postagem original mostrou que você entende como excluir uma única condição usando soluções alternativas. Você disse que o C # é ótimo para isso e está certo, mas não é a única opção. Os tipos de regex do .NET encontrados em C #, VB.NET e Visual C ++, por exemplo, bem como o
regex
módulo ainda experimental a ser substituídore
em Python, são os únicos dois mecanismos que conheço que suportam lookbehind de largura infinita. Com essas ferramentas, uma condição em um olhar para trás pode cuidar não apenas de olhar para trás, mas também para o fósforo e além dele, evitando a necessidade de coordenar com um olhar para frente. Mais condições? Mais alternativas.Reciclando a regex que você tinha para s3 em C #, todo o padrão ficaria assim.
Mas agora você sabe que não estou recomendando isso, certo?
Exclusões
@HamZa e @Jerry sugeriram que eu mencionasse um truque adicional para casos em que você deseja apenas excluir
WhatYouWant
. Você se lembra que a receita para combinarWhatYouWant
(capturá-lo no Grupo 1) eras1|s2|s3|(WhatYouWant)
, certo? Para excluir todas as instâncias deWhatYouWant
, você altera o regex paraPara a string de substituição, você usa
$1
. O que acontece aqui é que para cada instâncias1|s2|s3
que é correspondida, a substituição$1
substitui essa instância por ela mesma (referenciada por$1
). Por outro lado, quandoWhatYouWant
é correspondido, ele é substituído por um grupo vazio e nada mais - e, portanto, excluído. Veja esta demonstração , obrigado @HamZa e @Jerry por sugerirem esta adição maravilhosa.Substituições
Isso nos leva a substituições, nas quais tocarei brevemente.
(*SKIP)(*F)
variação mencionada acima para corresponder exatamente ao que você deseja e faça uma substituição direta.Diverta-se!
Não, espere, tem mais!
Ah, nah, vou guardar isso para minhas memórias em vinte volumes, a serem lançados na próxima primavera.
fonte
Tarzan
, mas não quando estiver entre aspas duplas. O:/no|no|(yes)/
trick regex seria algo como:/"[^"]*"|Tarzan/
(ignorando caracteres escapados). Isto irá funcionar para muitos casos, mas falha completamente quando aplicado ao seguinte texto JavaScript válido:var bug1 = 'One " quote here. Should match this Tarzan'; var bug2 = "Should not match this Tarzan";
. O truque de Rex só funciona quando TODAS as estruturas possíveis são combinadas - em outras palavras - você precisa analisar totalmente o texto para garantir 100% de precisão.var bug1 = /"[^"]*"|(Tarzan)/gi;
e teve o mesmo efeito (e este segundo exemplo certamente não é um caso extremo). Há muitos outros exemplos que eu poderia citar onde essa técnica não funciona de maneira confiável.(?<!\\)"(?:\\"|[^"\r\n])*+"
Você não puxa as armas grandes a menos que tenha um motivo. O princípio da solução ainda é válido. Se não formos capazes de expressar um padrão para colocar no lado esquerdo, é uma história diferente, precisamos de uma solução diferente. Mas a solução faz o que anuncia.Faça três combinações diferentes e lide com a combinação das três situações usando a lógica condicional no programa. Você não precisa lidar com tudo em um regex gigante.
EDITAR: deixe-me expandir um pouco porque a questão se tornou mais interessante :-)
A ideia geral que você está tentando capturar aqui é comparar com um determinado padrão de regex, mas não quando há certos outros (pode ser qualquer número) padrões presentes na string de teste. Felizmente, você pode tirar proveito de sua linguagem de programação: mantenha as regexes simples e use apenas uma condicional composta. Uma prática recomendada seria capturar essa ideia em um componente reutilizável, então vamos criar uma classe e um método para implementá-lo:
Portanto, acima, configuramos a string de pesquisa (os cinco dígitos), várias strings de exceção (seu s1 , s2 e s3 ) e, em seguida, tentamos comparar com várias strings de teste. Os resultados impressos devem ser mostrados nos comentários ao lado de cada string de teste.
fonte
Sua exigência de que não esteja entre parênteses é impossível de satisfazer em todos os casos. Ou seja, se você pode de alguma forma encontrar um
(
à esquerda e)
à direita, nem sempre significa que você está dentro dos parênteses. Por exemplo.(....) + 55555 + (.....)
- não dentro dos parênteses ainda existem(
e)
à esquerda e à direitaAgora você pode se achar inteligente e olhar para
(
a esquerda apenas se não encontrar)
antes e vice-versa para a direita. Isso não funcionará neste caso:((.....) + 55555 + (.....))
- dentro dos parênteses, embora haja fechamento)
e(
à esquerda e à direita.É impossível descobrir se você está dentro dos parênteses usando regex, pois a regex não pode contar quantos parênteses foram abertos e quantos fechados.
Considere esta tarefa mais fácil: usando regex, descubra se todos os parênteses (possivelmente aninhados) em uma string estão fechados, ou seja, para cada que
(
você precisa encontrar)
. Você vai descobrir que é impossível resolver e se você não puder resolver isso com regex, então você não pode descobrir se uma palavra está dentro de parênteses para todos os casos, já que você não pode descobrir em alguma posição na string se todos os precedentes(
têm um correspondente)
.fonte
Hans, se você não se importa, eu usei a máquina de lavar do seu vizinho chamada perl :)
Editado: Abaixo de um pseudocódigo:
Dado o arquivo input.txt:
E o script validator.pl:
Execução:
fonte
Não tenho certeza se isso ajudaria você ou não, mas estou fornecendo uma solução considerando as seguintes suposições -
No entanto, considerei também o seguinte -
if(
blocos.Ok, aqui está a solução -
Usei C # e com ele MEF (Microsoft Extensibility Framework) para implementar os analisadores configuráveis. A ideia é usar um único analisador para analisar e uma lista de classes validadoras configuráveis para validar a linha e retornar verdadeiro ou falso com base na validação. Em seguida, você pode adicionar ou remover qualquer validador a qualquer momento ou adicionar novos, se desejar. Até agora eu já implementei para S1, S2 e S3 que você mencionou, verifique as classes no ponto 3. Você terá que adicionar classes para s4, s5 se precisar no futuro.
Primeiro, crie as interfaces -
Em seguida, vem o leitor e verificador de arquivos -
Em seguida, vem a implementação de verificadores individuais, os nomes das classes são autoexplicativos, então não acho que eles precisem de mais descrições.
O programa -
Para o teste peguei o arquivo de amostra do @Tiago
Test.txt
que tinha as seguintes linhas -Dá a saída -
Não sei se isso te ajudaria ou não, eu me diverti brincando com isso .... :)
A melhor parte é que, para adicionar uma nova condição, tudo o que você precisa fazer é fornecer uma implementação de
IPatternMatcher
, ela será chamada automaticamente e, portanto, será validada.fonte
O mesmo que @ zx81,
(*SKIP)(*F)
mas com o uso de uma afirmação antecipada negativa.DEMO
Em python, eu faria facilmente assim,
Resultado:
fonte