regular one how especiales escape classes caracteres c# regex

one - regex reference c#



Regex-Escape escape personajes (3)

Mi problema es bastante complejo, pero puede reducirse a un simple ejemplo.

Estoy escribiendo un lenguaje de consulta personalizado donde los usuarios pueden ingresar cadenas que analizo a LinQ Expressions.

Lo que me gustaría poder hacer es dividir cadenas por el carácter * , a menos que se escape correctamente.

Input Output Query Description "*//*" --> { "*", "//", "*" } -- contains a ''/' "*///**" --> { "*", "///*", "*" } -- contains ''/*'' "*/**" --> { "*", "/*", "*" } -- contains ''*'' (works now)

No me importa Regex.Split devolver cadenas vacías, pero termino con esto:

Regex.Split(@"*//*", @"(?<!//)(/*)") --> {"", "*", "//*"}

Como puede ver, lo he intentado con un aspecto negativo, lo que funciona para todos mis casos, excepto este. También he probado Regex.Escape , pero sin suerte.

Obviamente, mi problema es que estoy buscando /* , que //* coincide. Pero en este caso, // es otra secuencia escapada.

Cualquier solución no necesariamente tiene que involucrar un Regex.


Creo que es mucho más fácil de emparejar que de dividir, especialmente porque no estás eliminando nada de la cadena inicial. Entonces, ¿qué hacer coincidir? Todo excepto un sin escaparse * .

¿Como hacer eso? Con el regex siguiente:

@"(?:[^*//]+|//.)+|/*"

(?:[^*//]+|//.)+ coincide con todo lo que no es un * , o cualquier personaje escapado. No hay necesidad de ningún lookaround.

/* coincidirá con el separador.

En codigo:

using System; using System.Text.RegularExpressions; using System.Linq; public class Test { public static void Main() { string[] tests = new string[]{ @"*//*", @"*///**", @"*/**", }; Regex re = new Regex(@"(?:[^*//]+|//.)+|/*"); foreach (string s in tests) { var parts = re.Matches(s) .OfType<Match>() .Select(m => m.Value) .ToList(); Console.WriteLine(string.Join(", ", parts.ToArray())); } } }

Salida:

*, //, * *, ///*, * *, /*, *

demo ideone


Me di cuenta de que una solución de análisis simple, no regex sería una buena adición a esta pregunta.

Podía leer esto significativamente más rápido de lo que podía entender cualquiera de esas expresiones regulares. Esto también facilita la fijación de esquinas inesperadas. La lógica se establece directamente.

public static String[] splitOnDelimiterWithEscape(String toSplit, char delimiter, char escape) { List<String> strings = new ArrayList<>(); char[] chars = toSplit.toCharArray(); String sub = ""; for(int i = 0 ; i < chars.length ; i++) { if(chars[i] == escape) { sub += (i+1 < chars.length) ? chars[++i] : ""; //assign whatever char is after the escape to the string. This essentially makes single escape character non-existent. It just forces the next character to be literal. If the escape is at end, then we just ignore it //this is the simplest implementation of the escape. If escaping certain characters should have //special behaviour it should be implemented here. //You could even pass a Map mapping escape characters, to literal characters to make this even //more general. } else if(chars[i] == delimiter) { strings.add(sub); //Found delimiter. So we split. sub = ""; } else { sub += chars[i]; //nothing special. Just append to current string. } } strings.add(sub); //end of string is a boundary. Must include. return strings.toArray(new String[strings.size()]); }

ACTUALIZACIÓN: Estoy un poco confundido acerca de la pregunta ahora en realidad. La división, como siempre lo he sabido, no incluye la delimitación (pero parece que sí lo hacen los ejemplos). Si desea que los delimitadores existan en la matriz, en su propia ranura, la modificación de esto es bastante simple. (Lo dejaré como un ejercicio para el lector como evidencia de la capacidad de mantenimiento del código)


Se me ocurrió esta expresión regular (?<=(?:^|[^//])(?:////)*)(/*) .

Explicación:

Simplemente coloca en la lista blanca las situaciones que pueden suceder antes * y estas son:

  • inicio de la cadena ^
  • no / - [^//]
  • (no / o principio de la cadena) y luego el número par de / - (^|[^//])(////)*

Código de prueba y ejemplos:

string[] tests = new string[]{ @"*//*", @"*///**", @"*/**", @"test/**test2", }; Regex re = new Regex(@"(?<=(?:^|[^//])(?:////)*)(/*)"); foreach (string s in tests) { string[] m = re.Split( s ); Console.WriteLine(String.Format("{0,-20} {1}", s, String.Join(", ", m.Where(x => !String.IsNullOrEmpty(x))))); }

Resultado:

*//* *, //, * *///** *, ///*, * */** *, /*, * test/**test2 test/*, *, test2