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:
(?:
(?!lazy)
.
)*
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)
grep, pero esta respuesta sí.