理解CSV标准:为什么常用工具偏离RFC 4180标准?

2sbarzqh  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(140)

我试图解析许多CSV文件(逗号分隔,UTF-8编码),并遇到了一个反复出现的问题。
考虑一个场景,我有三个字段,其值为:A, "B", C
如果使用双引号括住字段,则必须通过在字段中使用另一个双引号对其进行转义
基于此,我的理解是正确的CSV表示应该是A,"""B""",C。然而,各种工具生成的许多文件的格式都是::"A, ""B"", C"
这会导致CSV解析器(例如c#中的CsvHelper)将这些行解释为单个字段,而不是三个单独的字段。
我在这里错过了什么吗?为什么这个看似“不正确”的格式在不同的工具中如此普遍使用?
为了便于讨论,这里有一个更现实的例子:
"00AA12345,30/11/2023,30/11/2023,01/12/2023,01/12/2023,""BAS"",1 111 000.27,""NRT"",""Test, ok"","""","""","""","""","""""
我需要阅读

  • 字段0:id
  • 字段1至4:日期
  • 字段5:日期
  • 字段6:字符串
  • field 7和+:string(可以包含双引号)
cyej8jka

cyej8jka1#

CsvMode.Escape接近你要找的东西。它适用于你非常简单的"A, ""B"", C"示例。然而,对于""Test, ok"",它创建了两个字段"Testok",我怀疑这应该是一个字段。但也许我错了,这确实对你有用,所以我想我至少会建议它。

var config = new CsvConfiguration(CultureInfo.InvariantCulture) {
    Mode = CsvMode.Escape   
};
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, config))

字符串
我倾向于同意@PanagiotisKanavos的观点,这个文件被编码了两次。这就是为什么我建议阅读两次。首先读取它,好像每行都应该是一个单独的字段,然后读取该字段以获得记录。
这将删除那些你说应该在字段中的双引号,但我不相信它们应该是数据的一部分。

void Main()
{
    var sb = new StringBuilder();
    sb.Append("\"00AA12345,30/11/2023,30/11/2023,01/12/2023,01/12/2023");
    sb.Append(",\"\"BAS\"\",1 111 000.27,\"\"NRT\"\",\"\"Test, ok\"\"");
    sb.Append(",\"\"\"\",\"\"\"\",\"\"\"\",\"\"\"\",\"\"\"\"\"");
    sb.AppendLine();
    sb.Append("\"00AA12345,30/11/2023,30/11/2023,01/12/2023,01/12/2023");
    sb.Append(",\"\"BAS\"\",1 111 000.27,\"\"NRT\"\",\"\"Test, ok\"\"");
    sb.Append(",\"\"\"\",\"\"\"\",\"\"\"\",\"\"\"\",\"\"\"\"\"");

    var records = new List<Foo>();

    var config = new CsvConfiguration(CultureInfo.InvariantCulture) {
        HasHeaderRecord = false
    };
    
    using (var reader = new StringReader(sb.ToString()))
    using (var csv = new CsvReader(reader, config))
    {
        while(csv.Read())
        {
            var line = csv.GetRecord<SingleLine>().Line;

            using (var reader2 = new StringReader(line))
            using (var csv2 = new CsvReader(reader2, config))
            {
                if(csv2.Read())
                {
                    var options = new TypeConverterOptions { Formats = new[] { "dd/MM/yyyy" } };
                    csv2.Context.TypeConverterOptionsCache.AddOptions<DateTime>(options);

                    var record = csv2.GetRecord<Foo>();
                    records.Add(record);
                }               
            }
        }       
    }
    records.Dump();
}

public class SingleLine
{
    public string Line { get; set; }
}

public class Foo
{
    [Index(0)]
    public string Field0 { get; set; }
    [Index(1)]
    public DateTime Field1 { get; set; }
    [Index(2)]
    public DateTime Field2 { get; set; }
    [Index(3)]
    public DateTime Field3 { get; set; }
    [Index(4)]
    public DateTime Field4 { get; set; }
    [Index(5)]
    public string Field5 { get; set; }
    [Index(6)]
    public string Field6 { get; set; }
    [Index(7)]
    public string Field7 { get; set; }
    [Index(8)]
    public string Field8 { get; set; }
    [Index(9)]
    public string Field9 { get; set; }
    [Index(10)]
    public string Field10 { get; set; }
    [Index(11)]
    public string Field11 { get; set; }
    [Index(12)]
    public string Field12 { get; set; }
    [Index(13)]
    public string Field13 { get; set; }
}

ar7v8xwq

ar7v8xwq2#

我将不讨论具体的代码问题,而只是解决这个高级问题:
为什么通用工具偏离RFC 4180标准?
答案是这个标准直到2005年才发布,但CSV数据已经使用了 * 很长时间 。在我们使用CSV数据的大部分时间里, 没有标准!*(我们过得很好,非常感谢)。
这意味着不仅有LOT的不兼容CSV数据仍然存在,而且今天仍然有大量的遗留系统继续产生新的不兼容输出,甚至创建新的应用程序来匹配不兼容行为,以便它们在更大的系统中运行(不兼容CSV的完整的忒修斯之船)。
CSV解析器需要能够处理这一点。

相关问题