Regex hasta pero sin incluir


81

Para expresiones regulares, ¿cuál es la sintaxis de búsqueda hasta pero sin incluir? Más o menos como:

Haystack:
The quick red fox jumped over the lazy brown dog

Expression:
.*?quick -> and then everything until it hits the letter "z" but do not include z

Respuestas:


161

La forma explícita de decir "buscar hasta Xpero sin incluir X" es:

(?:(?!X).)*

donde Xpuede ser cualquier expresión regular.

Sin embargo, en su caso, esto podría ser excesivo; aquí la forma más fácil sería

[^z]*

Esto coincidirá con cualquier cosa excepto zy, por lo tanto, se detendrá justo antes del siguiente z.

Así .*?quick[^z]*coincidirá The quick fox jumps over the la.

Sin embargo, tan pronto como tenga más de una letra simple a la que prestar atención, (?:(?!X).)*entra en juego, por ejemplo

(?:(?!lazy).)*- coincidir con cualquier cosa hasta el comienzo de la palabra lazy.

Esto está utilizando una afirmación de anticipación , más específicamente una anticipación negativa.

.*?quick(?:(?!lazy).)*coincidirá The quick fox jumps over the.

Explicación:

(?:        # Match the following but do not capture it:
 (?!lazy)  # (first assert that it's not possible to match "lazy" here
 .         # then match any character
)*         # end of group, zero or more repetitions.

Además, al buscar palabras clave, es posible que desee rodearlas con anclajes de límites de palabras: \bfox\bsolo coincidirá con la palabra completa, foxpero no con el zorro foxy.

Nota

Si el texto que se va a hacer coincidir también puede incluir saltos de línea, deberá establecer la opción "el punto coincide con todo" de su motor de expresiones regulares. Por lo general, puede lograrlo anteponiendo (?s)la expresión regular, pero eso no funciona en todos los motores de expresiones regulares (especialmente JavaScript).

Solución alternativa:

En muchos casos, también puede usar una solución más simple y legible que usa un cuantificador diferido. Al agregar un ?al *cuantificador, intentará hacer coincidir la menor cantidad de caracteres posible desde la posición actual:

.*?(?=(?:X)|$)

coincidirá con cualquier número de caracteres, deteniéndose justo antes X(que puede ser cualquier expresión regular) o al final de la cadena (si Xno coincide). Es posible que también deba configurar la opción "el punto coincide con todo" para que esto funcione. (Nota: agregué un grupo que no captura Xpara aislarlo de manera confiable de la alternancia)


+1 Muy buena respuesta, desafortunadamente no funciona grep, pero esta respuesta sí.
Alexandre Lavoie

@AlexandreLavoie: Interesante. ¿Por qué debería funcionar el otro y no este? Ambos usan aserciones de anticipación. ¿Quizás es solo por el (?:...)grupo que no capturó? ¿Funciona con ((?!X).)*?
Tim Pietzcker

1
Realmente no lo sé, no soy un experto en expresiones regulares ni grep. Estaba usando greppara filtrar solicitudes para solo una base de datos de mysql bin transformet en sql. Aquí está la bestia:grep -Po "(?s)use database_to_keep(.*?)(?=^use)" mysql-bin.000045.sql > filtered.sql
Alexandre Lavoie

Parece un conflicto de bash ya que cuando presioné la Uptecla, el último comando no es el que usé:grep -Po "(?s)use database_to_keep(.*?)(?:(?!^use).)*" mysql-bin.000045.sql > filtered.sql
Alexandre Lavoie

1
Buena edición, @Tim, solo agregue una $alternativa: reemplace .*?(?=X)con.*?(?=X|$)
Wiktor Stribiżew

15

Una sintaxis de expresiones regulares anticipadas puede ayudarlo a lograr su objetivo. Por lo tanto, una expresión regular para su ejemplo es

.*?quick.*?(?=z)

Y es importante notar la .*?coincidencia diferida antes de la (?=z)búsqueda anticipada : la expresión coincide con una subcadena hasta la primera aparición de la zletra.

Aquí hay un ejemplo de código C #:

const string text = "The quick red fox jumped over the lazy brown dogz";

string lazy = new Regex(".*?quick.*?(?=z)").Match(text).Value;
Console.WriteLine(lazy); // The quick red fox jumped over the la

string greedy = new Regex(".*?quick.*(?=z)").Match(text).Value;
Console.WriteLine(greedy); // The quick red fox jumped over the lazy brown dog

0

Prueba esto

(.*?quick.*?)z

3
Esto incluye la "z" en la coincidencia, que es precisamente lo que el autor de la pregunta quiere evitar. Quizás la expresión regular está destinada a ser un término en un '|' alternativa, y esa expresión regular alternativa se usa para realizar múltiples coincidencias. Si la "z" es el comienzo de una cadena que coincidiría con otro término en la alternativa, entonces esta coincidencia se perderá porque la "z" ya está consumida por la coincidencia actual.
Szczepan Hołyszewski
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.