c# .net

csharp .net CsvHelper

kimbs0301 2024. 10. 12. 17:09

c# .net csv 파일 쓰기 및 읽기

 

.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CsvHelper" Version="33.0.1" />
  </ItemGroup>

</Project>

 

Program.cs

using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;
using System.Globalization;
using System.Text;

public class Program
{
	static void Main(string[] args)
	{
		Write();
		WriteAppend();
		Console.WriteLine();
		Read();
		Console.WriteLine();
		Read2();
        Console.WriteLine();
		Read3();
	}

	static void Write()
	{
		System.IO.File.Delete("C:/files/Item1.csv");
		DateTime dateTime = DateTime.UtcNow;

		List<Item> list = new();
		list.Add(new Item() { ID = 1, Name = "a123~!@#$%^&*()_+", Description = "a", ItemType = EItemType.MONEY, EventStart = dateTime, EventEnd = dateTime, AttrGID = 1 });
		list.Add(new Item() { ID = 2, Name = "한글", Description = "b", ItemType = EItemType.ARMOR, EventStart = dateTime, EventEnd = dateTime, AttrGID = 1 });
		list.Add(new Item() { ID = 3, Name = "한\"글", Description = "c", ItemType = EItemType.ARMOR, EventStart = dateTime, EventEnd = dateTime, AttrGID = 1 });
		list.Add(new Item() { ID = 4, Name = "'a3", Description = "d", ItemType = EItemType.ARMOR, EventStart = dateTime, EventEnd = dateTime, AttrGID = 1 });

		var csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
		{
			Encoding = new UTF8Encoding(true), // UTF8-BOM: true
			NewLine = "\n",
			//Delimiter = ",",
			//Mode = CsvMode.NoEscape, // "한""글" -> 한"글
			TrimOptions = TrimOptions.InsideQuotes,
		};

		using (var streamReader = new StreamWriter("C:\\files\\Item1.csv", false, new UTF8Encoding(true)))
		using (var csvWriter = new CsvWriter(streamReader, csvConfiguration, leaveOpen: true))
		{
			//var options = new TypeConverterOptions { Formats = new[] { "yyyy-MM-dd'T'HH:mm:ss.fff" } };
			//csvWriter.Context.TypeConverterOptionsCache.AddOptions<DateTime>(options);
			//csvWriter.Context.TypeConverterOptionsCache.AddOptions<DateTime?>(options);

			csvWriter.WriteRecords(list);
		}
	}

	static void WriteAppend()
	{
		DateTime dateTime = DateTime.UtcNow;

		List<Item> list = new();
		list.Add(new Item() { ID = 5, Name = "123", Description = "d", ItemType = EItemType.ETC, EventStart = null, EventEnd = dateTime, AttrGID = 5 });

		var csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
		{
			Encoding = new UTF8Encoding(true), // UTF8-BOM: true
			NewLine = "\n",
			HasHeaderRecord = false, // CSV Header 쓰지 않도록 설정
		};

		// 기존 csv 파일 하단에 추가하여 쓰기
		using (var streamWriter = new StreamWriter("C:\\files\\Item1.csv", true, new UTF8Encoding(true)))
		using (var csvWriter = new CsvWriter(streamWriter, csvConfiguration, leaveOpen: true))
		{
			csvWriter.WriteRecords(list);
		}
	}

	static void Read()
	{
		var csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
		{
			Encoding = new UTF8Encoding(true), // UTF8-BOM: true
			NewLine = "\n",
			BadDataFound = null, // Unhandled exception. CsvHelper.BadDataException: You can ignore bad data by setting BadDataFound to null.
		};

		using (var streamReader = new StreamReader("C:\\files\\Item1.csv", new UTF8Encoding(true)))
		using (var csvReader = new CsvReader(streamReader, csvConfiguration))
		{
			List<Item> list = csvReader.GetRecords<Item>().ToList();
			foreach (var item in list)
			{
				Console.WriteLine($"{item.ID} {item.Name} {item.Description} {item.ItemType} {item.EventStart} {item.EventEnd} {item.AttrGID}");
			}
		}
	}

	static void Read2()
	{
		var csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
		{
			Encoding = new UTF8Encoding(true), // UTF8-BOM: true
			NewLine = "\n",
			Delimiter = ",",
			//Mode = CsvMode.NoEscape, // 한"글 -> "한""글"
			//TrimOptions = TrimOptions.InsideQuotes,
			PrepareHeaderForMatch = options => options.Header.Trim('"'),
			//BadDataFound = null, // Unhandled exception. CsvHelper.BadDataException: You can ignore bad data by setting BadDataFound to null.
		};

		using (var streamReader = new StreamReader("C:\\files\\Item2.csv", new UTF8Encoding(true)))
		using (var csvReader = new CsvReader(streamReader, csvConfiguration))
		{
			csvReader.Context.TypeConverterCache.AddConverter<string>(new RemoveQuotesStringConverter());
			csvReader.Context.TypeConverterCache.AddConverter<int>(new RemoveQuotesIntegerConverter());

			List<Item> list = csvReader.GetRecords<Item>().ToList();
			foreach (var item in list)
			{
				Console.WriteLine($"{item.ID} {item.Name} {item.Description} {item.ItemType} {item.EventStart} {item.EventEnd} {item.AttrGID}");
			}
		}
	}

