【问题标题】:Parse comma seperated string with a complication in C#在 C# 中使用复杂的逗号分隔字符串解析
【发布时间】:2015-05-06 13:21:26
【问题描述】:

我知道如何从逗号分隔的字符串中获取子字符串,但这里有一个复杂的问题:如果子字符串包含一个逗号。

如果子字符串包含逗号、换行符或双引号,则整个子字符串用双引号封装。

如果子字符串包含双引号,则双引号会被另一个双引号转义。 最坏的情况是,如果我有这样的事情:

first,"second, second","""third"" third","""fourth"", fourth"

在这种情况下,子字符串是:

  • 首先
  • 秒,秒
  • “第三”第三
  • “第四”,第四

second, second 是用双引号封装的,我不希望那些双引号出现在列表/数组中。

"third" third 用双引号封装,因为它包含双引号,并且用附加的双引号进行转义。同样,我不希望在列表/数组中使用封装双引号,也不希望双引号转义双引号,但我想要作为子字符串一部分的原始双引号。

【问题讨论】:

  • 我觉得分隔符更适合; 或逗号以外的其他东西。特别是因为您打算在输入中引入逗号。
  • 您已经为现有的 CSV 定义了接近“标准”的内容。因此,最简单的选择是找到一个已经存在的解析器。如果您必须自己执行此操作,请考虑使用简单的状态机来跟踪您是否处于引用或未引用的值中。
  • 你可以使用正则表达式吗? stackoverflow.com/questions/13915857/…
  • 我会像戴夫所说的那样使用正则表达式,或者使用新行作为分隔符。新行可能更容易,但如果您的文件可能包含任何内容,正则表达式将为您提供更大的灵活性。
  • 看看这个answer

标签: c# string list parsing substring


【解决方案1】:

一种使用TextFieldParser的方式:

using (var reader = new StringReader("first,\"second, second\",\"\"\"third\"\" third\",\"\"\"fourth\"\", fourth\""))    
using (var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(reader))
{
    parser.Delimiters = new[] { "," };
    parser.HasFieldsEnclosedInQuotes = true;

    while (!parser.EndOfData)
    {
        foreach (var field in parser.ReadFields())
            Console.WriteLine(field);
    }
}

对于

first
second, second
"third" third
"fourth", fourth

