4

我正在尝试使用 C# 来解析 CSV。","如果我的标题计数等于我的匹配计数,我使用正则表达式来查找和读取字符串。

现在,如果我有如下值,这将不起作用:

"a",""b","x","y"","c"

那么我的输出是:

'a'
'"b'
'x'
'y"'
'c'

但我想要的是:

'a'
'"b","x","y"'
'c'

我可以为此使用任何正则表达式或任何其他逻辑吗?

4

13 回答 13

12

CSV,在处理多行、引用、不同的分隔符*等时 - 可能比你想象的更棘手......也许考虑一个预先滚动的答案?我用这个,效果很好。

*=请记住,某些语言环境使用 [tab] 作为 CSV 中的 C...

于 2008-11-25T08:34:19.407 回答
10

CSV 是代码重用的一个很好的例子——无论你选择哪一个 csv 解析器,都不要选择你自己的。 停止滚动你自己的 CSV 解析器

于 2009-01-06T20:35:09.993 回答
3

如果我是你,我会使用FileHelpers 。正则表达式很好,但很难阅读,特别是如果您在一段时间后返回以进行快速修复。

只是为了锻炼我的头脑,快速而肮脏的工作C# 程序:

public static List<string> SplitCSV(string line)
{
    if (string.IsNullOrEmpty(line))
        throw new ArgumentException();

    List<string> result = new List<string>();

    bool inQuote = false;
    StringBuilder val = new StringBuilder();

    // parse line
    foreach (var t in line.Split(','))
    {
        int count = t.Count(c => c == '"');

        if (count > 2 && !inQuote)
        {
            inQuote = true;
            val.Append(t);
            val.Append(',');
            continue;
        }

        if (count > 2 && inQuote)
        {
            inQuote = false;
            val.Append(t);
            result.Add(val.ToString());
            continue;
        }

        if (count == 2 && !inQuote)
        {
            result.Add(t);
            continue;
        }

        if (count == 2 && inQuote)
        {
            val.Append(t);
            val.Append(',');
            continue;
        }
    }

    // remove quotation
    for (int i = 0; i < result.Count; i++)
    {
        string t = result[i];
        result[i] = t.Substring(1, t.Length - 2);
    }

    return result;
}
于 2008-11-25T11:11:19.793 回答
2

有一句常被引用的话:

有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。(杰米·扎温斯基)

鉴于 CSV 文件没有官方标准(相反有大量稍微不兼容的样式),您需要确保您实现的内容适合您将收到的文件。实现比你需要的更高级的东西没有意义——而且我很确定你不需要正则表达式。

这是我对提取术语的简单方法的尝试 - 基本上,它遍历查找逗号的行,跟踪当前索引是否在字符串中:

    public IEnumerable<string> SplitCSV(string line)
    {
        int index = 0;
        int start = 0;
        bool inString = false;

        foreach (char c in line)
        {
            switch (c)
            {
                case '"':
                    inString = !inString;
                    break;

                case ',':
                    if (!inString)
                    {
                        yield return line.Substring(start, index - start);
                        start = index + 1;
                    }
                    break;
            }
            index++;
        }

        if (start < index)
            yield return line.Substring(start, index - start);
    }

标准警告 - 未经测试的代码,可能存在错误。

限制

  • 值周围的引号不会自动删除。
    为此,请在yield return接近结尾的语句之前添加一个检查。

  • 单引号与双引号的支持方式
    不同您可以添加一个单独的 boolean inSingleQuotedString,将现有的 boolean 重命名为inDoubleQuotedString并以相同的方式处理两者。(你不能让现有的布尔值做双重工作,因为你需要字符串以开始它的同一个引号结尾。)

  • 空格不会自动删除
    一些工具会在 CSV 文件中的逗号周围引入空格以“漂亮”文件;然后就很难从格式化空白中分辨出有意的空白。

于 2008-11-25T08:53:01.770 回答
1

文件助手支持多行字段。

