--Editar-- Las respuestas actuales tienen algunas ideas útiles, pero quiero algo más completo que pueda comprender y reutilizar al 100%; por eso puse una recompensa. Además, las ideas que funcionan en todas partes son mejores para mí que la sintaxis estándar como\K
Esta pregunta trata sobre cómo puedo hacer coincidir un patrón, excepto en algunas situaciones s1 s2 s3. Doy un ejemplo específico para mostrar mi significado, pero prefiero una respuesta general que pueda entender al 100% para poder reutilizarla en otras situaciones.
Ejemplo
Quiero hacer coincidir cinco dígitos usando \b\d{5}\b
pero no en tres situaciones s1 s2 s3:
s1: No en una línea que termine con un punto como esta oración.
s2: No en ningún lugar dentro de los parens.
s3: No dentro de un bloque que comienza if(
y termina con//endif
Sé cómo resolver cualquiera de s1 s2 s3 con un lookahead y lookbehind, especialmente en C # lookbehind o \K
en PHP.
Por ejemplo
s1 (?m)(?!\d+.*?\.$)\d+
s3 con C # lookbehind (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
s3 con PHP \ K (?:(?:if\(.*?//endif)\D*)*\K\d+
Pero la mezcla de condiciones juntas hace que mi cabeza explote. Aún más malas noticias es que es posible que deba agregar otras condiciones s4 s5 en otro momento.
La buena noticia es que no me importa si proceso los archivos usando los lenguajes más comunes como PHP, C #, Python o la lavadora de mi vecino. :) Soy prácticamente un principiante en Python y Java, pero estoy interesado en saber si tiene una solución.
Así que vine aquí para ver si alguien piensa en una receta flexible.
Las sugerencias están bien: no es necesario que me des el código completo. :)
Gracias.
\K
no es una sintaxis php especial. Por favor, explique y aclare lo que quiere decir. Si su objetivo es decirnos que no necesita una solución "complicada", tiene que decir qué es complicado para usted y por qué."if("
par : open sea cerrado, no con a")"
, sino con a"//endif"
:? Y si para s3 realmente quiso decir que la cláusula if debería cerrarse con:,"//endif)"
entonces el requisito de s3 es un subconjunto de s2.especially in C# lookbehind or \K in PHP
... Pero C # mira hacia atrás no solo C # es .NET, así que tú también puedes quejarte. Yo digo C # no .NET :) Y en respuesta digo Ruby, no Onigurama, eso también es malo ... ¿Hay otro idioma que use PCRE? No hablamos de Notepad ++ o herramientas de servidor esta es la pregunta acerca del uso de función en el lenguaje espero que la explican y lo siento si se ve malRespuestas:
Hans, morderé el anzuelo y desarrollaré mi respuesta anterior. Dijiste que querías "algo más completo", así que espero que no te importe la respuesta larga, solo intento complacer. Comencemos con algunos antecedentes.
En primer lugar, esta es una excelente pregunta. A menudo hay preguntas sobre la coincidencia de ciertos patrones, excepto en ciertos contextos (por ejemplo, dentro de un bloque de código o entre paréntesis). Estas preguntas a menudo dan lugar a soluciones bastante incómodas. Entonces, su pregunta sobre múltiples contextos es un desafío especial.
Sorpresa
Sorprendentemente, existe al menos una solución eficiente que es general, fácil de implementar y un placer de mantener. Se trabaja con todos los sabores de expresiones regulares que le permiten inspeccionar los grupos de captura en su código. Y resulta que responde a una serie de preguntas comunes que al principio pueden sonar diferentes a las suyas: "hacer coincidir todo excepto Donuts", "reemplazar todas menos ...", "hacer coincidir todas las palabras excepto las de la lista negra de mi madre", "ignorar etiquetas "," coinciden con la temperatura a menos que estén en cursiva "...
Lamentablemente, la técnica no es muy conocida: estimo que en veinte preguntas de SO que podrían usarla, solo una tiene una respuesta que la menciona, lo que significa tal vez una de cada cincuenta o sesenta respuestas. Vea mi intercambio con Kobi en los comentarios. La técnica se describe con cierta profundidad en este artículo que la llama (con optimismo) el "mejor truco de expresiones regulares". Sin entrar en muchos detalles, intentaré darles una idea clara de cómo funciona la técnica. Para obtener más detalles y ejemplos de código en varios idiomas, le animo a que consulte ese recurso.
Una variación más conocida
Existe una variación que usa la sintaxis específica de Perl y PHP que logra lo mismo. Lo verá en SO en manos de maestros de expresiones regulares como CasimiretHippolyte y HamZa . Le contaré más sobre esto a continuación, pero mi enfoque aquí está en la solución general que funciona con todos los tipos de expresiones regulares (siempre que pueda inspeccionar los grupos de captura en su código).
Hecho clave
De hecho, el truco consiste en hacer coincidir los diversos contextos que no queremos (encadenar estos contextos usando la
|
OR / alternancia) para "neutralizarlos". Después de comparar todos los contextos no deseados, la parte final de la alternancia coincide con lo que sí queremos y lo captura al grupo 1.La receta general es
Esto coincidirá
Not_this_context
, pero en cierto sentido esa coincidencia va a la basura, porque no miraremos las coincidencias generales: solo miramos las capturas del Grupo 1.En su caso, con sus dígitos y sus tres contextos para ignorar, podemos hacer:
Tenga en cuenta que debido a que en realidad hacemos coincidir s1, s2 y s3 en lugar de tratar de evitarlos con cambios de opinión, las expresiones individuales para s1, s2 y s3 pueden permanecer claras como el día. (Son las subexpresiones a cada lado de a
|
)La expresión completa se puede escribir así:
Vea esta demostración (pero concéntrese en los grupos de captura en el panel inferior derecho).
Si mentalmente intenta dividir esta expresión regular en cada
|
delimitador, en realidad es solo una serie de cuatro expresiones muy simples.Para los sabores que admiten el espacio libre, esto se lee particularmente bien.
Esto es excepcionalmente fácil de leer y mantener.
Extendiendo la expresión regular
Cuando desee ignorar más situaciones s4 y s5, agréguelas en más alternancias a la izquierda:
¿Como funciona esto?
Los contextos que no desea se agregan a una lista de alternancias a la izquierda: coincidirán, pero estas coincidencias generales nunca se examinan, por lo que emparejarlas es una forma de ponerlas en un "contenedor de basura".
Sin embargo, el contenido que desea se captura en el Grupo 1. Luego, debe verificar mediante programación que el Grupo 1 esté configurado y no vacío. Esta es una tarea de programación trivial (y luego hablaremos sobre cómo se hace), especialmente considerando que te deja con una expresión regular simple que puedes entender de un vistazo y revisar o ampliar según sea necesario.
No siempre soy un fanático de las visualizaciones, pero esta hace un buen trabajo al mostrar lo simple que es el método. Cada "línea" corresponde a una coincidencia potencial, pero solo la línea de fondo se captura en el Grupo 1.
Demostración de Debuggex
Variación Perl / PCRE
En contraste con la solución general anterior, existe una variación para Perl y PCRE que a menudo se ve en SO, al menos en manos de dioses regex como @CasimiretHippolyte y @HamZa. Es:
En tu caso:
Esta variación es un poco más fácil de usar porque el contenido que coincide en los contextos s1, s2 y s3 simplemente se omite, por lo que no es necesario inspeccionar las capturas del Grupo 1 (observe que los paréntesis han desaparecido). Los partidos solo contienen
whatYouWant
Tenga en cuenta que
(*F)
,(*FAIL)
y(?!)
son la misma cosa. Si quisieras ser más oscuro, podrías usar(*SKIP)(?!)
demo para esta versión
Aplicaciones
A continuación, se muestran algunos problemas comunes que esta técnica a menudo puede resolver fácilmente. Notará que la elección de palabras puede hacer que algunos de estos problemas suenen diferentes, mientras que en realidad son prácticamente idénticos.
<a stuff...>...</a>
?<i>
etiqueta o un fragmento de JavaScript (más condiciones)?Cómo programar las capturas del grupo 1
No lo hizo en cuanto al código, pero, para completarlo ... El código para inspeccionar el Grupo 1 obviamente dependerá del idioma que elija. En cualquier caso, no debería agregar más de un par de líneas al código que usaría para inspeccionar las coincidencias.
En caso de duda, le recomiendo que consulte la sección de ejemplos de código del artículo mencionado anteriormente, que presenta código para bastantes idiomas.
Alternativas
Dependiendo de la complejidad de la pregunta y del motor de expresiones regulares utilizado, existen varias alternativas. Estos son los dos que pueden aplicarse a la mayoría de situaciones, incluidas múltiples condiciones. En mi opinión, ninguno es tan atractivo como la
s1|s2|s3|(whatYouWant)
receta, aunque solo sea porque la claridad siempre gana.1. Reemplace y luego haga coincidir.
Una buena solución que suena hacky pero funciona bien en muchos entornos es trabajar en dos pasos. Una primera expresión regular neutraliza el contexto que desea ignorar reemplazando cadenas potencialmente conflictivas. Si solo desea hacer coincidir, puede reemplazar con una cadena vacía y luego ejecutar su coincidencia en el segundo paso. Si desea reemplazar, primero puede reemplazar las cadenas que se ignorarán con algo distintivo, por ejemplo, rodeando sus dígitos con una cadena de ancho fijo de
@@@
. Después de este reemplazo, es libre de reemplazar lo que realmente deseaba, luego tendrá que revertir sus@@@
cadenas distintivas .2. Lookarounds.
Su publicación original demostró que comprende cómo excluir una sola condición utilizando métodos alternativos. Dijiste que C # es genial para esto, y tienes razón, pero no es la única opción. Los tipos de expresiones regulares de .NET que se encuentran en C #, VB.NET y Visual C ++, por ejemplo, así como el
regex
módulo aún experimental para reemplazarre
en Python, son los únicos dos motores que conozco que admiten la búsqueda hacia atrás de ancho infinito. Con estas herramientas, una condición en una mirada hacia atrás puede encargarse de mirar no solo hacia atrás, sino también al partido y más allá del partido, evitando la necesidad de coordinar con un mirar hacia adelante. ¿Más condiciones? Más revisiones.Reciclando la expresión regular que tenía para s3 en C #, todo el patrón se vería así.
Pero a estas alturas ya sabes que no estoy recomendando esto, ¿verdad?
Eliminaciones
@HamZa y @Jerry han sugerido que mencione un truco adicional para los casos en los que solo busca eliminar
WhatYouWant
. ¿Recuerdas que la receta para combinarWhatYouWant
(capturarla en el Grupo 1) eras1|s2|s3|(WhatYouWant)
, verdad? Para eliminar todas las instancias deWhatYouWant
, cambie la expresión regular aPara la cadena de reemplazo, usa
$1
. Lo que sucede aquí es que para cada instancias1|s2|s3
que coincide, el reemplazo$1
reemplaza esa instancia por sí misma (referenciada por$1
). Por otro lado, cuandoWhatYouWant
coincide, se reemplaza por un grupo vacío y nada más y, por lo tanto, se elimina. Vea esta demostración , gracias @HamZa y @Jerry por sugerir esta maravillosa adición.Reemplazos
Esto nos lleva a los reemplazos, de los que hablaré brevemente.
(*SKIP)(*F)
variación mencionada anteriormente para que coincida exactamente con lo que desea y realice un reemplazo directo.¡Que te diviertas!
¡No, espera, hay más!
Ah, no, lo guardaré para mis memorias en veinte volúmenes, que se publicarán la próxima primavera.
fuente
Tarzan
, pero no cuando esté entre comillas dobles. La/no|no|(yes)/
expresión regular : truco sería algo como:/"[^"]*"|Tarzan/
(ignorando los caracteres escapados). Esto funcionará para muchos casos, pero falla por completo cuando se aplica a la siguiente texto JavaScript válida:var bug1 = 'One " quote here. Should match this Tarzan'; var bug2 = "Should not match this Tarzan";
. El truco de Rex solo funciona cuando TODAS las estructuras posibles coinciden; en otras palabras, es necesario analizar completamente el texto para garantizar el 100% de precisión.var bug1 = /"[^"]*"|(Tarzan)/gi;
y tuvo el mismo efecto (y este segundo ejemplo ciertamente no es un caso límite). Hay muchos más ejemplos que podría citar en los que esta técnica no funciona de manera confiable.(?<!\\)"(?:\\"|[^"\r\n])*+"
No tira de las armas grandes a menos que tenga una razón. El principio de la solución sigue siendo válido. Si no podemos expresar un patrón para poner en el lado izquierdo, esa es una historia diferente, necesitamos una solución diferente. Pero la solución hace lo que anuncia.Haga tres coincidencias diferentes y maneje la combinación de las tres situaciones utilizando lógica condicional en el programa. No necesita manejar todo en una expresión regular gigante.
EDITAR: permítanme expandirme un poco porque la pregunta se volvió más interesante :-)
La idea general que está tratando de capturar aquí es hacer coincidir con un cierto patrón de expresiones regulares, pero no cuando hay ciertos otros patrones (podría haber cualquier número) presentes en la cadena de prueba. Afortunadamente, puede aprovechar su lenguaje de programación: mantenga las expresiones regulares simples y solo use un condicional compuesto. Una mejor práctica sería capturar esta idea en un componente reutilizable, así que creemos una clase y un método que lo implemente:
Entonces, arriba, configuramos la cadena de búsqueda (los cinco dígitos), múltiples cadenas de excepción (su s1 , s2 y s3 ), y luego intentamos hacer coincidir varias cadenas de prueba. Los resultados impresos deben ser los que se muestran en los comentarios junto a cada cadena de prueba.
fuente
Su requisito de que no esté dentro de los parens es imposible de satisfacer en todos los casos. Es decir, si de alguna manera puede encontrar un
(
a la izquierda y)
a la derecha, no siempre significa que está dentro de los parientes. P.ej.(....) + 55555 + (.....)
- no dentro de los parientes todavía hay(
y)
a izquierda y derechaAhora puede pensar que es inteligente y buscar
(
a la izquierda solo si no se encuentra)
antes y viceversa a la derecha. Esto no funcionará para este caso:((.....) + 55555 + (.....))
- Parens interiores aunque hay cierres)
ya(
izquierda y derecha.Es imposible saber si está dentro de los parens usando regex, ya que regex no puede contar cuántos parens se han abierto y cuántos cerrados.
Considere esta tarea más fácil: usando expresiones regulares, averigüe si todos los parens (posiblemente anidados) en una cadena están cerrados, es decir, para todos los
(
que necesita encontrar)
. Descubrirá que es imposible de resolver y si no puede resolverlo con expresiones regulares, entonces no puede averiguar si una palabra está dentro de los paréntesis en todos los casos, ya que no puede averiguar en alguna posición en la cadena si todos los anteriores(
tienen un correspondiente)
.fuente
Hans, si no te importa, usé la lavadora de tu vecino llamada perl :)
Editado: debajo de un pseudo código:
Dado el archivo input.txt:
Y el script validator.pl:
Ejecución:
fuente
No estoy seguro de si esto lo ayudaría o no, pero estoy brindando una solución considerando las siguientes suposiciones:
Sin embargo, consideré también lo siguiente:
if(
bloques.Ok, aquí está la solución:
Usé C # y con él MEF (Microsoft Extensibility Framework) para implementar los analizadores configurables. La idea es usar un solo analizador para analizar y una lista de clases de validación configurables para validar la línea y devolver verdadero o falso según la validación. Luego, puede agregar o eliminar cualquier validador en cualquier momento o agregar nuevos si lo desea. Hasta ahora ya he implementado para S1, S2 y S3 que mencionaste, verifica las clases en el punto 3. Tienes que agregar clases para s4, s5 si lo necesitas en el futuro.
Primero, cree las interfaces:
Luego viene el lector y verificador de archivos:
Luego viene la implementación de las fichas individuales, los nombres de las clases se explican por sí mismos, por lo que no creo que necesiten más descripciones.
El programa -
Para la prueba, tomé el archivo de muestra de @ Tiago,
Test.txt
que tenía las siguientes líneas:Da la salida -
No sé si esto te ayudaría o no, me divertí mucho jugando con él .... :)
La mejor parte de esto es que, para agregar una nueva condición, todo lo que tiene que hacer es proporcionar una implementación de
IPatternMatcher
, se llamará automáticamente y, por lo tanto, se validará.fuente
Igual que @ zx81
(*SKIP)(*F)
pero con el uso de una aserción de anticipación negativa.MANIFESTACIÓN
En Python, lo haría fácilmente así,
Salida:
fuente