  	static void Read3()
	{
		var csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
		{
			Encoding = new UTF8Encoding(true), // UTF8-BOM: true
			NewLine = "\n",
			Delimiter = ",",
			//Mode = CsvMode.NoEscape, // 한"글 -> "한""글"
			//TrimOptions = TrimOptions.InsideQuotes,
			PrepareHeaderForMatch = options => options.Header.Trim('"'),
			//BadDataFound = null, // Unhandled exception. CsvHelper.BadDataException: You can ignore bad data by setting BadDataFound to null.
		};

		using (var streamReader = new StreamReader("C:\\files\\Content1.csv", new UTF8Encoding(true)))
		using (var csvReader = new CsvReader(streamReader, csvConfiguration))
		{
			var options = new TypeConverterOptions { Formats = new[] { "yyyy-MM-dd'T'HH:mm:ss.fff", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm" } };
			csvReader.Context.TypeConverterOptionsCache.RemoveOptions<DateTime>();
			csvReader.Context.TypeConverterOptionsCache.AddOptions<DateTime>(options);
			//csvReader.Context.TypeConverterOptionsCache.AddOptions<DateTime?>(options);

			csvReader.Context.TypeConverterCache.AddConverter<string>(new RemoveQuotesStringConverter());
			csvReader.Context.TypeConverterCache.AddConverter<int>(new RemoveQuotesIntegerConverter());
			csvReader.Context.TypeConverterCache.RemoveConverter<DateTime>();
			csvReader.Context.TypeConverterCache.AddConverter<DateTime>(new RemoveEmptyDateTimeConverter());

			List<Content> list = csvReader.GetRecords<Content>().ToList();
			foreach (var item in list)
			{
				Console.WriteLine($"{item.ID} {item.Name} {item.Description} {item.ItemType} {item.EventStart} {item.EventEnd} {item.AttrGID}");
			}
		}
	}
}

 

Converter.cs

using CsvHelper.Configuration;
using CsvHelper;
using CsvHelper.TypeConversion;

public class RemoveQuotesStringConverter : StringConverter
{
	public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
	{
		if (text == null)
			text = string.Empty;

		var trimmed = text.Trim('"');

		return base.ConvertFromString(trimmed, row, memberMapData);
	}
}

public class RemoveQuotesIntegerConverter : Int32Converter
{
	public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
	{
		if (text == null)
			text = string.Empty;

		var trimmed = text.Trim('"');

		return base.ConvertFromString(trimmed, row, memberMapData);
	}
}

public class RemoveEmptyDateTimeConverter : DateTimeConverter
{
	private readonly TimeZoneInfo LocalTimeZone = TimeZoneInfo.Local; // Unspecified -> Local

	public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
	{
		if (string.IsNullOrEmpty(text))
			return TimeZoneInfo.ConvertTimeToUtc(DateTime.MinValue, LocalTimeZone); // return base.ConvertFromString("0001-01-01T00:00:00.000", row, memberMapData);

		var textDateTime = base.ConvertFromString(text, row, memberMapData);
		if (textDateTime == null)
			return TimeZoneInfo.ConvertTimeToUtc(DateTime.MinValue, LocalTimeZone);

		return TimeZoneInfo.ConvertTimeToUtc((DateTime) textDateTime, LocalTimeZone);
	}
}

 

Models.cs

using CsvHelper.Configuration.Attributes;

public class Item
{
	[Name("ID")]
	public long ID { get; set; }
	[Name("Name")]
	public string Name { get; set; } = string.Empty;
	[Name("Description")]
	public string Description { get; set; } = string.Empty;
	[Name("ItemType")]
	public EItemType ItemType { get; set; } = EItemType.NONE;
	[Name("EventStart")]
	[Format("yyyy-MM-dd'T'HH:mm:ss.fff")]
	public DateTime? EventStart { get; set; } = null;
	[Name("EventEnd")]
	[Format("yyyy-MM-dd'T'HH:mm:ss.fff")]
	public DateTime? EventEnd { get; set; } = null;
	[Name("AttrGID")]
	public int AttrGID { get; set; }
}

public enum EItemType
{
	NONE,
	MONEY,
	ARMOR,
	ETC
}

public class Content
{
	public long ID { get; set; }
	public string Name { get; set; } = string.Empty;
	public string Description { get; set; } = string.Empty;
	public EItemType ItemType { get; set; } = EItemType.NONE;
	public DateTime EventStart { get; set; }
	public DateTime EventEnd { get; set; }
	public int AttrGID { get; set; }
}

 

references
https://joshclose.github.io/CsvHelper/getting-started/
https://stackoverflow.com/questions/66360488/csv-helper-read-file-with-double-quotes-in-field-data
https://stackoverflow.com/questions/39564585/csvhelper-changing-how-dates-and-times-are-output