您可以解析如下文件:

a,"line 1
line 2
line 3"
b,"line 1
line 2
line 3"

这是数据类型声明:

[DelimitedRecord(",")]
public class MyRecord
{ 
 public string field1;
 [FieldQuoted('"', QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
 public string field2;
}

这是用法:

static void Main()
{
 FileHelperEngine engine = new FileHelperEngine(typeof(MyRecord));
 MyRecord[] res = engine.ReadFile("file.csv");       
}
于 2010-08-25T16:08:27.637 回答
1

如果保证所有值都用引号引起来,请查找值,而不是逗号:

("".*?""|"[^"]*")

这利用了“最早的最长匹配获胜”这一事实 - 它首先查找双引号值,而正常引用值的优先级较低。

如果您不希望封闭引号成为匹配的一部分,请使用:

"(".*?"|[^"]*)"

并选择匹配组 1 中的值。

正如我所说:这个工作的先决条件是格式良好的输入,每个值周围都有保证的引号或双引号。空值也必须被引用!一个很好的副作用是它不关心分隔符。逗号、制表符、分号、空格,应有尽有。一切都会奏效。

于 2008-11-25T08:17:44.683 回答
1

为了有一个可解析的 CSV 文件,值内的任何双引号都需要以某种方式正确转义。执行此操作的两种标准方法是将双引号表示为两个背靠背的双引号或反斜杠双引号。这是以下两种形式之一:

“”

\"

在第二种形式中,您的初始字符串如下所示:

"a","\"b\",\"x\",\"y\"","c"

如果您的输入字符串没有针对像这样的严格格式进行格式化,那么您在自动化环境中成功解析它的机会就很小。

于 2008-11-25T08:13:42.567 回答
1

试试CsvHelper(我维护的一个库)或FastCsvReader。两者都运作良好。CsvHelper 也进行写作。就像其他人一直在说的那样,不要自己动手。:P

于 2010-01-19T16:04:12.347 回答
0

.Net 的FileHelpers是您的朋友。

于 2008-11-25T08:47:38.973 回答
0

请参阅链接“正则表达式与 CSV 的乐趣”:

http://snippets.dzone.com/posts/show/4430

于 2008-11-25T09:01:50.710 回答
0

Lumenworks CSV 解析器(开源,免费,但需要登录codeproject)是迄今为止我用过的最好的。它可以让您不必编写正则表达式并且使用起来很直观。

于 2008-11-25T09:27:13.840 回答
0

好吧,我不是正则表达式专家,但我确信他们对此有答案。

从程序上讲,它是一个字母一个字母地进行。将变量(例如 dontMatch)设置为 FALSE。

每次遇到报价切换时都不会匹配。

每次遇到逗号时,请检查 dontMatch。如果为 TRUE,请忽略逗号。如果为 FALSE,则以逗号分隔。

这适用于您给出的示例,但您用于引号的逻辑从根本上是错误的 - 您必须转义它们或使用另一个分隔符(例如单引号)将主要引号与次要引号分开。

例如,

"a", ""b", ""c", "d"", "e""

会产生不好的结果。

这可以用另一个补丁来修复。您必须匹配引号,而不是简单地保持真假。

要匹配引号,您必须知道上次看到的内容,这进入了非常深入的解析领域。到那时,您可能希望确保您的语言设计良好,如果是,您可以使用编译器工具为您创建解析器。

-亚当

于 2008-11-25T08:15:18.633 回答
0

我刚刚在我的代码中尝试了您的正则表达式。它适用于带引号的格式化文本...

但想知道我们是否可以通过正则表达式解析低于值..

"First_Bat7679",""NAME","ENAME","FILE"","","","来自:"DDD,_Ala%as"@sib.com"

我正在寻找结果:

'First_Bat7679'
'"NAME","ENAME","文件"'
''
''
'来自:“DDD,_Ala%as”@sib.com'

谢谢

于 2008-11-25T12:07:10.163 回答