【C#】CsvHelper 示例文档

4bd94366d514d62cc022c4d4a5566c27.png

CsvHelper

https://github.com/JoshClose/CsvHelper

Prerequisites

以下是使用 CsvHelper 所需的一些先决条件。这些是使用 CsvHelper 时隐含的 .NET 基础知识。Microsoft 拥有出色的文档Microsoft has excellent documentation ,您可以使用这些文档了解更多信息。

Using and Dispose

每当你有一个实现 IDisposable 的对象时,你需要在完成后处理资源。大多数使用非托管资源的类将实现 IDisposable。这意味着 System.IO 命名空间中的许多类都需要处理掉。

处理完对象后最好的做法是将代码包装在 using 块中。当 using 块退出时,资源会自动尽快处理掉。

using (var stream = new MemoryStream())
{
   // Use the stream.
}
// The stream will be disposed of as soon as possible.

如果您需要将其保留一段时间并在以后处理它,则 using 会为您处理一些错误,因此使用它而不是直接调用 Dispose 仍然是一个好主意。关于这是否是一个好主意存在一些争论,因为它没有表现出意图。

var stream = new MemoryStream();
// Later in a different part of your code.
using (stream) { }

Reading and Writing Files

要打开文件进行读取或写入,我们可以使用 System.IO.File。

using (var stream = File.OpenRead("path\\to\\file.csv"))
{
}


using (var stream = File.OpenWrite("path\\to\\file.csv"))
{   
}

它们都返回一个 FileStream 来处理我们的文件。由于我们的数据是文本,我们需要使用 StreamReader 和 StreamWriter 来读取和写入文本。

using (var stream = File.OpenRead("path\\to\\file.csv"))
using (var reader = new StreamReader(stream))
{
}


using (var stream = File.OpenWrite("path\\to\\file.csv"))
using (var writer = new StreamWriter(stream))
{   
}

StreamReader 和 StreamWriter 具有执行此操作的快捷方式。

using (var reader = new StreamReader("path\\to\\file.csv"))
{
}
using (var writer = new StreamWriter("path\\to\\file.csv"))
{   
}

CsvHelper 对您的编码一无所知,因此如果您有特定的编码,则需要在流中指定它。

using (var reader = new StreamReader("path\\to\\file.csv", Encoding.UTF8))
{
}


using (var writer = new StreamWriter("path\\to\\file.csv", Encoding.UTF8))
{      
}

CsvReader 和 CsvWriter 在其构造函数中采用 TextReader 和 TextWriter。TextReader 和 TextWriter 是用于读写文本的抽象类。StreamReader 继承了 TextReader,StreamWriter 继承了 TextWriter,所以我们可以将它们与 CsvReader 和 CsvWriter 一起使用。

using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader))
{
}


using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer))
{   
}

Streams

从流中读取时,如果需要回到流的开头,可以使用 Stream.Position 属性。

using (var stream = new File.OpenRead("path\\to\\file"))
using (var reader = new StreamReader(stream))
{      
       // Read file content.
       var content = reader.ReadToEnd();
       // Go back to beginning of the stream.
       stream.Position = 0;
       // Read file content again.
       content = reader.ReadToEnd();
}

写入文件时,您需要刷新写入器以将数据写入流。 StreamWriter 包含一个内部缓冲区,只有在缓冲区已满或调用 Flush 时才会将数据写入流。当 using 块退出时,会自动调用 Flush。

using (var stream = new File.OpenWrite("path\\to\\file"))
using (var writer = new StreamWriter(stream))
{   
   writer.WriteLine("Foo");
   writer.Flush(); // Data is written from the writer buffer to the stream.
} // Flush is also called here.

CsvDataReader

关于如何使用 CsvHelper 加载数据表的问题经常出现,以至于我只是在其中构建了该功能。

CsvDataReader 实现 IDataReader。这意味着它具有仅转发数据读取器的所有功能。真的没有理由直接使用这个类而不是使用 CsvReader。CsvDataReader 需要一个 CsvReader 实例并在内部使用它来完成它的工作。

在 CsvHelper 中加载 DataTable 很简单。默认情况下,将加载一个表,其中所有列都填充为字符串。为了使读取器在实例化后准备就绪,需要立即读取第一行,因此您需要在创建 CsvDataReader 实例之前进行任何配置更改。

