【问题标题】:Best way to parse Space Separated Text解析空格分隔文本的最佳方法
【发布时间】:2010-09-08 11:10:24
【问题描述】:

我有这样的字符串

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

我想对其进行标记,但是我不能只在空格上进行拆分。我想出了一些丑陋的解析器,但我想知道是否有人有更优雅的设计。

顺便说一句,这是在 C# 中。

编辑:我的丑陋版本,虽然丑陋,是 O(N),实际上可能比使用 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();            
}

【问题讨论】:

  • 请告诉我们更多关于你想要完成的事情,包括为什么你不能在空格上分割。然后我们可以根据您的情况量身定制答案。

标签: c# string tokenize


【解决方案1】:

你正在做的计算机术语是lexical analysis;阅读这篇文章以获得对这个常见任务的一个很好的总结。

根据您的示例,我猜您希望用空格分隔单词,但引号中的内容应视为不带引号的“单词”。

最简单的方法是将单词定义为正则表达式:

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

此表达式表明“单词”是 (1) 非引号、被空格包围的非空格文本,或 (2) 被引号包围的非引号文本(后跟一些空格)。请注意使用捕获括号来突出显示所需的文本。

使用该正则表达式,您的算法很简单:在您的文本中搜索由捕获括号定义的下一个“单词”,然后返回它。重复此操作,直到用完“单词”。

这是我能想到的最简单的工作代码,在 VB.NET 中。请注意,我们必须检查 both 组的数据,因为有两组捕获括号。

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 上面的答案与此相同。希望这个答案能更好地解释幕后的细节:)

【讨论】:

    【解决方案2】:

    Microsoft.VisualBasic.FileIO 命名空间(在 Microsoft.VisualBasic.dll 中)有一个 TextFieldParser,您可以使用它来分割空格分隔的文本。它可以很好地处理引号内的字符串(即“这是一个标记”thisistokentwo)。

    注意,仅仅因为 DLL 显示 VisualBasic 并不意味着您只能在 VB 项目中使用它。它是整个框架的一部分。

    【讨论】:

      【解决方案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&lt;string&gt; 允许您的代码只解析您需要的部分。这种惰性方法没有任何真正的缺点,因为字符串是不可变的,所以你知道input 在你解析整个事情之前不会改变。

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

      【讨论】:

        【解决方案4】:

        您可能还想研究正则表达式。这可能会帮助你。这是从 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
        

        【讨论】:

          【解决方案5】:

          Craig 是对的——使用正则表达式。 Regex.Split 可能更符合您的需求。

          【讨论】:

            【解决方案6】:

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

            使用正则表达式绝对是最好的选择,但是这个只是返回整个字符串。我正在尝试对其进行调整,但到目前为止运气不佳。

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

            【讨论】:

            • 这不起作用,因为 Regex.Split 旨在基于分隔符而不是标记进行捕获。使用 Regex.Match 获得想要的效果。
            猜你喜欢
            • 2010-12-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-09
            • 1970-01-01
            • 1970-01-01
            • 2020-08-28
            相关资源
            最近更新 更多