Najlepszy sposób do analizowania spacjami tekstu

głosy
7

Mam ciąg takiego

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

Chcę tokenize, jednak nie mogę po prostu podzielone na przestrzeniach. Mam wymyślić nieco brzydki parsera, który działa, ale zastanawiam się, czy ktoś ma bardziej elegancki wygląd.

Jest to w C # btw.

EDIT: Mam brzydkie wersja, natomiast brzydka, jest O (N) i może być faktycznie szybciej niż przy użyciu wyrażenia regularnego.

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();            
}
Utwórz 10/09/2008 o 19:00
źródło użytkownik
W innych językach...                            


6 odpowiedzi

głosy
16

Termin komputer za to, co robisz jest analiza leksykalna ; Czytałem, że na dobre podsumowanie tego wspólnego zadania.

Opierając się na swoim przykładzie, zgaduję, że chcesz spacji aby rozdzielić słowa, ale rzeczy w cudzysłowach powinny być traktowane jako „słowo” bez cudzysłowów.

Najprostszym sposobem, aby to zrobić, aby zdefiniować słowo jako wyrażenie regularne:

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

Wyrażenie to stanowi, że „słowo” jest albo (1) non-cytat, tekst bez spacji otoczony białymi znakami, lub (2) tekst nie cytat w cudzysłowie (po jakimś spacji). Zwróć uwagę na użycie przechwytywanie nawiasy, aby podświetlić żądany tekst.

Uzbrojony w tym regex, Twój algorytm jest prosty: szukaj tekstu do następnej „słowo”, jak określono przez przechwytywanie nawiasach, i zwraca go. Powtarzam, że dopóki nie zabraknie „Słowa”.

Oto najprostszy kawałek kodu mogłem wymyślić pracuje w VB.NET. Należy pamiętać, że musimy sprawdzić obie grupy danych, ponieważ istnieją dwa zestawy przechwytywania nawiasach.

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

Uwaga 1: Will odpowiedź powyżej, jest taki sam pomysł jak ten. Mam nadzieję, że ta odpowiedź wyjaśnia szczegóły za sceną trochę lepiej :)

Odpowiedział 10/09/2008 o 19:20
źródło użytkownik

głosy
8

Microsoft.VisualBasic.FileIO nazw (w Microsoft.VisualBasic.dll) ma TextFieldParser można używać do podziału na przestrzeń delimeted tekstu. Obsługuje ciągi w cudzysłowie (czyli „to jeden żeton” thisistokentwo) dobrze.

Uwaga, tylko dlatego, że DLL mówi VisualBasic nie oznacza, że ​​można go używać tylko w projekcie VB. Jego częścią całego Framework.

Odpowiedział 10/09/2008 o 19:03
źródło użytkownik

głosy
3

Jest to podejście Stan maszyny.

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

To może być łatwo rozszerzona na takie rzeczy jak zagnieżdżonych cytatów i ucieczkę. Wracając jak IEnumerable<string>pozwala kodu do analizowania jedynie tyle, ile trzeba. Nie ma żadnych prawdziwych downsides do tego rodzaju leniwe podejście jako ciągi są niezmienne, więc wiesz, że inputnie ulegnie zmianie, dopóki nie zostanie przeanalizowany całość.

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

Odpowiedział 10/09/2008 o 21:12
źródło użytkownik

głosy
0

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

używając regex zdecydowanie wygląda najlepiej, jednak tym razem po prostu zwraca cały ciąg. Staram się go podkręcić, ale nie za dużo szczęścia do tej pory.

string[] tokens = System.Text.RegularExpressions.Regex.Split(this.BuildArgs, @"[^\t]+\t|""[^""]+""\t");
Odpowiedział 10/09/2008 o 20:12
źródło użytkownik

głosy
0

Craig ma rację - używać wyrażeń regularnych. Regex.Split może być bardziej zwięzły do swoich potrzeb.

Odpowiedział 10/09/2008 o 19:15
źródło użytkownik

głosy
0

Warto również zajrzeć do wyrażeń regularnych. To może pomóc. Oto przykład oszukany z 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
Odpowiedział 10/09/2008 o 19:03
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more