using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
  // Do any configuration to `CsvReader` before creating CsvDataReader.
  using (var dr = new CsvDataReader(csv))
  {    
    var dt = new DataTable();
    dt.Load(dr);
  }
}

如果要指定列和列类型,数据表将加载自动转换的类型。

using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
  // Do any configuration to `CsvReader` before creating CsvDataReader.
  using (var dr = new CsvDataReader(csv))
  {    
    var dt = new DataTable();
    dt.Columns.Add("Id", typeof(int));
    dt.Columns.Add("Name", typeof(string));


    dt.Load(dr);
  }
}

Reading

Enumerate Class Records

将 CSV  rows转换为在可枚举的每次迭代中重复使用的类对象。每个枚举都会水合给定的记录,但只有映射的成员。如果您提供了map但未映射其中一个成员,则该成员将不会与当前行的数据相结合。当心。您在投影上调用的任何强制评估 IEnumerable 的方法,例如 ToList(),您将获得一个列表,其中所有记录都是您提供的与 CSV 文件中的最后一条记录水合的相同实例。

Data
Id,Name
1,one

Example

void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
    var record = new Foo();
        var records = csv.EnumerateRecords(record);
    foreach (var r in records)
    {
      // r is the same instance as record.
    }
    }
}


public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Get Anonymous Type Records

将 CSV 行转换为匿名类型对象。您只需要提供匿名类型定义。

Data
Id,Name
1,one
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
      var anonymousTypeDefinition = new
      {
         Id = default(int),
         Name = string.Empty
      };
        var records = csv.GetRecords(anonymousTypeDefinition);
    }
}

Get Class Records

将 CSV 行转换为类对象。

Data
Id,Name
1,one
Example
void Main()
{
   using (var reader = new StreamReader("path\\to\\file.csv"))
   using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
   {
      var records = csv.GetRecords();
   }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get; set; }
}

Get Dynamic Records

将 CSV 行转换为动态对象。由于无法判断属性应该是什么类型,因此动态对象上的所有属性都是字符串。

Data
Id,Name
1,one
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        var records = csv.GetRecords();
    }
}

Reading by Hand

有时,出于各种原因,不尝试配置映射以匹配您的类定义会更容易。 手动读取行通常只需要几行代码。

Data
Id,Name
1,one
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        var records = new List();
      csv.Read();
      csv.ReadHeader();
      while (csv.Read())
      {
         var record = new Foo
         {
            Id = csv.GetField("Id"),
            Name = csv.GetField("Name")
         };
         records.Add(record);
      }
    }
}


public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Reading Multiple Data Sets

出于某种原因,存在 CSV 文件,其中包含多组 CSV 数据。您应该能够毫无问题地读取这样的文件。您将需要检测何时更改您正在检索的类类型。

Data
FooId,Name
1,foo
BarId,Name
07a0fca2-1b1c-4e44-b1be-c2b05da5afc7,bar
Example
void Main()
{
    var config = new CsvConfiguration(CultureInfo.InvariantCulture)
   {
      IgnoreBlankLines = false,     
   };
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, config))
    {
      csv.Context.RegisterClassMap();
      csv.Context.RegisterClassMap();
      var fooRecords = new List();
      var barRecords = new List();
      var isHeader = true;
      while (csv.Read())
      {
         if (isHeader)
         {
            csv.ReadHeader();
            isHeader = false;
            continue;
         }


         if (string.IsNullOrEmpty(csv.GetField(0)))
         {
            isHeader = true;
            continue;
         }




         switch (csv.HeaderRecord[0])
         {
            case "FooId":
               fooRecords.Add(csv.GetRecord());
               break;
            case "BarId":
               barRecords.Add(csv.GetRecord());
               break;
            default:
               throw new InvalidOperationException("Unknown record type.");
         }
      }
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}




public class Bar
{
   public Guid Id { get; set; }
   public string Name { get; set; }
}




public sealed class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id).Name("FooId");
      Map(m => m.Name);
   }
}




public sealed class BarMap : ClassMap
{
   public BarMap()
   {
      Map(m => m.Id).Name("BarId");
      Map(m => m.Name);
   }
}

Reading Multiple Record Types

如果您有 CSV 数据,其中每一行可能是不同的记录类型,您应该能够根据行类型或类似的内容进行读取。

