【问题标题】:String to Sequence of Tokens字符串到令牌序列
【发布时间】:2011-11-02 22:09:19
【问题描述】:

我正在解析命令序列字符串,需要将每个字符串转换为字符串[],该字符串将按读取顺序包含命令标记。

原因是这些序列存储在数据库中,以指示协议客户端为各个远程应用程序执行某个规定的序列。这些字符串中有一些特殊的标记,我需要自己添加到 string[] 中,因为它们不代表正在传输的数据;相反,它们表示阻塞暂停。

序列不包含分隔符。在命令序列的任何地方都可以找到任意数量的特殊标记,这就是为什么我不能简单地用正则表达式解析字符串的原因。此外,序列中的所有这些特殊命令都使用${}

下面是我需要解析成token的数据示例(P1表示阻塞暂停一秒):

"some data to transmit${P1}more data here"

结果数组应如下所示:

{ "some data to transmit", "${P1}", "more data here" }

我认为 LINQ 可以帮助解决这个问题,但我不太确定。我能想到的唯一解决方案是遍历每个字符,直到找到$,然后检测是否有特殊的暂停命令可用,然后使用索引从那里解析序列。

【问题讨论】:

  • 我不明白你为什么不能使用Regex.Split -- 你能详细解释一下吗?
  • 为什么不使用一组分隔符?这将使这个问题变得微不足道。
  • 您或多或少准确地描述了lexer。应该有大量关于词法分析器如何工作的信息,而且它们实施起来相对简单。
  • @Gabe,因为我没有分隔符。
  • @Tejs,因为命令字符串是多个应用程序之间共享的数据,无法更改以适合我正在编写的一个应用程序。

标签: c# string tokenize


【解决方案1】:

好的,正如 cmets 中提到的,我建议您阅读 lexers。他们有能力做你所描述的一切,甚至更多。

既然你的要求很简单,我会说手工编写词法分析器并不太难。这是一些可以做到这一点的伪代码。

IEnumerable<string> tokenize(string str) {

    var result = new List<string>();
    int pos = -1;
    int state = 0;
    int temp = -1;

    while( ++pos < str.Length ) {
        switch(state) {
            case 0:
                if( str[pos] == "$" ) { state = 1; temp = pos; }
                break;
            case 1:
                if( str[pos] == "{" ) { state = 2; } else { state = 0; }
                break;
            case 2:
                if( str[pos] == "}" } {
                    state = 0;
                    result.Add( str.Substring(0, temp) );
                    result.Add( str.Substring(temp, pos) );
                    str = str.Substring(pos);
                    pos = -1;
                }
                break;
            }
    }

    if( str != "" ) {
        result.Add(str);
    }

    return result;
}

或者类似的东西。我通常在第一次尝试时得到Substring 的参数错误,但这是一般的想法。

您可以使用ANTLR 之类的东西获得更强大(更易于阅读)的词法分析器。

【讨论】:

  • 也许 ANTLR 是我的答案,因为这就是我想要的,更简单、更容易阅读/理解的东西。我可以遍历字符串来标记序列没有问题。
  • @jlafay,这有点取决于你需要它有多强大。如果您预计事情会变得更复杂,请使用 ANTLR。如果上述算法完全解决了您的问题,那么请改用手写算法。上面的代码更好地描述为状态机而不是循环,并且与 ANTLR 将生成的代码非常相似,因此您不太可能在效率方面获得太多收益。这完全取决于您的需求。
【解决方案2】:

根据 Gabe 的一些建议,我想出了一个完全符合我要求的解决方案:

string tokenPattern = @"(\${\w{1,4}})";
string cmdSequence = "${P}test${P}${P}test${P}${Cr}";

string[] tokenized = (from token in Regex.Split(cmdSequence, tokenPattern)
                      where token != string.Empty
                      select token).ToArray();

使用上例中的命令序列,数组包含以下内容:

{ "${P}", "test", "${P}", "${P}", "test", "${P}", "${Cr}"}

【讨论】:

    【解决方案3】:

    一种选择是使用Regex.Split(str, @"(\${.*?})") 并忽略当两个特殊标记彼此相邻时获得的空字符串。

    也许Regex.Split(str, @"(\${.*?})").Where(s =&gt; s != "") 就是你想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-04
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-21
      相关资源
      最近更新 更多