Domanda

Ho stringa come questa

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

Voglio dividere, tuttavia non posso dividere sugli spazi.Sono venuto su con un po ' brutti parser che funziona, ma mi chiedo se qualcuno ha un disegno più elegante.

Questo è in C# btw.

EDIT: La mia versione brutta, mentre il brutto, è O(N) e può effettivamente essere più veloce rispetto all'utilizzo di un'espressione regolare.

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();            
}
È stato utile?

Soluzione

Il termine computer e per quello che stai facendo è analisi lessicale;leggere che per una buona sintesi di questo lavoro comune.

Basato sul tuo esempio, sto cercando di indovinare che si desidera che gli spazi per separare le parole, ma roba tra virgolette devono essere trattati come una "parola", senza le virgolette.

Il modo più semplice per farlo è quello di definire una parola come un'espressione regolare:

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

Questa espressione afferma che una "parola" è o (1) la non citazione, che non siano spazi di testo, circondato da spazi vuoti, o (2) non citare il testo racchiuso tra virgolette (seguita da alcuni spazi vuoti).Nota l'uso di catturare le parentesi per evidenziare il testo desiderato.

Armati con la regex, il tuo algoritmo è semplice:ricerca il tuo testo per il prossimo "parola" come l'ha definito l'acquisizione di parentesi e ritorno.Ripetere fino a quando si esegue fuori di "parole".

Ecco il bit più semplice del codice di lavoro ho potuto venire con, in VB.NET.Nota che dobbiamo verificare entrambi gruppi per i dati, dato che ci sono due set di acquisizione di parentesi.

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: Will di risposta, di cui sopra, è la stessa idea come questa.Speriamo che questa risposta spiega i dettagli dietro la scena un po ' meglio :)

Altri suggerimenti

La Microsoft.VisualBasic.FileIO spazio dei nomi (in Microsoft.VisualBasic.dll) ha un TextFieldParser è possibile utilizzare per dividere lo spazio delimitata testo.Esso gestisce le stringhe tra virgolette (es. "questo è un segno" thisistokentwo) bene.

Nota, solo perché la DLL dice VisualBasic non significa che si può usare solo in un progetto di visual basic.La sua parte di tutto il Quadro.

C'è una macchina a stati approccio.

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

Esso può essere esteso facilmente per le cose come le citazioni nidificate e scappa.Tornando IEnumerable<string> consente al codice solo analizzare quanto hai bisogno.Non ci sono i veri aspetti negativi di questo tipo di approccio "pigro" come stringhe sono immutabili e quindi sai che input non cambierà prima di aver analizzato il tutto.

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

Si potrebbe anche voler guardare le espressioni regolari.Che potrebbe aiutarti.Ecco un esempio di ripped off da 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 è giusto usare le espressioni regolari. Regex.Split può essere più concisa per le tue esigenze.

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

utilizza la Regex sicuramente appare come la migliore scommessa, tuttavia questo restituisce la stringa intera.Sto cercando di modificarlo, ma non molta fortuna finora.

string[] tokens = System.Text.RegularExpressions.Regex.Split(this.BuildArgs, @"[^\t]+\t|""[^""]+""\t");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top