用正则表达式拆分行(CSV文件)

9

我不擅长正则表达式。有人能帮我写正则表达式吗?

在读取csv文件时,我可能会遇到以下值。

"Artist,Name",Album,12-SCS
"val""u,e1",value2,value3

输出:

Artist,Name  
Album
12-SCS
Val"u,e1 
Value2 
Value3

更新: 我喜欢使用Oledb提供程序的想法。我们在网页上有文件上传控件,我使用流阅读器读取文件内容而不实际保存文件在文件系统中。是否有任何方法可以使用Oledb提供程序,因为我们需要在连接字符串中指定文件名,在我的情况下,我没有将文件保存在文件系统中。


这将是一个棘手的正则表达式问题,你是否考虑过使用有限状态机进行解析? - stimms
http://regex.info/blog/2006-09-15/247 - Stephen Cleary
7个回答

15

我只是在这里分享今天早上我找到的解决方案。

var regex = new Regex("(?<=^|,)(\"(?:[^\"]|\"\")*\"|[^,]*)");

foreach (Match m in regex.Matches("<-- input line -->"))
{
    var s = m.Value; 
}

正如您所看到的,您需要对每一行调用regex.Matches()。然后它将返回一个与您拥有的列数相同的MatchCollection。每个匹配的Value属性显然是解析出来的值。

这仍然是一个正在进行中的工作,但它可以愉快地解析CSV字符串,例如:

2,3.03,"Hello, my name is ""Joshua""",A,B,C,,,D

很遗憾,该正则表达式无法匹配只有一个双引号的字段,例如 field1,"""",field2 - Alexandre
一个被引号包围且中间有换行符的字段怎么样? - Joe Skeen

9

实际上,使用正则表达式匹配CVS行非常容易。试试这个:

StringCollection resultList = new StringCollection();
try {
    Regex pattern = new Regex(@"
        # Parse CVS line. Capture next value in named group: 'val'
        \s*                      # Ignore leading whitespace.
        (?:                      # Group of value alternatives.
          ""                     # Either a double quoted string,
          (?<val>                # Capture contents between quotes.
            [^""]*(""""[^""]*)*  # Zero or more non-quotes, allowing 
          )                      # doubled "" quotes within string.
          ""\s*                  # Ignore whitespace following quote.
        |  (?<val>[^,]*)         # Or... zero or more non-commas.
        )                        # End value alternatives group.
        (?:,|$)                  # Match end is comma or EOS", 
        RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);
    Match matchResult = pattern.Match(subjectString);
    while (matchResult.Success) {
        resultList.Add(matchResult.Groups["val"].Value);
        matchResult = matchResult.NextMatch();
    } 
} catch (ArgumentException ex) {
    // Syntax error in the regular expression
}

免责声明:该正则表达式已在RegexBuddy中进行了测试(生成了此代码片段),并且正确匹配了原始测试数据,但C#代码逻辑未经测试。(我无法访问C#工具。)


@viggity - 很高兴能帮忙。您可能还想查看更复杂的正则表达式解决方案,以解析CSV行-请参见:如何使用Javascript解析CSV字符串? - ridgerunner

6

正则表达式不是处理这个的合适工具。使用CSV 解析器。可以使用内置的解析器第三方解析器。


同意,正则表达式不是正确的工具。我曾经使用过你在Codeproject上提供的CsvReader,并发现它非常适合处理csv文件。 - quentin-starin
我喜欢使用Oledb提供程序的想法。我们在网页上有文件上传控件,我使用流读取器读取文件内容,而不实际将文件保存在文件系统中。但是,由于需要在连接字符串中指定文件名,所以我是否可以使用Oledb提供程序呢?在我的情况下,我没有将文件保存在文件系统中。 - shailesh
那是一个新问题。请尝试用正确的标题、上下文和标签提出一个新问题。 - BalusC
内置的方法会强制你将值转换为.NET类型。如果它猜错了列,那么数据就会丢失。而第三方库中的方法则存在很多bug。第三方代码中的CsvReader类长达2500行,还有许多编写不当的函数,所以调试也变得非常麻烦。祝好运! - Jake
+1 但是,为什么不将那个漂亮的正则表达式库发布到开源社区托管平台(如Github、Google Code)上呢?如果没有CodeProject账户,我无法下载源代码。 - Evan Plaice

5

请查看TextFieldParser类。它在Microsoft.VisualBasic程序集中,可以进行分隔符和固定宽度解析。


+1 for TextFieldParser。它是.NET的隐藏宝石之一 - 可能是因为它在VisualBasic命名空间中被隐藏了。 (附言:始终遵循Brian S.的建议。这些家伙非常聪明!) - Brian Schroer

1

试试 CsvHelper(我维护的一个库)。它可以通过 NuGet 获取。

你可以轻松地将 CSV 文件读入自定义类集合中。它也非常快速。

var streamReader = // Create a StreamReader to your CSV file
var csvReader = new CsvReader( streamReader );
var myObjects = csvReader.GetRecords<MyObject>();

-1

正则表达式在这里可能会变得过于复杂。将该行按逗号分割,然后迭代结果位并将它们连接起来,其中“连接字符串中双引号的数量”不是偶数。

"hello,this",is,"a ""test"""

...分割...

"hello | this" | is | "a ""test"""

...迭代并合并直到有偶数个双引号...

"hello,this" - 双引号数量为偶数(注意逗号被分割插入到位之间)

is - 双引号数量为偶数

"a ""test""" - 双引号数量为偶数

...然后去掉前导和尾随引号(如果有),并用“”替换“”。


-1

可以使用以下代码完成:

using Microsoft.VisualBasic.FileIO;
string csv = "1,2,3,"4,3","a,"b",c",end";
TextFieldParser parser = new TextFieldParser(new StringReader(csv));
//To read from file
//TextFieldParser parser = new TextFieldParser("csvfile.csv");
parser.HasFieldsEnclosedInQuotes = true;
parser.SetDelimiters(",");
string[] fields =null;
while (!parser.EndOfData)
{
    fields = parser.ReadFields();
}
parser.Close();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接