Frage

Ich habe eine solche Saite

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

Ich möchte es tokenisieren, kann es aber nicht einfach in die Leerzeichen aufteilen.Ich habe mir einen etwas hässlichen Parser ausgedacht, der funktioniert, aber ich frage mich, ob jemand ein eleganteres Design hat.

Das ist übrigens in C#.

BEARBEITEN: Meine hässliche Version ist zwar hässlich, aber O(N) und möglicherweise tatsächlich schneller als die Verwendung eines RegEx.

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();            
}
War es hilfreich?

Lösung

Der Computer Begriff für das, was Sie tun, ist lexikalische Analyse ; gelesen, dass für eine gute Zusammenfassung dieser gemeinsamen Aufgabe.

Basierend auf Ihrem Beispiel, ich nehme an, dass Sie Leerzeichen wollen, um Ihre Worte zu trennen, aber Sachen in Anführungszeichen sollten ohne die Anführungszeichen als ein „Wort“ behandelt werden.

Der einfachste Weg, dies zu tun, ist ein Wort als regulärer Ausdruck definiert werden:

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

Dieser Ausdruck besagt, dass ein „Wort“ ist entweder (1) Nicht-Zitat, das kein Leerzeichen Text Leerzeichen umgeben ist, oder (2) nicht-Zitat Text in Anführungszeichen (von einigen Leerzeichen folgt). Beachten Sie die Verwendung von einfangenden Klammern den gewünschten Text zu markieren.

mit dieser Regex Bewaffnete, Ihr Algorithmus ist einfach: Sie Ihren Text für das nächste „Wort“ suchen, wie durch die Erfassung von Klammern definiert, und gibt es zurück. Wiederholen Sie das, bis Sie laufen aus „Worte“.

Hier ist die einfachste Bit-Code zu arbeiten ich tun konnte, in VB.NET. Beachten Sie, dass wir müssen prüfen, beide Gruppen für Daten, da es zwei Sätze von einfangenden Klammern.

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

Anmerkung 1: Wills Antwort, oben, ist die gleiche Idee wie dieser. Hoffentlich wird diese Antwort erklärt die Details hinter der Szene ein wenig besser:)

Andere Tipps

Das Microsoft.VisualBasic.FileIO Namespace (in Microsoft.VisualBasic.dll) ein TextFieldParser Sie verwenden können, auf Platz delimeted Text zu teilen. Es behandelt Strings in Anführungszeichen (das heißt, "das ist ein Token" thisistokentwo) gut.

Beachten Sie, nur weil die DLL sagt VisualBasic- bedeutet nicht nur in einem VB-Projekt verwenden können. Sein Teil des gesamten Rahmen.

Es ist die Zustandsmaschine Ansatz.

    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);
        }
    }

Es kann leicht für Dinge wie verschachtelte Zitate erweitert werden und zu entkommen. Wiederkehrende als IEnumerable<string> ermöglicht Ihren Code nur so viel zu analysieren, wie Sie benötigen. Es gibt keine wirklichen Nachteile zu dieser Art von faulen Ansatz als Strings sind unveränderlich, so dass Sie wissen, dass input wird nicht ändern, bevor Sie das Ganze analysiert haben.

Siehe auch: http://en.wikipedia.org/wiki/Automata-Based_Programming

Sie können auch in reguläre Ausdrücke zu suchen. Das könnte Ihnen helfen. Hier wird eine Probe abgezockt von 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 stimmt - reguläre Ausdrücke verwenden. Regex.Split können für Ihre Bedürfnisse prägnanter sein.

  

[^ \ t] + \ t | "[^"] + "\ t

mit der Regex sieht definitiv wie die beste Wette, aber dieses nur die gesamte Zeichenfolge zurückgibt. Ich versuche, es zu zwicken, aber nicht viel Glück so weit.

string[] tokens = System.Text.RegularExpressions.Regex.Split(this.BuildArgs, @"[^\t]+\t|""[^""]+""\t");
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top