2010-03-30 27 views
10

Necesito obtener todas las subcadenas de la cadena.
Por ejemplo:
Buscar todas las subcadenas entre dos cadenas

StringParser.GetSubstrings("[start]aaaaaa[end] wwwww [start]cccccc[end]", "[start]", "[end]"); 

que devuelve 2 cadena "aaaaaa" y "cccccc" Supongamos que tenemos un solo nivel de anidamiento. No estoy seguro acerca de regexp, pero creo que será útil.

+0

1 nivel de los medios de anidación '[Inicio] xxx [iniciar] yyy [final] zzz [final]' es posible? – kennytm

+0

Es impossibe. –

+0

Si está haciendo esto para analizar HTML o XML, hay formas mejores ... – Randolpho

Respuesta

31
private IEnumerable<string> GetSubStrings(string input, string start, string end) 
{ 
    Regex r = new Regex(Regex.Escape(start) + "(.*?)" + Regex.Escape(end)); 
    MatchCollection matches = r.Matches(input); 
    foreach (Match match in matches) 
     yield return match.Groups[1].Value; 
} 
+0

Exacto lo que necesito, gracias –

+3

+1 - especialmente para el Regex.Escape :) –

2

Necesitará definir mejor las reglas que rigen sus necesidades de coincidencia. Al construir cualquier tipo de código coincidente o de búsqueda, debe tener una opinión clara sobre qué entradas anticipa y qué salidas necesita producir. Es muy fácil producir código defectuoso si no se tienen en cuenta estas cuestiones. Dicho esto ...

Debería poder usar expresiones regulares. El anidamiento puede hacerlo un poco más complicado pero aún factible (según lo que espere encontrar en escenarios anidados). Algo como debería comenzar:

var start = "[start]"; 
var end = "[end]"; 
var regEx = new Regex(String.Format("{0}(.*){1}", Regex.Escape(start), Regex.Escape(end))); 
var source = "[start]aaaaaa[end] wwwww [start]cccccc[end]"; 
var matches = regEx.Match(source); 

Debe ser trivial para ajustar el código anterior en una función adecuada a sus necesidades.

2

Se puede utilizar una expresión regular, pero recuerde que debe llamar Regex.Escape en sus argumentos:

public static IEnumerable<string> GetSubStrings(
    string text, 
    string start, 
    string end) 
{ 
    string regex = string.Format("{0}(.*?){1}", 
     Regex.Escape(start), 
     Regex.Escape(end)); 

    return Regex.Matches(text, regex, RegexOptions.Singleline) 
     .Cast<Match>() 
     .Select(match => match.Groups[1].Value); 
} 

También he añadido la opción SingleLine de modo que coincidirá incluso si hay nuevas líneas en el texto.

4

Aquí hay una solución que no usa expresiones regulares y no tiene en cuenta la anidación.

public static IEnumerable<string> EnclosedStrings(
    this string s, 
    string begin, 
    string end) 
{ 
    int beginPos = s.IndexOf(begin, 0); 
    while (beginPos >= 0) 
    { 
     int start = beginPos + begin.Length; 
     int stop = s.IndexOf(end, start); 
     if (stop < 0) 
      yield break; 
     yield return s.Substring(start, stop - start); 
     beginPos = s.IndexOf(begin, stop+end.Length); 
    }   
} 
0

estaba aburrido, y por lo tanto hice un punto de referencia micro inútil, que "demuestra" (en mi conjunto de datos, que tiene cadenas de hasta 7k de personajes y <b> etiquetas para los parámetros de inicio/final) mi sospecha de que juharr La solución de es la más rápida de las tres en general.

resultados (1000000 iteraciones * 20 casos de prueba):

juharr: 6371ms 
Jake: 6825ms 
Mark Byers: 82063ms 

NOTA: Compilado de expresiones regulares no acelerar las cosas mucho en mi conjunto de datos.

0

libre de Regex método:

public static List<string> extract_strings(string src, string start, string end) 
{ 
    if (src.IndexOf(start) > 0) 
    { 
     src = src.Substring(src.IndexOf(start)); 
    } 
    string[] array1 = src.Split(new[] { start }, StringSplitOptions.None); 
    List<string> list = new List<string>(); 
    foreach (string value in array1) 
    { 
     if (value.Contains(end)) 
     { 
      list.Add(value.Split(new[] { end }, StringSplitOptions.None)[0]); 
     } 
    } 
    return list; 
} 
Cuestiones relacionadas