使用带引号和未带引号的字符串分隔逗号分隔的字符串

Pet*_*lén 52 c# regex

我有以下逗号分隔的字符串,我需要拆分.问题是某些内容在引号内并包含不应在分割中使用的逗号...

串:

111,222,"33,44,55",666,"77,88","99"
Run Code Online (Sandbox Code Playgroud)

我想要输出:

111  
222  
33,44,55  
666  
77,88  
99  
Run Code Online (Sandbox Code Playgroud)

我试过这个:

(?:,?)((?<=")[^"]+(?=")|[^",]+)   
Run Code Online (Sandbox Code Playgroud)

但它读取"77,88","99"之间的逗号作为命中,我得到以下输出:

111  
222  
33,44,55  
666  
77,88  
,  
99  
Run Code Online (Sandbox Code Playgroud)

有谁能够帮我?我用完了几个小时...... :) /彼得

jim*_*ode 85

根据您的需要,您可能无法使用csv解析器,实际上可能想要重新发明轮子!

您可以使用一些简单的正则表达式来完成

(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)
Run Code Online (Sandbox Code Playgroud)

这将执行以下操作:

(?:^|,)=匹配表达式"行或字符串的开头,"

(\"(?:[^\"]+|\"\")*\"|[^,]*) =编号的捕获组,这将在2个备选项之间进行选择:

  1. 引号中的东西
  2. 逗号之间的东西

这应该可以为您提供所需的输出.

C#中的示例代码

 static Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)", RegexOptions.Compiled);

public static string[] SplitCSV(string input)
{

  List<string> list = new List<string>();
  string curr = null;
  foreach (Match match in csvSplit.Matches(input))
  {        
    curr = match.Value;
    if (0 == curr.Length)
    {
      list.Add("");
    }

    list.Add(curr.TrimStart(','));
  }

  return list.ToArray();
}

private void button1_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine(SplitCSV("111,222,\"33,44,55\",666,\"77,88\",\"99\""));
}
Run Code Online (Sandbox Code Playgroud)

警告根据@MrE的评论 - 如果一个流氓的新行字符出现在一个格式错误的csv文件中并且你最终得到一个不均匀的("字符串")你会得到灾难性的回溯(https://www.regular-expressions.info/ catastrophic.html)你的正则表达式和你的系统可能会崩溃(就像我们的生产系统那样).可以很容易地在Visual Studio中复制,因为我发现它会崩溃.一个简单的try/catch也不会陷入这个问题.

你应该使用:

(?:^|,)(\"(?:[^\"])*\"|[^,]*)
Run Code Online (Sandbox Code Playgroud)

代替

  • 如果没有代码示例,它就会非常有意义,并且没有给出任何示例,因为我不知道他正在编写什么语言.我现在已经在C#中包含了一个示例 (5认同)
  • 嗯,不适合我...双引号内的逗号仍然用来"拆分"我的字符串. (4认同)
  • 如果一行以"但是错过了结束的那个(即有一个损坏的csv文件)"http://www.regular-expressions.info/catastrophic.html这个`(?:^ |,)(这会产生灾难性的回溯: \"(?:[^ \"])*\"| [^,]*)`覆盖它没有这个问题,并且更简单. (4认同)
  • 我更改了列表。添加到列表.Add(curr.TrimStart(',')。TrimStart('“')。TrimEnd('”')); (2认同)

qqb*_*enq 15

我真的很喜欢jimplode的答案,但我认为一个带有yield return的版本更有用,所以这里是:

public IEnumerable<string> SplitCSV(string input)
{
    Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)", RegexOptions.Compiled);

    foreach (Match match in csvSplit.Matches(input))
    {
        yield return match.Value.TrimStart(',');
    }
}
Run Code Online (Sandbox Code Playgroud)

也许让它像扩展方法更有用:

public static class StringHelper
{
    public static IEnumerable<string> SplitCSV(this string input)
    {
        Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)", RegexOptions.Compiled);

        foreach (Match match in csvSplit.Matches(input))
        {
            yield return match.Value.TrimStart(',');
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*uer 5

这个正则表达式无需循环遍历值和TrimStart(','),就像在接受的答案中一样:

((?<=\")[^\"]*(?=\"(,|$)+)|(?<=,|^)[^,\"]*(?=,|$))
Run Code Online (Sandbox Code Playgroud)

下面是 C# 中的实现:

string values = "111,222,\"33,44,55\",666,\"77,88\",\"99\"";

MatchCollection matches = new Regex("((?<=\")[^\"]*(?=\"(,|$)+)|(?<=,|^)[^,\"]*(?=,|$))").Matches(values);

foreach (var match in matches)
{
    Console.WriteLine(match);
}
Run Code Online (Sandbox Code Playgroud)

输出

111  
222  
33,44,55  
666  
77,88  
99  
Run Code Online (Sandbox Code Playgroud)


Ant*_*ine 5

快速简便:

    public static string[] SplitCsv(string line)
    {
        List<string> result = new List<string>();
        StringBuilder currentStr = new StringBuilder("");
        bool inQuotes = false;
        for (int i = 0; i < line.Length; i++) // For each character
        {
            if (line[i] == '\"') // Quotes are closing or opening
                inQuotes = !inQuotes;
            else if (line[i] == ',') // Comma
            {
                if (!inQuotes) // If not in quotes, end of current string, add it to result
                {
                    result.Add(currentStr.ToString());
                    currentStr.Clear();
                }
                else
                    currentStr.Append(line[i]); // If in quotes, just add it 
            }
            else // Add any other character to current string
                currentStr.Append(line[i]); 
        }
        result.Add(currentStr.ToString());
        return result.ToArray(); // Return array of all strings
    }
Run Code Online (Sandbox Code Playgroud)

使用此字符串作为输入:

 111,222,"33,44,55",666,"77,88","99"
Run Code Online (Sandbox Code Playgroud)

它将返回:

111  
222  
33,44,55  
666  
77,88  
99  
Run Code Online (Sandbox Code Playgroud)