El bucle:¿Cómo puedo acelerar este ciclo? ¿Hay una clase para reemplazar múltiples términos a la vez?
var pattern = _dict[key];
string before;
do
{
before = pattern;
foreach (var pair in _dict)
if (key != pair.Key)
pattern = pattern.Replace(string.Concat("{", pair.Key, "}"), string.Concat("(", pair.Value, ")"));
} while (pattern != before);
return pattern;
Simplemente hace una repetida de buscar y reemplazar en un manojo de llaves. El diccionario es solo <string,string>
.
Puedo ver 2 mejoras a esto.
- Cada vez que hacemos
pattern.Replace
vuelve a buscar desde el comienzo de la cadena. Sería mejor si cuando alcanzara el primer{
, simplemente miraría a través de la lista de claves para encontrar una coincidencia (quizás usando una búsqueda binaria), y luego reemplazaría la apropiada. - El bit
pattern != before
es cómo compruebo si se reemplazó algo durante esa iteración. Si la funciónpattern.Replace
devolvió cuántas sustituciones sucedieron, no las necesitaría.
Sin embargo ... Realmente no quiero escribir una gran clase de cosas desagradables para hacer todo eso. Este debe ser un escenario bastante común? ¿Hay alguna solución existente?
clase completa
Gracias a Elián Ebbing y ChrisWue.
class FlexDict : IEnumerable<KeyValuePair<string,string>>
{
private Dictionary<string, string> _dict = new Dictionary<string, string>();
private static readonly Regex _re = new Regex(@"{([_a-z][_a-z0-9-]*)}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public void Add(string key, string pattern)
{
_dict[key] = pattern;
}
public string Expand(string pattern)
{
pattern = _re.Replace(pattern, match =>
{
string key = match.Groups[1].Value;
if (_dict.ContainsKey(key))
return "(" + Expand(_dict[key]) + ")";
return match.Value;
});
return pattern;
}
public string this[string key]
{
get { return Expand(_dict[key]); }
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
foreach (var p in _dict)
yield return new KeyValuePair<string,string>(p.Key, this[p.Key]);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Ejemplo de Uso
class Program
{
static void Main(string[] args)
{
var flex = new FlexDict
{
{"h", @"[0-9a-f]"},
{"nonascii", @"[\200-\377]"},
{"unicode", @"\\{h}{1,6}(\r\n|[ \t\r\n\f])?"},
{"escape", @"{unicode}|\\[^\r\n\f0-9a-f]"},
{"nmstart", @"[_a-z]|{nonascii}|{escape}"},
{"nmchar", @"[_a-z0-9-]|{nonascii}|{escape}"},
{"string1", @"""([^\n\r\f\\""]|\\{nl}|{escape})*"""},
{"string2", @"'([^\n\r\f\\']|\\{nl}|{escape})*'"},
{"badstring1", @"""([^\n\r\f\\""]|\\{nl}|{escape})*\\?"},
{"badstring2", @"'([^\n\r\f\\']|\\{nl}|{escape})*\\?"},
{"badcomment1", @"/\*[^*]*\*+([^/*][^*]*\*+)*"},
{"badcomment2", @"/\*[^*]*(\*+[^/*][^*]*)*"},
{"baduri1", @"url\({w}([!#$%&*-\[\]-~]|{nonascii}|{escape})*{w}"},
{"baduri2", @"url\({w}{string}{w}"},
{"baduri3", @"url\({w}{badstring}"},
{"comment", @"/\*[^*]*\*+([^/*][^*]*\*+)*/"},
{"ident", @"-?{nmstart}{nmchar}*"},
{"name", @"{nmchar}+"},
{"num", @"[0-9]+|[0-9]*\.[0-9]+"},
{"string", @"{string1}|{string2}"},
{"badstring", @"{badstring1}|{badstring2}"},
{"badcomment", @"{badcomment1}|{badcomment2}"},
{"baduri", @"{baduri1}|{baduri2}|{baduri3}"},
{"url", @"([!#$%&*-~]|{nonascii}|{escape})*"},
{"s", @"[ \t\r\n\f]+"},
{"w", @"{s}?"},
{"nl", @"\n|\r\n|\r|\f"},
{"A", @"a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?"},
{"C", @"c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?"},
{"D", @"d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?"},
{"E", @"e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?"},
{"G", @"g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g"},
{"H", @"h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h"},
{"I", @"i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i"},
{"K", @"k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k"},
{"L", @"l|\\0{0,4}(4c|6c)(\r\n|[ \t\r\n\f])?|\\l"},
{"M", @"m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m"},
{"N", @"n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n"},
{"O", @"o|\\0{0,4}(4f|6f)(\r\n|[ \t\r\n\f])?|\\o"},
{"P", @"p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p"},
{"R", @"r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r"},
{"S", @"s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s"},
{"T", @"t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t"},
{"U", @"u|\\0{0,4}(55|75)(\r\n|[ \t\r\n\f])?|\\u"},
{"X", @"x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x"},
{"Z", @"z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z"},
{"Z", @"z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z"},
{"CDO", @"<!--"},
{"CDC", @"-->"},
{"INCLUDES", @"~="},
{"DASHMATCH", @"\|="},
{"STRING", @"{string}"},
{"BAD_STRING", @"{badstring}"},
{"IDENT", @"{ident}"},
{"HASH", @"#{name}"},
{"IMPORT_SYM", @"@{I}{M}{P}{O}{R}{T}"},
{"PAGE_SYM", @"@{P}{A}{G}{E}"},
{"MEDIA_SYM", @"@{M}{E}{D}{I}{A}"},
{"CHARSET_SYM", @"@charset\b"},
{"IMPORTANT_SYM", @"!({w}|{comment})*{I}{M}{P}{O}{R}{T}{A}{N}{T}"},
{"EMS", @"{num}{E}{M}"},
{"EXS", @"{num}{E}{X}"},
{"LENGTH", @"{num}({P}{X}|{C}{M}|{M}{M}|{I}{N}|{P}{T}|{P}{C})"},
{"ANGLE", @"{num}({D}{E}{G}|{R}{A}{D}|{G}{R}{A}{D})"},
{"TIME", @"{num}({M}{S}|{S})"},
{"PERCENTAGE", @"{num}%"},
{"NUMBER", @"{num}"},
{"URI", @"{U}{R}{L}\({w}{string}{w}\)|{U}{R}{L}\({w}{url}{w}\)"},
{"BAD_URI", @"{baduri}"},
{"FUNCTION", @"{ident}\("},
};
var testStrings = new[] { @"""str""", @"'str'", "5", "5.", "5.0", "a", "alpha", "url(hello)",
"url(\"hello\")", "url(\"blah)", @"\g", @"/*comment*/", @"/**/", @"<!--", @"-->", @"~=",
"|=", @"#hash", "@import", "@page", "@media", "@charset", "!/*iehack*/important"};
foreach (var pair in flex)
{
Console.WriteLine("{0}\n\t{1}\n", pair.Key, pair.Value);
}
var sw = Stopwatch.StartNew();
foreach (var str in testStrings)
{
Console.WriteLine("{0} matches: ", str);
foreach (var pair in flex)
{
if (Regex.IsMatch(str, "^(" + pair.Value + ")$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture))
Console.WriteLine(" {0}", pair.Key);
}
}
Console.WriteLine("\nRan in {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
Propósito
Para la construcción de las expresiones regulares complejos que pueden extenderse unos de otros. A saber, estoy tratando de implementar the css spec.
Sí, el valor contiene otros '{placeholders}'. No, no puedo estar seguro de no quedarme atascado en un ciclo infinito ... se suponía que la 'clave! = Par.clave 'lo impediría (reemplazando a w/de todos modos), pero como usted señaló, puede ser causado de otras maneras. Aunque no estoy demasiado preocupado ... si se crea un ciclo infinito, debería ser rápidamente evidente. – mpen
Una mejora adicional podría ser hacerlo recursivo en lugar de un ciclo. Suponiendo que el código de reemplazo anterior es una función llamada Reemplazar (s, re, dict), entonces 'return' ("+ Replace (_dict [matchKey], re, _dict) +") "'Dependiendo de la cantidad de anidamiento y la longitud de la cadena fuente, esto podría mejorar aún más las cosas. Tendría la "ventaja" de correr en un stackoverflow en lugar de un bucle infinito. También pre compilar la expresión regular podría ayudar. – ChrisWue
@ChrisWue: No creo que pueda hacerlo recursivo de esa manera (vea la pregunta actualizada). La forma en que lo tengo configurado, simplemente devuelve el patrón para una clave en particular, pero no aceptará una cadena de entrada ... en realidad ... con algunas modificaciones puedo hacerlo. Actualizando Q nuevamente. – mpen