Skip to content

Instantly share code, notes, and snippets.

@dimzon
Last active April 23, 2017 05:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dimzon/293b6c8ba513da054646282a244bbd43 to your computer and use it in GitHub Desktop.
Save dimzon/293b6c8ba513da054646282a244bbd43 to your computer and use it in GitHub Desktop.
Простая работа с CSV
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Microsoft.VisualBasic.FileIO;
namespace IKompass.PreparePDF.IO
{
public static class CsvUtils
{
public static CsvDefinition<TItem> DefineCsv<TItem, TContext>(
Func<string[],TContext> contextFactory,
Func<string[],TContext,int,TItem> objectFactory,
Action<TItem,TContext,int,string[]> rowBuilder,
params string[] headers
)
{
Action<TItem, int, string[]> w=null;
if (rowBuilder != null)
{
var ctx0 = contextFactory(headers);
w = (o, i, f) => rowBuilder(o, ctx0, i, f);
}
return new CsvDefinition<TItem>(headers,
headerNames =>
{
var ctx = contextFactory(headerNames);
return ((f, i) => objectFactory(f, ctx, i));
}, w);
}
public class CsvDefinition<TItem>
{
public void Write(TextWriter textWriter, IEnumerable<TItem> items)
{
WriteCsvWithHeaders(textWriter, items, _writerFunc, _headers);
}
public IEnumerable<TItem> Read(TextReader textReader)
{
return ParseCsvWithHeaders(textReader, _readerFunc);
}
private readonly string[] _headers;
private readonly Func<string[], Func<string[], int, TItem>> _readerFunc;
private readonly Action<TItem, int, string[]> _writerFunc;
internal CsvDefinition(string[] headers,
Func<string[], Func<string[], int, TItem>> readerFunc,
Action<TItem, int, string[]> writerFunc)
{
_headers = headers;
_readerFunc = readerFunc;
_writerFunc = writerFunc;
}
}
public static Converter NewConverter()
{
return new Converter();
}
public class Converter
{
public Converter WithCultureInfo(CultureInfo ci)
{
_cultureInfo = ci;
return this;
}
public Converter WithDateTimeFormat(string format)
{
_dateFormat = format;
return this;
}
public Converter WithGuidFormat(string format)
{
_guidFormat = format;
return this;
}
// ReSharper disable RedundantNameQualifier
private CultureInfo _cultureInfo = global::System.Globalization.CultureInfo.InvariantCulture;
// ReSharper restore RedundantNameQualifier
private string _dateFormat = "yyyyMMdd";
private string _guidFormat = "B";
public DateTime? ToDateTime(string str, string format = null, DateTime? defaultValue = null,
CultureInfo ci = null)
{
return string.IsNullOrEmpty(str)
? defaultValue
: DateTime.ParseExact(str, format ?? _dateFormat, ci ?? _cultureInfo);
}
public string ToString(DateTime? dt, string format = null, CultureInfo ci = null)
{
return dt == null ? null : ToString((DateTime)dt, format, ci);
}
public string ToString(DateTime dt, string format = null, CultureInfo ci = null)
{
return dt.ToString(format ?? _dateFormat, ci ?? _cultureInfo);
}
public Guid? ToGuid(string str, string format = null, Guid? defaultValue = null)
{
return string.IsNullOrEmpty(str) ? defaultValue : Guid.ParseExact(str, format ?? _guidFormat);
}
public string ToString(Guid guid, string format = null)
{
return guid.ToString(format ?? _guidFormat, _cultureInfo);
}
public string ToString(Guid? guid, string format = null)
{
return guid == null ? null : ToString((Guid)guid, format);
}
public string ToString<T>(T? t, string format = "G", CultureInfo ci = null)
where T : struct, IFormattable
{
return t == null ? null : ToString((T)t, format, ci);
}
public string ToString<T>(T t, string format = "G", CultureInfo ci = null)
where T : struct, IFormattable
{
return t.ToString(format, ci ?? _cultureInfo);
}
public int? ToInt32(string str, int? defaultValue = null, CultureInfo ci = null)
{
return str == null ? defaultValue : int.Parse(str, ci ?? _cultureInfo);
}
public long? ToInt64(string str, long? defaultValue = null, CultureInfo ci = null)
{
return str == null ? defaultValue : long.Parse(str, ci ?? _cultureInfo);
}
public double? ToDouble(string str, double? defaultValue = null, CultureInfo ci = null)
{
return str == null ? defaultValue : double.Parse(str, ci ?? _cultureInfo);
}
public decimal? ToDecimal(string str, decimal? defaultValue = null, CultureInfo ci = null)
{
return str == null ? defaultValue : decimal.Parse(str, ci ?? _cultureInfo);
}
}
public static int FindFieldOrThrow(string[] headers, string fieldName)
{
var n = 0;
foreach (var header in headers)
{
if (string.Equals(header, fieldName, StringComparison.OrdinalIgnoreCase))
return n;
++n;
}
throw new Exception(string.Format("Field '{0}' not found!", fieldName));
}
public static void WriteCsv<T>(TextWriter writer, IEnumerable<T> col,
Action<T, int, string[]> fillAction, int size)
{
var arr = new string[size];
foreach (var e in col)
{
Array.Clear(arr, 0, size);
fillAction(e, 0, arr);
WriteFields(writer, arr);
}
}
public static void WriteCsvWithHeaders<T>(TextWriter writer, IEnumerable<T> col,
Action<T, int, string[]> fillAction, params string[] headers)
{
WriteFields(writer, headers);
WriteCsv(writer, col, fillAction, headers.Length);
}
public static void WriteFields(TextWriter textWriter, params string[] csvLine)
{
var isNotFirst = false;
foreach (var f in csvLine)
{
if (isNotFirst)
textWriter.Write(";");
else
isNotFirst = true;
if (string.IsNullOrEmpty(f)) continue;
if (f.Contains("\""))
{
textWriter.Write("\"");
textWriter.Write(f.Replace("\"", "\"\""));
textWriter.Write("\"");
}
else if (f.Contains(";") || char.IsWhiteSpace(f[0]) || char.IsWhiteSpace(f[f.Length - 1]))
{
textWriter.Write("\"");
textWriter.Write(f);
textWriter.Write("\"");
}
else
{
textWriter.Write(f);
}
}
textWriter.WriteLine();
}
public static IEnumerable<T> ParseCsv<T>(TextReader reader, int skipLines, Func<string[], int, T> factory)
{
using (var f = NewTextFieldParser(reader))
{
var i = -1;
while (true)
{
var lines = f.ReadFields();
if (lines == null) break;
if (skipLines > 0)
{
--skipLines;
continue;
}
EmptyStringsToNull(lines);
yield return factory(lines, ++i);
}
}
}
private static void EmptyStringsToNull(string[] lines)
{
for (var i = 0; i < lines.Length; ++i)
if (string.IsNullOrEmpty(lines[i]))
lines[i] = null;
}
public static IEnumerable<T> ParseCsvWithHeaders<T>(TextReader reader, T obj,
Func<string[], Func<string[], int, T>> fn)
{
return ParseCsvWithHeaders(reader, fn);
}
public static IEnumerable<T> ParseCsvWithHeaders<T>(TextReader reader,
Func<string[], Func<string[], int, T>> fn)
{
using (var f = NewTextFieldParser(reader))
{
var headersLine = f.ReadFields();
if (headersLine == null) yield break;
var factory = fn(headersLine);
var index = -1;
while (true)
{
var lines = f.ReadFields();
if (lines == null) break;
EmptyStringsToNull(lines);
yield return factory(lines, ++index);
}
}
}
private static TextFieldParser NewTextFieldParser(TextReader reader)
{
return new TextFieldParser(reader)
{
Delimiters = new[] { ";" },
HasFieldsEnclosedInQuotes = true,
TrimWhiteSpace = true
};
}
public static IEnumerable<TItem> ParseCsvWithHeaders<TItem, TContext>(TextReader reader,
Func<string[], TContext> contextFactory,
Func<string[], TContext, int, TItem> factory)
{
using (var f = NewTextFieldParser(reader))
{
var headersLine = f.ReadFields();
if (headersLine == null) yield break;
var ctx = contextFactory(headersLine);
var index = -1;
while (true)
{
var lines = f.ReadFields();
if (lines == null) break;
EmptyStringsToNull(lines);
yield return factory(lines, ctx, ++index);
}
}
}
}
}
[SuppressMessage("ReSharper", "UnusedMember.Local")]
[SuppressMessage("ReSharper", "ArrangeStaticMemberQualifier")]
[SuppressMessage("ReSharper", "UnusedVariable")]
[SuppressMessage("ReSharper", "PossibleInvalidOperationException")]
private static void Demo()
{
var c = CsvUtils.NewConverter()
.WithDateTimeFormat("dd.MM.yyyy hh:mm")
.WithGuidFormat("n")
.WithCultureInfo(CultureInfo.CurrentCulture);
var a = CsvUtils.ParseCsvWithHeaders(TextReader.Null,
headers => new
{
name = CsvUtils.FindFieldOrThrow(headers, "Name"),
date = CsvUtils.FindFieldOrThrow(headers, "Date"),
count = CsvUtils.FindFieldOrThrow(headers, "Count"),
},
(fields, cols, i) => new
{
RowIndex = i,
Name = fields[cols.name],
Date = c.ToDateTime(fields[cols.date], defaultValue: DateTime.Today).Value,
});
CsvUtils.WriteCsvWithHeaders(TextWriter.Null, a, (e, n, f) =>
{
f[0] = e.Name;
f[1] = c.ToString(e.Date);
f[2] = c.ToString(0);
}, "Name","Date","Count");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment