Expresiones regulares y negación de un grupo de caracteres completo

Estoy intentando algo que creo que debería ser bastante obvio para mí, pero no lo es. Estoy tratando de hacer coincidir una cadena que NO contiene una secuencia específica de caracteres. He intentado usar [^ab] , [^(ab)] , etc. para hacer coincidir cadenas que no contengan ‘a’s o’ b’s, o solo ‘a’s o solo’ b’s o ‘ba’ pero que no coincidan en ‘ab’. Los ejemplos que di no coincidirán con ‘ab’, es cierto, pero tampoco coincidirán con ‘a’ solo y los necesito. ¿Hay alguna forma simple de hacer esto?

Use un lookahead negativo:

 ^(?!.*ab).*$ 

ACTUALIZACIÓN: En los comentarios a continuación, dije que este enfoque es más lento que el dado en la respuesta de Peter . He realizado algunas pruebas desde entonces y descubrí que es un poco más rápido. Sin embargo, la razón para preferir esta técnica sobre la otra no es la velocidad, sino la simplicidad.

La otra técnica, que se describe aquí como token codicioso atemperado , es adecuada para problemas más complejos, como la coincidencia de texto delimitado donde los delimitadores se componen de varios caracteres (como HTML, como comenta Lucas a continuación ). Para el problema descrito en la pregunta, es excesivo.

Para cualquiera que esté interesado, probé con una gran cantidad de texto de Lorem Ipsum, contando el número de líneas que no contienen la palabra “quo”. Estas son las expresiones regulares que utilicé:

 (?m)^(?!.*\bquo\b).+$ (?m)^(?:(?!\bquo\b).)+$ 

Ya sea que busque coincidencias en todo el texto, o las divida en líneas y las combine individualmente, la búsqueda anclada supera consistentemente a la que está flotando.

El uso de una clase de caracteres como [^ab] coincidirá con un solo carácter que no esté dentro del conjunto de caracteres. (Con el ^ es la parte negadora).

Para hacer coincidir una cadena que no contenga la secuencia de varios caracteres ab , desea utilizar un lookahead negativo:

 ^(?:(?!ab).)+$ 

Y la expresión anterior disectada en el modo de comentario regex es:

 (?x) # enable regex comment mode ^ # match start of line/string (?: # begin non-capturing group (?! # begin negative lookahead ab # literal text sequence ab ) # end negative lookahead . # any single character ) # end non-capturing group + # repeat previous match one or more times $ # match end of line/string 

Sí, se llama lookahead negativo. Funciona así – (?!regex here) . Entonces abc(?!def) coincidirá con abc y no con def. Entonces coincidirá con abce, abc, abck, etc.

De manera similar, hay un avance positivo – (?=regex here) . Entonces abc(?=def) coincidirá con abc seguido de def.

También hay un aspecto negativo y positivo detrás – (? y (?<=regex here) respectivamente

Un punto a tener en cuenta es que la anticipación negativa es de ancho cero. Es decir, no cuenta como que haya necesitado espacio.

Por lo tanto, puede parecer que a(?=b)c coincidirá con "abc" pero no lo hará. Coincidirá con 'a', luego con la 'b' positiva hacia adelante, pero no avanzará hacia la cuerda. Luego intentará hacer coincidir la 'c' con 'b', lo que no funcionará. Del mismo modo ^a(?=b)b$ coincidirá con 'ab' y no con 'abb' porque las vistas son de ancho cero (en la mayoría de las implementaciones de expresiones regulares).

Más información en esta página

Usar una expresión regular como describiste es la manera más simple (hasta donde yo sé). Si quieres un rango, puedes usar [^ af].

La forma más simple es sacar por completo la negación de la expresión regular:

 if (!userName.matches("^([Ss]ys)?admin$")) { ... } 

abc (?! def) coincidirá con abc no seguido de def. Entonces coincidirá con abce, abc, abck, etc. ¿y si no quiero ni def ni xyz será abc (?! (Def) (xyz)) ???

Tenía la misma pregunta y encontré una solución:

 abc(?:(?!def))(?:(?!xyz)) 

Estos grupos sin conteo se combinan por “Y”, por lo que esto debería ser el truco. Espero eso ayude.

La expresión regular [^ (ab)] coincidirá, por ejemplo, con ‘ab ab ab ab’ pero no ‘ab’, porque coincidirá con la cadena ‘a’ o ‘b’.

¿Qué idioma / escenario tienes? ¿Puedes restar los resultados del conjunto original y solo igualar ab?

Si está utilizando GNU grep y está analizando la entrada, use el indicador ‘-v’ para invertir sus resultados y devolver todos los que no coincidan. Otras herramientas de expresiones regulares también tienen una función de “retorno no coincidente” también.

Si entiendo correctamente, quieres todo menos aquellos artículos que contienen ‘ab’ en cualquier lugar.

En este caso, simplemente podría evitar las expresiones regulares por completo e ir con algo como:

 if (StringToTest.IndexOf("ab") < 0) //do stuff 

Es probable que esto también sea mucho más rápido (una prueba rápida frente a expresiones regemáticas anteriores mostró que este método toma aproximadamente el 25% del tiempo del método de expresión regular). En general, si conozco la cadena exacta que estoy buscando, he encontrado que las expresiones regulares son excesivas. Como sabes que no quieres "ab", es una cuestión simple probar si la cadena contiene esa cadena, sin usar expresiones regulares.

Simplemente busca “ab” en la cadena y luego niega el resultado:

 !/ab/.test("bamboo"); // true !/ab/.test("baobab"); // false 

Parece más fácil y debería ser más rápido también.