Data
A,1,foo
B,07a0fca2-1b1c-4e44-b1be-c2b05da5afc7,bar
Example
void Main()
{
   var config = new CsvConfiguration(CultureInfo.InvariantCulture)
   {
      HasHeaderRecord = false,
   };
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, config))
    {
      csv.Context.RegisterClassMap();
      csv.Context.RegisterClassMap();
      var fooRecords = new List();
      var barRecords = new List();
      while (csv.Read())
      {
         switch (csv.GetField(0))
         {
            case "A":
               fooRecords.Add(csv.GetRecord());
               break;
            case "B":
               barRecords.Add(csv.GetRecord());
               break;
            default:
               throw new InvalidOperationException("Unknown record type.");
         }
      }
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}




public class Bar
{
   public Guid Id { get; set; }
   public string Name { get; set; }
}




public sealed class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id).Index(1);
      Map(m => m.Name).Index(2);
   }
}




public sealed class BarMap : ClassMap
{
   public BarMap()
   {
      Map(m => m.Id).Index(1);
      Map(m => m.Name).Index(2);
   }
}

Writing

注入警告

在外部程序中打开 CSV 时,可能会运行包含漏洞的字段中的公式。在此处阅读更多信息:逗号分隔漏洞。由于此问题,如果字段以字符 =、@、+ 或 - 开头,则该字段将以 \t 开头。如果该字段有引号,则 \t 将出现在 "之后.

=one -> \t=one

"=one" -> "\t=one"

您可以在配置中关闭此功能。

csv.Configuration.SanitizeForInjection = false;

写入时,您可以抛出一个可枚举的类对象、动态对象、匿名类型对象或几乎任何其他东西,然后它就会被写入。

Write Class Objects

Example
void Main()
{
   var records = new List
   {
      new Foo { Id = 1, Name = "one" },
   };


   using (var writer = new StreamWriter("path\\to\\file.csv"))
   using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
   {
      csv.WriteRecords(records);
   }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get; set; }
}
Output
Id,Name
1,one

Appending to an Existing CSV File

Example

void Main()
{
       var records = new List
       {
              new Foo { Id = 1, Name = "one" },
       };




       // Write to a file.
       using (var writer = new StreamWriter("path\\to\\file.csv"))
       using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
       {
              csv.WriteRecords(records);
       }




       records = new List
       {
              new Foo { Id = 2, Name = "two" },
       };




       // Append to the file.
       var config = new CsvConfiguration(CultureInfo.InvariantCulture)
       {
              // Don't write the header again.
              HasHeaderRecord = false,
       };
       using (var stream = File.Open("path\\to\\file.csv", FileMode.Append))
       using (var writer = new StreamWriter(stream))
       using (var csv = new CsvWriter(writer, config))
       {
              csv.WriteRecords(records);
       }
}




public class Foo
{
       public int Id { get; set; }
       public string Name { get; set; }
}

Output

Id,Name

1,one

2,two

Write Anonymous Type Objects

Example

void Main()
{
  var records = new List<object>
  {
    new { Id = 1, Name = "one" },
  };


  using (var writer = new StreamWriter("path\\to\\file.csv"))
  using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
  {
    csv.WriteRecords(records);
  }
}

Output

Id,Name

1,one

Write Dynamic Objects

Example

void Main()
{
  var records = new List<dynamic>();


  dynamic record = new ExpandoObject();
  record.Id = 1;
  record.Name = "one";
  records.Add(record);


  using (var writer = new StringWriter())
  using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
  {
    csv.WriteRecords(records);


    writer.ToString().Dump();
  }
}

Output

Id,Name

1,one

Configuration

class-maps

Auto Mapping

如果您不为配置提供map,则会自动为您动态创建一个map。 您也可以直接在您的类映射中调用自动映射。如果您有大量默认情况下将正确设置的属性,您可能需要执行此操作,并且只需要进行一些更改。

Data
Id,The Name
1,one
Example
void Main()
{     
   using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        var records = csv.GetRecords();
    }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get; set; }
}




public sealed class FooMap : ClassMap
{
   public FooMap()
   {
      AutoMap(CultureInfo.InvariantCulture);
      Map(m => m.Name).Name("The Name");
   }
}

Constant Value

您可以将常量值设置为属性,而不是将其映射到字段。

