Pregunta

He cadena como esta

 /c SomeText\MoreText "Some Text\More Text\Lol" SomeText

Quiero acortar el que, sin embargo, yo no sólo pueden dividir en los espacios.Me he encontrado con algo feo analizador que funciona, pero me pregunto si alguien tiene un diseño más elegante.

Esto es en C# por cierto.

EDITAR: Mi versión "fea", mientras que feo, es O(N) y en realidad puede ser más rápido que el uso de una Expresión regular.

private string[] tokenize(string input)
{
    string[] tokens = input.Split(' ');
    List<String> output = new List<String>();

    for (int i = 0; i < tokens.Length; i++)
    {
        if (tokens[i].StartsWith("\""))
        {
            string temp = tokens[i];
            int k = 0;
            for (k = i + 1; k < tokens.Length; k++)
            {
                if (tokens[k].EndsWith("\""))
                {
                    temp += " " + tokens[k];
                    break;
                }
                else
                {
                    temp += " " + tokens[k];
                }
            }
            output.Add(temp);
            i = k + 1;
        }
        else
        {
            output.Add(tokens[i]);
        }
    }

    return output.ToArray();            
}
¿Fue útil?

Solución

El término ordenador para lo que estamos haciendo es análisis léxico;leído que para un buen resumen de esta tarea común.

Basado en el ejemplo, supongo que quieres un espacio en blanco para separar las palabras, pero las cosas entre comillas, debe ser tratada como una "palabra" sin las comillas.

La manera más sencilla de hacer esto es definir la palabra como una expresión regular:

([^"^\s]+)\s*|"([^"]+)"\s*

Esta expresión indica que una "palabra" es (1) no cita, no el espacio en blanco de texto rodeado por un espacio en blanco, o (2) no cita el texto entre comillas (seguido por algunos espacios en blanco).Nota el uso de la captura de paréntesis para resaltar el texto deseado.

Armado con esa expresión, el algoritmo es simple:la búsqueda de su texto por el siguiente "palabra", como se define por la captura de los paréntesis, y la devuelva.Repetir esto hasta que se acabe la de "palabras".

Aquí es el más simple de bits de código de trabajo que se me ocurrió, en VB.NET.Tenga en cuenta que tenemos que comprobar ambos los grupos de datos, ya que hay dos conjuntos de captura de paréntesis.

Dim token As String
Dim r As Regex = New Regex("([^""^\s]+)\s*|""([^""]+)""\s*")
Dim m As Match = r.Match("this is a ""test string""")

While m.Success
    token = m.Groups(1).ToString
    If token.length = 0 And m.Groups.Count > 1 Then
        token = m.Groups(2).ToString
    End If
    m = m.NextMatch
End While

Nota 1: La respuesta anterior, es la misma idea como esta.Espero que esta respuesta explica los detalles detrás de la escena un poco mejor :)

Otros consejos

El De Microsoft.VisualBasic.FileIO espacio de nombres (en Microsoft.VisualBasic.dll) tiene un objeto textfieldparser puede utilizar para dividir el espacio delimeted de texto.Se encarga de las cadenas entre comillas (es decir, "esto es una señal" thisistokentwo) bien.

Nota, simplemente debido a que el DLL dice VisualBasic no significa que sólo se puede utilizar en un proyecto de VB.Su parte de todo el Marco.

No es el estado de la máquina de enfoque.

    private enum State
    {
        None = 0,
        InTokin,
        InQuote
    }

    private static IEnumerable<string> Tokinize(string input)
    {
        input += ' '; // ensure we end on whitespace
        State state = State.None;
        State? next = null; // setting the next state implies that we have found a tokin
        StringBuilder sb = new StringBuilder();
        foreach (char c in input)
        {
            switch (state)
            {
                default:
                case State.None:
                    if (char.IsWhiteSpace(c))
                        continue;
                    else if (c == '"')
                    {
                        state = State.InQuote;
                        continue;
                    }
                    else
                        state = State.InTokin;
                    break;
                case State.InTokin:
                    if (char.IsWhiteSpace(c))
                        next = State.None;
                    else if (c == '"')
                        next = State.InQuote;
                    break;
                case State.InQuote:
                    if (c == '"')
                        next = State.None;
                    break;
            }
            if (next.HasValue)
            {
                yield return sb.ToString();
                sb = new StringBuilder();
                state = next.Value;
                next = null;
            }
            else
                sb.Append(c);
        }
    }

Puede ser fácilmente extendido para cosas como citas anidadas y escape.Regresando como IEnumerable<string> permite que el código sólo analice tanto como usted necesita.No hay ningún real desventajas de ese tipo de enfoque perezoso como las cadenas son inmutables, así que usted sabe que input no va a cambiar antes de haber analizado toda la cosa.

Ver: http://en.wikipedia.org/wiki/Automata-Based_Programming

También es posible que desee ver en las expresiones regulares.Que podría ayudarle.He aquí una muestra de lo arrancó de MSDN...

using System;
using System.Text.RegularExpressions;

public class Test
{

    public static void Main ()
    {

        // Define a regular expression for repeated words.
        Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b",
          RegexOptions.Compiled | RegexOptions.IgnoreCase);

        // Define a test string.        
        string text = "The the quick brown fox  fox jumped over the lazy dog dog.";

        // Find matches.
        MatchCollection matches = rx.Matches(text);

        // Report the number of matches found.
        Console.WriteLine("{0} matches found in:\n   {1}", 
                          matches.Count, 
                          text);

        // Report on each match.
        foreach (Match match in matches)
        {
            GroupCollection groups = match.Groups;
            Console.WriteLine("'{0}' repeated at positions {1} and {2}",  
                              groups["word"].Value, 
                              groups[0].Index, 
                              groups[1].Index);
        }

    }

}
// The example produces the following output to the console:
//       3 matches found in:
//          The the quick brown fox  fox jumped over the lazy dog dog.
//       'The' repeated at positions 0 and 4
//       'fox' repeated at positions 20 and 25
//       'dog' repeated at positions 50 and 54

Craig es correcto el uso de expresiones regulares. Regex.Split puede ser más conciso para sus necesidades.

[^ ]+ |"[^"]+"

el uso de la Expresión regular que sin duda parece la mejor apuesta, sin embargo, esto sólo devuelve la cadena entera.Estoy tratando de ajustar, pero no mucha suerte hasta ahora.

string[] tokens = System.Text.RegularExpressions.Regex.Split(this.BuildArgs, @"[^\t]+\t|""[^""]+""\t");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top