【问题标题】:Efficiently split a string in format "{ {}, {}, ...}"有效地拆分格式为“{ {}, {}, ...}”的字符串
【发布时间】:2020-01-27 02:55:53
【问题描述】:

我有一个string,格式如下。

string instance = "{112,This is the first day 23/12/2009},{132,This is the second day 24/12/2009}"

private void parsestring(string input)
{
    string[] tokens = input.Split(','); // I thought this would split on the , seperating the {}
    foreach (string item in tokens)     // but that doesn't seem to be what it is doing
    {
       Console.WriteLine(item); 
    }
}

我想要的输出应该是这样的:

112,This is the first day 23/12/2009
132,This is the second day 24/12/2009

但目前,我得到了以下一个:

{112
This is the first day 23/12/2009
{132
This is the second day 24/12/2009

我是 C# 的新手,如有任何帮助,我们将不胜感激。

【问题讨论】:

  • 如果格式真的那么简单,分割"},{"作为分隔符,然后从结果数组的第一项中删除孤儿{,并从结果数组中的最后一项。
  • @SaniSinghHuttunen 你能帮帮我吗?
  • 您可以使用 TextFieldParser 类,即使它是在 VisualBasic 空间中定义的。 docs.microsoft.com/en-us/dotnet/api/… 另外,如果你的内部文本不包含双引号,那么你可以用双引号替换大括号,然后使用文本字段解析器忽略引号
  • @EdPlunkett 当我在拆分方法中使用"},{" 时,出现错误无法从字符串转换为字符。我不确定其中有什么问题?
  • @Analia string[] tokens = input.Split("},{"); 为我工作。

标签: c# split string-parsing


【解决方案1】:

不要专注于 Split() 是解决方案!没有它,这是一件很容易解析的事情。正则表达式的答案可能也可以,但我想就原始效率而言,制作“解析器”可以解决问题。

IEnumerable<string> Parse(string input)
{
    var results = new List<string>();
    int startIndex = 0;            
    int currentIndex = 0;

    while (currentIndex < input.Length)
    {
        var currentChar = input[currentIndex];
        if (currentChar == '{')
        {
            startIndex = currentIndex + 1;
        }
        else if (currentChar == '}')
        {
            int endIndex = currentIndex - 1;
            int length = endIndex - startIndex + 1;
            results.Add(input.Substring(startIndex, length));
        }

        currentIndex++;
    }

    return results;
}

所以它的线路并不短。它迭代一次,每个“结果”只执行一次分配。稍微调整一下,我可能可以制作一个带有索引类型的 C#8 版本来减少分配?这可能已经足够了。

您可能会花一整天时间弄清楚如何理解正则表达式,但这很简单:

  • 扫描每个字符。
  • 如果您找到{,请注意下一个字符是结果的开头。
  • 如果您找到},请将最后注明的“开始”到该字符之前的索引之间的所有内容视为“结果”。

这不会捕获不匹配的括号,并且可能会为“}}{”之类的字符串抛出异常。你没有要求处理这些案件,但改进这种逻辑以抓住它并为此尖叫或恢复并不难。

例如,当找到} 时,您可以将startIndex 重置为-1。从那里,您可以推断当 startIndex != -1 找到 { 时,您找到了“{{”。并且您可以推断,如果您在 startIndex == -1 时找到},则您找到了“}}”。如果你在 startIndex {,没有关闭的 }。这将字符串 "}whoops" 保留为未发现的情况,但可以通过将 startIndex 初始化为 -2 并专门检查它来处理它。 使用正则表达式,你会头疼的。

我建议这样做的主要原因是您说“有效”。 icepickle 的解决方案很好,但是Split() 为每个令牌分配一次,然后您为每个TrimX() 调用执行分配。这不是“有效的”。那是“n + 2 个分配”。

【讨论】:

  • 关于效率的公平点,但我只想做一个更清楚的例子,因为我认为 OP 需要从基础开始:) 不错的解决方案,但为什么不 yield,你的解决方案似乎很适合它;)
  • @Icepickle 当我走到最后时我想到了yield 并避免它,原因与您引用的相同:我想坚持基础。 yield return 需要一点解释,在你掌握它之前有点笨拙!
【解决方案2】:

好吧,如果您有一个名为 ParseString 的方法,那么它会返回一些东西是一件好事(如果说它是 ParseTokens 可能也不错)。所以如果你这样做,你可以来到下面的代码

private static IEnumerable<string> ParseTokens(string input)
{
    return input
        // removes the leading {
        .TrimStart('{')
        // removes the trailing }
        .TrimEnd('}')
        // splits on the different token in the middle
        .Split( new string[] { "},{" }, StringSplitOptions.None );
}

它之前对你不起作用的原因是因为你对 split 方法如何工作的理解是错误的,它会在你的示例中有效地分割所有,

现在,如果你把这些放在一起,你会得到类似dotnetfiddle

using System;
using System.Collections.Generic;

public class Program
{
    private static IEnumerable<string> ParseTokens(string input)
    {
        return input
            // removes the leading {
            .TrimStart('{')
            // removes the trailing }
            .TrimEnd('}')
            // splits on the different token in the middle
            .Split( new string[] { "},{" }, StringSplitOptions.None );
    }

    public static void Main()
    {
        var instance = "{112,This is the first day 23/12/2009},{132,This is the second day 24/12/2009}";
        foreach (var item in ParseTokens( instance ) ) {
            Console.WriteLine( item );
        }
    }
}

【讨论】:

    【解决方案3】:

    如果你不想要你的正则表达式,下面的代码会产生你需要的输出。

            string instance = "{112,This is the first day 23/12/2009},{132,This is the second day 24/12/2009}";
    
            string[] tokens = instance.Replace("},{", "}{").Split('}', '{');
            foreach (string item in tokens)
            {
                if (string.IsNullOrWhiteSpace(item)) continue;
    
                Console.WriteLine(item);
            }
    
            Console.ReadLine();
    

    【讨论】:

      【解决方案4】:

      using System.Text.RegularExpressions; 添加到类的顶部

      并使用正则表达式拆分方法

      string[] tokens = Regex.Split(input, "(?&lt;=}),");

      在这里,我们使用正向前瞻来拆分紧接在 } 之后的 ,

      (注意:(?&lt;= 你的字符串 ) 只匹配你的字符串后面的所有字符。你可以阅读更多关于它的信息here

      【讨论】:

      • @Analia 您可以在此之后使用简单的文本替换删除 { 和 }
      【解决方案5】:

      为此使用Regex

      string[] tokens = Regex.Split(input, @"}\s*,\s*{")
        .Select(i => i.Replace("{", "").Replace("}", ""))
        .ToArray();
      

      模式说明:

      \s* - 匹配零个或多个空白字符

      【讨论】:

      • 为什么不使用前瞻,他只想在 } 之后分割
      • @AbishekAditya 它没有任何区别:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 2012-02-29
      • 1970-01-01
      相关资源
      最近更新 更多