Data
Id,Name
1,one
Example
void Main()
{      
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        var records = csv.GetRecords();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsDirty { get; set; }
}




public sealed class FooMap : ClassMap
{
    public FooMap()
    {
      Map(m => m.Id);
      Map(m => m.Name);
        Map(m => m.IsDirty).Constant(true);
    }
}

Ignoring Properties

当您在类映射中使用自动映射时,每个属性都会被映射。 如果有你不想映射的属性,你可以忽略它们

Data
Id,Name
1,one
Example
void Main()
{      
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        var records = csv.GetRecords();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsDirty { get; set; }
}




public sealed class FooMap : ClassMap
{
    public FooMap()
    {
        AutoMap(CultureInfo.InvariantCulture);
        Map(m => m.IsDirty).Ignore();
    }
}

Inline Type Conversion

如果您不想编写完整的 ITypeConverter 实现,则可以指定一个执行相同操作的函数。

Reading

Data
Id,Name,Json
1,one,"{ ""Foo"": ""Bar"" }"
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        csv.GetRecords().ToList().Dump();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Json Json { get; set; }
}




public class Json
{
    public string Foo { get; set; }
}




public class FooMap : ClassMap
{
    public FooMap()
    {
        Map(m => m.Id);
        Map(m => m.Name);
        Map(m => m.Json).Convert(row => JsonConvert.DeserializeObject(row.GetField("Json")));
    }
}

Writing

Example
void Main()
{
   var records = new List
   {
      new Foo { Id = 1, Name = "one" }
   };


   using (var writer = new StreamWriter("path\\to\\file.csv"))
   using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
   {
      csv.Context.RegisterClassMap();
      csv.WriteRecords(records);


      writer.ToString().Dump();
   }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get; set; }
   public Json Json { get; set; }
}




public class Json
{
   public string Foo { get; set; }
}




public class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id);
      Map(m => m.Name);
      Map(m => m.Json).Convert(o => JsonConvert.SerializeObject(o));
   }
}
Output
Id,Name,Json
1,one,"{""Id"":1,""Name"":""one"",""Json"":null}"

Mapping by Alternate Names

如果您的标头名称可能不同,则可以指定多个标头名称。

Data
Id,Name
1,one
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        var records = csv.GetRecords();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get set; }
}




public sealed class FooMap : ClassMap
{
    public FooMap()
    {
        Map(m => m.Id).Name("TheId", "Id");
        Map(m => m.Name).Name("TheName", "Name");
    }
}

Mapping by Index

如果您的数据没有标题,您可以按索引而不是名称进行映射。 您不能依赖 .NET 中类属性的顺序,因此如果您不按名称进行映射,请确保指定索引。

Data
1,one
Example
void Main()
{
    var config = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        HasHeaderRecord = false,
    };
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, config))
    {
        csv.Context.RegisterClassMap();
        var records = csv.GetRecords();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get set; }
}




public sealed class FooMap : ClassMap
{
    public FooMap()
    {
        Map(m => m.Id).Index(0);
        Map(m => m.Name).Index(1);
    }
}

Mapping by Name

如果您的属性名称与您的类名称不匹配,您可以按名称将属性映射到列。

Data
ColumnA,ColumnB
1,one
Example
void Main()
{
   using (var reader = new StreamReader("path\\to\\file.csv"))
   using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
   {
      csv.Context.RegisterClassMap();
      var records = csv.GetRecords();
   }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get set; }
}




public sealed class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id).Name("ColumnA");
      Map(m => m.Name).Name("ColumnB");
   }
}

Mapping Duplicate Names

有时您有重复的标题名称。 这是通过标题名称索引处理的。名称索引是该标题名称出现次数的索引,而不是标题的位置。

Data
Id,Name,Name
1,first,last
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        var records = csv.GetRecords();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string FirstName { get set; }
   public string LastName { get; set; }
}




public sealed class FooMap : ClassMap
{
    public FooMap()
    {
        Map(m => m.Id);
        Map(m => m.FirstName).Name("Name").NameIndex(0);
      Map(m => m.LastName).Name("Name").NameIndex(1);
    }
}

Mapping Properties

这会将类的属性映射到 CSV 数据的标题名称。映射需要在上下文中注册。此示例与根本不使用类映射相同。标头与属性名称匹配。

