【问题标题】:Regex split a string using newline (unless it is between double quotes)正则表达式使用换行符拆分字符串(除非它在双引号之间)
【发布时间】:2016-06-24 07:00:08
【问题描述】:

我正在处理一些分隔文件。我需要做的第一件事是获取所有“行”。得到每一行后,我可以根据指定的分隔符进行拆分。因此,要获得行,我需要使用各种行名称(\r\n、\r、\n)来拆分字符串。以下内容一直有效,直到我在双引号中遇到换行符:

return content.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);

因此,如果您考虑以下文本(我的原始文本在双引号中用 \" 而不是 "" 转义了双引号),其中每一行由一个行名称分隔,并且该行中的每个字段/列是由竖线“|”字符分隔:

string s = "row1 col1|\"row1 \"\"col2a\"\"\r\nrow1 col2b\"|row1 col3\nrow2 col1|\"row2 \"\"col2a\"\"\rrow2 \"\"col2b\"\"\"|row2 col3\r\nrow3 col1|\"row3 col2a\nrow3 col2b\"|row3 col3";

等于以下字符串:

row1 col1|"row1 ""col2a""{CRLF}row1 ""col2b"""|row1 col3{CRLF}row2 col1|"row2 ""col2a""{CRLF}row2 ""col2b"""| row2 col3{CRLF}row3 col1|"row3 col2a{CRLF}row3 col2b"|row3 col3

用我原来的方法拆分上面的结果是 5 行:

string[] result = s.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);

但我想使用行分隔符 (\r\n, \r, \n) 将上述内容拆分为 3 行:

result[0] == "row1 col1|\"row1 \"\"col2a\"\"\r\nrow1 col2b\"|row1 col3"
result[1] == "row2 col1|\"row2 \"\"col2a\"\"\rrow2 \"\"col2b\"\"\"|row2 col3"
result[2] == "row3 col1|\"row3 col2a\nrow3 col2b\"|row3 col3"

有没有人幸运地想出一个正则表达式来分割行(引号内除外)?

感谢 Alan,这是我最终得到的结果:

public string[] GetLines (string fileContent) {
    Regex regex = new Regex(@"^([^""\r\n]*(?:(?:""[^""]*"")*[^""\r\n]*))", RegexOptions.Multiline);
    MatchCollection matchCollection = regex.Matches(fileContent);
    string[] result = new string[matchCollection.Count];
    for (int i = 0; i < matchCollection.Count; i++) {
        Match match = matchCollection[i];
        result[i] = match.Value;
    }
    return result;
}

【问题讨论】:

  • 如果我错了,请纠正我。您是否尝试解析 CSV?
  • 文件可以用任何分隔符分隔。逗号是一种选择。在我的具体情况下,它是一个管道分隔文件。
  • 听起来不错。请在下面查看我的解决方案。它很灵活,您可以使用任何字段分隔符

标签: c# regex split newline double-quotes


【解决方案1】:

我会使用Matches() 而不是Split()

Regex r = new Regex(@"(?m)^[^""\r\n]*(?:(?:""[^""]*"")+[^""\r\n]*)*");
MatchCollection m = r.Matches(s);

内部部分(?:(?:"[^"]*")+ 匹配可能包含转义引号的双引号字符串。整个正则表达式匹配可能包含一个或多个双引号字符串的行。请注意,内部字符类 ([^"]) 可以匹配 \r\n,其中外部字符类 ([^"\r\n]) 明确排除它们。 line-start 锚点(^ 在多行模式下)可防止真实匹配之间出现虚假的空匹配。

这是demo。 (它在 PCRE 中,但我也在 .NET 中对其进行了测试。)

【讨论】:

  • 我喜欢这个 - 谢谢!这适用于我最初的问题,但我已经意识到我在双引号中错误地转义了我的双引号。我应该使用“”来表示转义的双引号,但我使用的是\“。我用我的原始字符串(使用\”)尝试了你的正则表达式并且它有效!但是,它似乎不适用于正确的转义双引号“”。
  • @Michael:这使得正则表达式更加简单。查看我的编辑。
  • 谢谢,艾伦。我猜您的新解决方案必须在多行模式下使用?
  • @Michael:是的,否则你只会得到一场比赛。
【解决方案2】:

你可以试试下面的正则表达式:

var fieldSeparator = "|";
var strRx = $@"""[^""\r\n]*""{fieldSeparator}[^|]+(?:\s*)";

var rx = new Regex(strRx);
var data = "row1 col1|\"row1 \\\"col2a\\\"\r\nrow1 \\\"col2b\\\"\"|row1 col3\nrow2 col1|\"row2 col2a\rrow2 col2b\"";

var m = rx.Match(data);

while (m.Success)
{
    Console.WriteLine(m.Value);
    m = m.NextMatch();
}

只需将 fieldSeparator 值替换为您要使用的任何字段分隔符即可。

上面的代码 sn -p 产生以下输出:

row1 col1
"row1 \"
col2a\"
row1 \"col2b\""
row1 col3
row2 col1
row2 col2b"

【讨论】:

  • 谢谢你,萨利姆。这确实正确拆分了字段,但没有在字段之前给我一行。因为每一行代表一条记录,所以我首先需要每一行,然后我可以使用你的正则表达式来获取字段。我想我可以确定一条记录有多少个字段,然后遍历所有字段(由您的正则表达式给出)并按索引填充记录。但是现在,我将继续逐行进行。谢谢!
猜你喜欢
  • 2020-11-06
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 2017-06-18
  • 2017-12-30
  • 1970-01-01
  • 2019-08-04
相关资源
最近更新 更多