2008-09-10 78 views
7

我有串這樣最佳方式分隔的文本

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

我要來標記它,但是我不能只拆分的空間。我想出了一些有點難懂的解析器,但我想知道是否有人有更優雅的設計。

這是C#中的btw。

編輯:我的醜陋的版本,雖然醜陋,是O(N),實際上可能比使用正則表達式更快。

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();    
} 
+0

請告訴我們更多關於你想實現什麼,包括你爲什麼不能在空間分割。然後我們可以根據您的情況定製我們的答案。 – 2008-09-10 18:11:57

回答

16

你在做什麼的電腦術語是lexical analysis;請仔細閱讀,以便對此常見任務進行總結。

根據你的例子,我猜你想用空格來分隔你的單詞,但引號中的內容應該被視爲沒有引號的「單詞」。

要做到這一點,最簡單的方法是定義一個字作爲一個正則表達式:

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

這種表達指出,一個「字」是:(1)無報價,非空白文本包圍空格,或(2)用引號括起來的非引號文本(後跟一些空格)。請注意使用捕獲圓括號來突出顯示所需的文本。

用這個正則表達式武裝起來,你的算法很簡單:搜索你的文本,獲取由捕獲括號定義的下一個「單詞」,並返回它。重複,直到你用完「單詞」。

這是VB.NET中我能想到的最簡單的工作代碼。請注意,我們必須檢查兩個組的數據,因爲有兩組捕獲括號。

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 

注1:Will's上面的答案和這個答案是一樣的。希望這個答案說明了現場好一點:)

8

的Microsoft.VisualBasic.FileIO命名空間(在Microsoft.VisualBasic.dll中)有你可以用它來分割空間delimeted文本TextFieldParser。它處理引號內的字符串(即「這是一個令牌」thisistokentwo)。

請注意,只是因爲DLL說VisualBasic並不意味着你只能在VB項目中使用它。它是整個框架的一部分。

0

你也可能想看看正則表達式。這可能會幫助你。下面是一個示例從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 
0

[^ \ t] + \ t背後的細節| 「[^」] +「\ t

使用正則表達式肯定看起來像最好的選擇,但是這只是返回整個字符串,我正在努力調整它,但是到目前爲止沒有太多的運氣。

string[] tokens = System.Text.RegularExpressions.Regex.Split(this.BuildArgs, @"[^\t]+\t|""[^""]+""\t"); 
+0

這不起作用,因爲Regex.Split旨在基於分隔符而不是標記進行捕獲。使用Regex.Match來獲得所需的效果。 – 2008-09-11 19:17:36

3

有狀態機方法。

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

它可以很容易地擴展爲像嵌套引號和轉義的東西。以IEnumerable<string>返回,您的代碼只能根據需要進行解析。對於這種懶惰的方法,沒有任何實際的缺點,因爲字符串是不可變的,所以你知道input在你解析整個事物之前不會改變。

參見:http://en.wikipedia.org/wiki/Automata-Based_Programming