Data
Id,Name
1,one
Example
void Main()
{
   using (var reader = new StreamReader("path\\to\\file.csv"))
   using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
   {     
      csv.Context.RegisterClassMap();
      var records = csv.GetRecords();
   }
}




public class Foo
{
   public int Id { get; set; }  
   public string Name { get; set; }
}




public sealed class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id);
      Map(m => m.Name);
   }
}

Optional Maps

如果您的数据可能有也可能没有标题,您可以将映射设为可选。

Data
Id,Name
1,one
Example
void Main()
{
   using (var reader = new StreamReader("path\\to\\file.csv"))
   using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
   {
      csv.Context.RegisterClassMap();
      csv.GetRecords().ToList().Dump();
   }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get; set; }
   public DateTimeOffset? Date { get; set; }
}




public class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id);
      Map(m => m.Name);
      Map(m => m.Date).Optional();
   }
}

Type Conversion

如果您需要与非标准 .NET 类型进行转换或从非标准类型转换,您可以提供用于属性的类型转换器。

Data
Id,Name,Json
1,one,"{ ""Foo"": ""Bar"" }"
Example
void Main()
{
   using (var reader = new StreamReader("path\\to\\file.csv"))
   using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
   {
      csv.Context.RegisterClassMap();
      csv.GetRecords().ToList().Dump();
   }
}




public class Foo
{
   public int Id { get; set; }
   public string Name { get; set; }
   public Json Json { get; set; }
}




public class Json
{
   public string Foo { get; set; }
}




public class JsonConverter: DefaultTypeConverter
{
   public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
   {
      return JsonConvert.DeserializeObject(text);
   }




   public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
   {
      return JsonConvert.SerializeObject(value);
   }
}




public class FooMap : ClassMap
{
   public FooMap()
   {
      Map(m => m.Id);
      Map(m => m.Name);
      Map(m => m.Json).TypeConverter<jsonconverter>();</jsonconverter
   }
}

Validation

如果您想确保您的数据符合某种标准,您可以对其进行验证。

Data
Id,Name
1,on-e
Example
void Main()
{
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Context.RegisterClassMap();
        csv.GetRecords().ToList().Dump();
    }
}




public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTimeOffset? Date { get; set; }
}




public class FooMap : ClassMap
{
    public FooMap()
    {
        Map(m => m.Id);
        Map(m => m.Name).Validate(field => !field.Contains("-"));
    }
}

Attributes 特性

大多数通过类映射完成的配置也可以使用特性完成。

Data
Identifier,name,IsBool,Constant
1,one,yes,a
2,two,no,b
Example
void Main()
{
   using (var reader = new StreamReader("path\\to\\file.csv"))
   using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
   {
      csv.GetRecords().ToList().Dump();
   }
}




public class Foo
{
   [Name("Identifier")]
   public int Id { get; set; }


   [Index(1)]
   public string Name { get; set; }


   [BooleanTrueValues("yes")]
   [BooleanFalseValues("no")]
   public bool IsBool { get; set; }


   [Constant("bar")]
   public string Constant { get; set; }


   [Optional]
   public string Optional { get; set; }


   [Ignore]
   public string Ignored { get; set; }  
}

官方测试代码

// https://github.com/JoshClose/CsvHelper
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Text;
using CsvHelper.Configuration;
using CsvHelper.Tests.Mocks;
using CsvHelper.TypeConversion;
using Xunit;


namespace CsvHelper.Tests.DataTableTests
{


