【问题标题】:Efficient Way to Tokenize String With Complex Delimiter / Separator and Preserving the Delimiter / Separator as Token in C#使用复杂分隔符/分隔符标记字符串并将分隔符/分隔符保留为 C# 中标记的有效方法
【发布时间】:2015-06-05 01:37:42
【问题描述】:

我正在尝试找到创建通用标记器的最有效方法,它将保留复杂的定界符/分隔符作为额外标记。

是的...我查看了一些 SO 问题,例如 How can i use string#split to split a string with the delimiters + - * / ( ) and space and retain them as an extra token?,但到目前为止,它太具体了。我需要针对通用字符串的解决方案。

就我而言,我希望对字符串进行标记,例如

"   A brown bear     A red firetruck  A white horse   "

因此,我期待以下标记:

"   ",              //3 spaces
"A brown bear",
"     ",            //5 spaces
"A red firetruck",
"  ",               //2 spaces
"A white horse",
"   "               //3 spaces

所以,这是我想出的代码,它按预期工作,但我想知道是否有任何改进...

public static class StringExtension
{
    public static List<string> TokenizeUsingRegex(this string input, string separatorRegexPattern, bool includeSeparatorsAsToken = true)
    {
        var tokens = Regex.Split(input, separatorRegexPattern).Where(t => !string.IsNullOrWhiteSpace(t)).ToList();

        if (!includeSeparatorsAsToken)
            return tokens;

        //Reinstate the removed separators      
        var newTokens = new List<string>();
        var startIndex  = 0;
        for(int i = 0, l = tokens.Count(); i < l; i++) 
        {
            var token = tokens[i];          
            var endIndex = input.IndexOf(token);

            if (startIndex < endIndex) {
                //Add back the separator as a new token
                newTokens.Add(input.Substring(startIndex, endIndex - startIndex));
            }
            //Then add the token afterward
            newTokens.Add(token);

            startIndex = endIndex + token.Length;           
        }

        //Add last separator if any
        if (startIndex < input.Length) {            
            newTokens.Add(input.Substring(startIndex));
        }

        return newTokens;
    }   
}

现场示例:https://dotnetfiddle.net/l3mesr

【问题讨论】:

  • 一个一个地读取字符,并在令牌字符第一次出现时,然后在下一个非令牌字符出现时,在最终的字符串令牌数组中创建一个新项。在我的示例中一次搜索一个令牌。

标签: c# string tokenize


【解决方案1】:

这个怎么样?

using System;
using System.Linq;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        var str = "    Invisible Pty. Ltd.     1 Nowhere St.  Sydney  2000  AUSTRALIA   ";
        //str = " A teddy bear   A red firetruck ";

        //tokenize the input delimited by 2 or more whitespaces
        var tokens = Regex.Matches(str, @"\s{2,}|(\s?[^\s]+(\s[^\s]+)*(\s$)?)").Cast<Match>().ToArray(); 

        foreach(var token in tokens)
        {
            Console.WriteLine("'{0}' - {1}", token, token.Length);
        }
    }
}

我在 Visual Studio 中使用了 Visual Studio 的 Perf 和 Diagnositics,这需要 40 毫秒,而现有的需要 80 毫秒。 dotnetfiddle.net 报告性能较慢(?)我可能会更信任 VS,但我只是想把它扔在那里。

基本上它的工作原理是查找多个空格或其他任何不超过一个空格的空格。

【讨论】:

  • 差不多了,但是如果我在 Invisible 之前有一个空格,在 AUSTRALIA 之后有一个空格,理想情况下应该将单个空格添加到尾随或前面的单词中,例如 `Invisible...` 和 @987654322 @ 但这很好而且非常紧凑:)
  • 我想是这样的:@"\s{2,}|(\s?[^\s]+(\s[^\s]+)*(\s$)?)"...?
  • 那行得通。很好地使用 ^ 和 $ 来提供特殊的第一个和最后一个案例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-23
  • 2015-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多