【讨论】:

    【解决方案2】:

    试试这个

               string input = "first,\"second, second\",\"\"\"third\"\" third\",\"\"\"fourth\"\", fourth\"";
    
                string[] output = input.Split(new string[] {"\",\""}, StringSplitOptions.RemoveEmptyEntries);

    【讨论】:

      【解决方案3】:

      我建议你为这个问题构建一个小型状态机。你会有这样的状态:

      • Out - 在到达第一个字段之前
      • InQuoted - 您已退出并“到达;现在您已加入且该字段已被引用
      • InQuotedMaybeOut - 你被 InQuoted 并且 " 到达了;现在你等待下一个字符来判断它是另一个 " 还是别的什么;如果是,则选择下一个有效状态(字符可以是空格、换行符、逗号,因此您决定下一个状态);否则,如果“到达,您将”推到输出并退回到 InQuoted
      • In - Out 之后,当出现除 , 和 " 之外的任何字符时,您将自动进入一个未引用的新字段。

      这肯定会正确读取 CSV。您还可以使分隔符可配置,以便支持 TSV 或分号分隔格式。

      还要记住 CSV 格式的一个非常重要的情况:引用字段可能包含新行!另一个需要注意的特殊情况:空字段(例如:,,)。

      【讨论】:

        【解决方案4】:

        这不是最优雅的解决方案,但它可能会对您有所帮助。我会遍历字符并对引号进行奇偶计数。例如,如果遇到奇数个引号,则 bool 为真,偶数个引号为假。

        当这个布尔值为真时遇到的任何逗号都不应被视为分隔符。如果你知道它是一个分隔符,你可以用这些信息做几件事。下面我用更易于管理的东西替换了分隔符(虽然不是很有效):

        bool odd = false;
        char replacementDelimiter = "|"; // Or some very unlikely character
        
        for(int i = 0; i < str.len; ++i)
        {
           if(str[i] == '\"')
               odd = !odd;
           else if (str[i] == ',')
           {
              if(!odd)
                  str[i] = replacementDelimiter;
           }
        }
        
        string[] commaSeparatedTokens = str.Split(replacementDelimiter);
        

        此时,您应该有一个字符串数组,这些字符串以您想要的逗号分隔。从这里开始,处理引号会更简单。

        希望对你有帮助。

        【讨论】:

          【解决方案5】:

          迷你解析器

          using System;
          using System.Collections.Generic;
          using System.Text;
          
          namespace ConsoleApp
          {
              class Program
              {
                  private static IEnumerable<string> Parse(string input)
                  {
                      if (string.IsNullOrWhiteSpace(input))
                      {
                          // empty string => nothing to do
                          yield break;
                      }
          
                      int count = input.Length;
                      StringBuilder sb = new StringBuilder();
                      int j;
          
                      for (int i = 0; i < count; i++)
                      {
                          char c = input[i];
                          if (c == ',')
                          {
                              yield return sb.ToString();
                              sb.Clear();
                          }
                          else if (c == '"')
                          {
                              // begin quoted string
                              sb.Clear();
                              for (j = i + 1; j < count; j++)
                              {
                                  if (input[j] == '"')
                                  {
                                      // quote
                                      if (j < count - 1 && input[j + 1] == '"')
                                      {
                                          // double quote
                                          sb.Append('"');
                                          j++;
                                      }
                                      else
                                      {
                                          break;
                                      }
                                  }
                                  else
                                  {
                                      sb.Append(input[j]);
                                  }
                              }
                              yield return sb.ToString();
          
                              // clear buffer and skip to next comma
                              sb.Clear();
                              for (i = j + 1; i < count && input[i] != ','; i++) ;
                          }
                          else
                          {
                              sb.Append(c);
                          }
                      }
                  }
          
                  [STAThread]
                  static void Main(string[] args)
                  {
                      foreach (string str in Parse("first,\"second, second\",\"\"\"third\"\" third\",\"\"\"fourth\"\", fourth\""))
                      {
                          Console.WriteLine(str);
                      }
          
                      Console.WriteLine();
                      Console.WriteLine("Press any key to continue...");
                      Console.ReadKey();
                  }
              }
          }
          

          结果

          • 首先
          • 秒,秒
          • “第三”第三
          • “第四”,第四

          【讨论】:

            【解决方案6】:

            感谢您的回答,但在我看到他们之前,我写了这个解决方案,它并不漂亮,但它对我有用。

            string line = "first,\"second, second\",\"\"\"third\"\" third\",\"\"\"fourth\"\", fourth\"";
            var substringArray = new List<string>();
            string substring = null;
            var doubleQuotesCount = 0;
            
            for (var i = 0; i < line.Length; i++)
            {
              if (line[i] == ',' && (doubleQuotesCount % 2) == 0)
              {
                substringArray.Add(substring);
                substring = null;
                doubleQuotesCount = 0;
                continue;
              }
              else
              {
                if (line[i] == '"')
                  doubleQuotesCount++;
            
                substring += line[i];
            
                //If it is a last character
                if (i == line.Length - 1)
                {
                  substringArray.Add(substring);
                  substring = null;
                  doubleQuotesCount = 0;
                }
              }
            }
            
            for(var i = 0; i < substringArray.Count; i++)
            {
              if (substringArray[i] != null)
              {
                //remove first double quote
                if (substringArray[i][0] == '"')
                {
                  substringArray[i] = substringArray[i].Substring(1);
                }
                //remove last double quote
                if (substringArray[i][substringArray[i].Length - 1] == '"')
                {
                  substringArray[i] = substringArray[i].Remove(substringArray[i].Length - 1);
                }
                //Replace double double quotes with single double quote
                substringArray[i] = substringArray[i].Replace("\"\"", "\"");
              }
            }
            

            【讨论】:

              猜你喜欢
              • 2021-11-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-20
              • 1970-01-01
              • 1970-01-01
              • 2019-12-04
              • 1970-01-01
              • 2015-05-24
              相关资源
              最近更新 更多