  public class CsvDataReaderTests
  {
    [Fact]
    public void GetValuesTest()
    {
      var s = new StringBuilder();
      s.AppendLine("Boolean,Byte,Bytes,Char,Chars,DateTime,Decimal,Double,Float,Guid,Short,Int,Long,Null");
      s.AppendLine("true,1,0x0102,a,ab,1/1/2019,1.23,4.56,7.89,eca0c8c6-9a2a-4e6c-8599-3561abda13f1,1,2,3,null");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
      {
        csv.Context.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("null");
        var dataReader = new CsvDataReader(csv);
        dataReader.Read();


        Assert.True(dataReader.GetBoolean(0));
        Assert.Equal(1, dataReader.GetByte(1));


        byte[] byteBuffer = new byte[2];
        dataReader.GetBytes(2, 0, byteBuffer, 0, byteBuffer.Length);
        Assert.Equal(0x1, byteBuffer[0]);
        Assert.Equal(0x2, byteBuffer[1]);


        Assert.Equal('a', dataReader.GetChar(3));


        char[] charBuffer = new char[2];
        dataReader.GetChars(4, 0, charBuffer, 0, charBuffer.Length);
        Assert.Equal('a', charBuffer[0]);
        Assert.Equal('b', charBuffer[1]);


        Assert.Null(dataReader.GetData(0));
        Assert.Equal(DateTime.Parse("1/1/2019"), dataReader.GetDateTime(5));
        Assert.Equal(typeof(string).Name, dataReader.GetDataTypeName(0));
        Assert.Equal(1.23m, dataReader.GetDecimal(6));
        Assert.Equal(4.56d, dataReader.GetDouble(7));
        Assert.Equal(typeof(string), dataReader.GetFieldType(0));
        Assert.Equal(7.89f, dataReader.GetFloat(8));
        Assert.Equal(Guid.Parse("eca0c8c6-9a2a-4e6c-8599-3561abda13f1"), dataReader.GetGuid(9));
        Assert.Equal(1, dataReader.GetInt16(10));
        Assert.Equal(2, dataReader.GetInt32(11));
        Assert.Equal(3, dataReader.GetInt64(12));
        Assert.Equal("Boolean", dataReader.GetName(0)); 
        Assert.Equal(0, dataReader.GetOrdinal("Boolean"));//返回字段的索引


        Assert.Equal("true", dataReader.GetString(0));
        Assert.Equal("true", dataReader.GetValue(0));


        var objectBuffer = new object[14];
        dataReader.GetValues(objectBuffer);
        Assert.Equal("true", objectBuffer[0]);
        Assert.Equal(DBNull.Value, objectBuffer[13]);
        Assert.True(dataReader.IsDBNull(13));
      }
    }


    [Fact]
    public void GetSchemaTableTest()
    {
      var s = new StringBuilder();
      s.AppendLine("Id,Name");
      s.AppendLine("1,one");
      s.AppendLine("2,two");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
      {
        var dataReader = new CsvDataReader(csv);


        var schemaTable = dataReader.GetSchemaTable();
        Assert.Equal(25, schemaTable.Columns.Count);
        Assert.Equal(2, schemaTable.Rows.Count);
      }
    }


    [Fact]
    public void DataTableLoadTest()
    {
      var s = new StringBuilder();
      s.AppendLine("Id,Name");
      s.AppendLine("1,one");
      s.AppendLine("2,two");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
      {
        var dataReader = new CsvDataReader(csv);


        var dataTable = new DataTable();
        dataTable.Columns.Add("Id", typeof(int));
        dataTable.Columns.Add("Name", typeof(string));


        dataTable.Load(dataReader);


        Assert.Equal(2, dataTable.Rows.Count);
        Assert.Equal(1, dataTable.Rows[0]["Id"]);
        Assert.Equal("one", dataTable.Rows[0]["Name"]);
        Assert.Equal(2, dataTable.Rows[1]["Id"]);
        Assert.Equal("two", dataTable.Rows[1]["Name"]);
      }
    }


    [Fact]
    public void DataTableLoadHeaderAndRowsHaveDifferentLengthTest()
    {
      var s = new StringBuilder();
      s.AppendLine("Id,Name");
      s.AppendLine("1,one,a");
      s.AppendLine("2,two,b");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
      {
        var dataReader = new CsvDataReader(csv);


        var dataTable = new DataTable();
        dataTable.Columns.Add("Id", typeof(int));
        dataTable.Columns.Add("Name", typeof(string));


        dataTable.Load(dataReader);


        Assert.Equal(2, dataTable.Rows.Count);
        Assert.Equal(1, dataTable.Rows[0]["Id"]);
        Assert.Equal("one", dataTable.Rows[0]["Name"]);
        Assert.Equal(2, dataTable.Rows[1]["Id"]);
        Assert.Equal("two", dataTable.Rows[1]["Name"]);
      }
    }


    [Fact]
    public void DataTableLoadNoHeaderTest()
    {
      var config = new CsvConfiguration(CultureInfo.InvariantCulture)
      {
        HasHeaderRecord = false,
      };
      var s = new StringBuilder();
      s.AppendLine("1,one");
      s.AppendLine("2,two");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, config))
      {
        var dataReader = new CsvDataReader(csv);


        var dataTable = new DataTable();


        dataTable.Load(dataReader);


        Assert.Equal(0, dataTable.Rows.Count);
      }
    }


    [Fact]
    public void ReadWithNoHeaderTest()
    {
      var config = new CsvConfiguration(CultureInfo.InvariantCulture)
      {
        HasHeaderRecord = false,
      };
      var s = new StringBuilder();
      s.AppendLine("1,one");
      s.AppendLine("2,two");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, config))
      {
        var dataReader = new CsvDataReader(csv);


        dataReader.Read();
        Assert.Equal(1, dataReader.GetInt32(0));
        Assert.Equal("one", dataReader.GetString(1));


        dataReader.Read();
        Assert.Equal(2, dataReader.GetInt32(0));
        Assert.Equal("two", dataReader.GetString(1));
      }
    }


    [Fact]
    public void IsNullTest()
    {
      var config = new CsvConfiguration(CultureInfo.InvariantCulture)
      {
        HasHeaderRecord = false,
      };
      var s = new StringBuilder();
      s.AppendLine(",null");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, config))
      {
        csv.Context.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("null");


        var dataReader = new CsvDataReader(csv);
        Assert.False(dataReader.IsDBNull(0));
        Assert.True(dataReader.IsDBNull(1));
      }
    }


    [Fact]
    public void DbNullTest()
    {
      var config = new CsvConfiguration(CultureInfo.InvariantCulture)
      {
        HasHeaderRecord = false,
      };
      var s = new StringBuilder();
      s.AppendLine(",null");
      using (var reader = new StringReader(s.ToString()))
      using (var csv = new CsvReader(reader, config))
      {
        csv.Context.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("null");


        var dataReader = new CsvDataReader(csv);
        Assert.Equal(string.Empty, dataReader.GetValue(0));
        Assert.Equal(DBNull.Value, dataReader.GetValue(1));


        var values = new object[2];
        dataReader.GetValues(values);
        Assert.Equal(string.Empty, values[0]);
        Assert.Equal(DBNull.Value, values[1]);
      }
    }


    [Fact]
    public void GetOrdinalCaseInsensitiveTest()
    {
      var parser = new ParserMock
      {
        { "Id", "Name" },
        { "1", "one" },
        null,
      };


      using (var csv = new CsvReader(parser))
      {
        using (var dr = new CsvDataReader(csv))
        {
          var ordinal = dr.GetOrdinal("name");


          Assert.Equal(1, ordinal);
        }
      }
    }


    [Fact]
    public void GetOrdinalMissingTest()
    {
      var parser = new ParserMock
      {
        { "Id", "Name" },
        { "1", "one" },
        null,
      };


      using (var csv = new CsvReader(parser))
      {
        using (var dr = new CsvDataReader(csv))
        {
          Assert.Throws<IndexOutOfRangeException>(() =>
          {
            dr.GetOrdinal("Foo");
          });
        }
      }
    }


    [Fact]
    public void DataTableLoadEmptyTest()
    {
      var config = new CsvConfiguration(CultureInfo.InvariantCulture)
      {
        HasHeaderRecord = false,
      };
      using (var reader = new StringReader(string.Empty))
      using (var csv = new CsvReader(reader, config))
      {
        var dataReader = new CsvDataReader(csv);
        Assert.Equal(0, dataReader.FieldCount);
      }
    }


    [Fact]
    public void DataTableNullableValueTypeTest()
    {
      var config = new CsvConfiguration(CultureInfo.InvariantCulture)
      {
      };
      var parser = new ParserMock(config)
      {
        { "Id", "Name", "DateTime" },
        { "1", "one", DateTime.Now.ToString() },
      };
      using (var csv = new CsvReader(parser))
      using (var dr = new CsvDataReader(csv))
      {
        csv.Context.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("");


        var table = new DataTable();
        table.Columns.Add("Id", typeof(int));
        table.Columns.Add("Name", typeof(string));
        var column = table.Columns.Add("DateTime", typeof(DateTime));
        column.AllowDBNull = true;


        table.Load(dr);
      }
    }
  }
}

参考:

A .NET library for reading and writing CSV files. Extremely fast, flexible, and easy to use. | CsvHelper (joshclose.github.io)

https://joshclose.github.io/CsvHelper/   

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值