Created
October 9, 2013 18:37
-
-
Save jchadwick/6905978 to your computer and use it in GitHub Desktop.
CSV Reader/Writer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
public abstract class CsvHelper | |
{ | |
protected internal const string DefaultDelimiter = ","; | |
protected internal static readonly char[] SpecialChars = new[] { ',', '\n', '\r', '"' }; | |
protected internal static string Escape(string field) | |
{ | |
if (field == null) | |
{ | |
return ""; | |
} | |
if (field.IndexOfAny(SpecialChars) != -1) | |
{ | |
return String.Format("\"{0}\"", field.Replace("\"", "\"\"")); | |
} | |
return field; | |
} | |
protected internal static IEnumerable<string> GetHeaders<T>() | |
{ | |
return GetHeaders(typeof(T)); | |
} | |
protected internal static IEnumerable<string> GetHeaders(object value) | |
{ | |
if (value == null) | |
yield break; | |
if (value is IDictionary) | |
{ | |
foreach (var key in (value as IDictionary).Keys) | |
{ | |
if (key != null) | |
yield return key.ToString(); | |
} | |
yield break; | |
} | |
var valueType = value.GetType(); | |
var enumerable = value as IEnumerable; | |
if (enumerable != null) | |
{ | |
foreach (var item in enumerable) | |
{ | |
if (item == null) | |
continue; | |
valueType = item.GetType(); | |
break; | |
} | |
} | |
foreach (var header in GetHeaders(valueType)) | |
{ | |
yield return header; | |
} | |
} | |
protected internal static IEnumerable<string> GetHeaders(Type valueType) | |
{ | |
return valueType.GetCachedProperties().Select(x => x.Name); | |
} | |
protected internal static IEnumerable<string> GetValues(object value) | |
{ | |
return value.ToDictionary().ToHashTable().Values; | |
} | |
protected internal static IEnumerable<string> ParseHeaders(string source, string delimiter = null) | |
{ | |
return | |
source | |
.Split(new[] { delimiter ?? DefaultDelimiter }, StringSplitOptions.None) | |
.Select(x => x.Trim('"').Trim()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
public class CsvReader<T> : CsvReader | |
{ | |
private readonly Func<IDictionary<string, string>, T> _mapper; | |
public CsvReader(TextReader reader, Func<IDictionary<string, string>, T> mapper, string delimiter = null) | |
: base(reader, GetHeaders<T>(), delimiter) | |
{ | |
_mapper = mapper; | |
} | |
public IEnumerable<T> ReadAll() | |
{ | |
return ReadAllRows(_mapper); | |
} | |
} | |
public class CsvReader : CsvHelper, IDisposable | |
{ | |
private readonly string _delimiter; | |
private readonly TextReader _reader; | |
public IEnumerable<string> Headers { get; private set; } | |
public CsvReader(TextReader reader, string delimiter = null) | |
: this(reader, ReadHeaders(reader, delimiter), delimiter) | |
{ | |
} | |
public CsvReader(TextReader reader, IEnumerable<string> headers, string delimiter = null) | |
{ | |
_reader = reader; | |
Headers = headers; | |
_delimiter = delimiter ?? DefaultDelimiter; | |
} | |
public void Dispose() | |
{ | |
if(_reader != null) | |
_reader.Dispose(); | |
} | |
public IEnumerable<T> ReadAllRows<T>(Func<IDictionary<string, string>, T> mapper) | |
{ | |
var rows = ReadAllRows(); | |
return rows.Select(mapper); | |
} | |
public IEnumerable<IDictionary<string, string>> ReadAllRows() | |
{ | |
string line; | |
while((line = _reader.ReadLine()) != null) | |
{ | |
yield return ReadRow(line); | |
} | |
} | |
public IDictionary<string, string> ReadRow() | |
{ | |
var line = _reader.ReadLine(); | |
return ReadRow(line); | |
} | |
private IDictionary<string, string> ReadRow(string line) | |
{ | |
if (line == null) | |
return null; | |
var values = line.Split(new[] { _delimiter }, StringSplitOptions.None); | |
return ToKeyValuePairs(values).ToDictionary(); | |
} | |
public static IEnumerable<string> ReadHeaders(TextReader reader, string delimiter = null) | |
{ | |
var firstLine = reader.ReadLine(); | |
if (firstLine == null) | |
return Enumerable.Empty<string>(); | |
return ParseHeaders(firstLine, delimiter ?? DefaultDelimiter); | |
} | |
private IEnumerable<KeyValuePair<string, string>> ToKeyValuePairs(string[] values) | |
{ | |
var idx = 0; | |
if (Headers.Count() < values.Length) | |
Trace.TraceError("** Found more values than headers ** "); | |
foreach (var header in Headers) | |
{ | |
if (values.Length > idx) | |
yield return new KeyValuePair<string, string>(header, values[idx]); | |
else | |
yield return new KeyValuePair<string, string>(header, string.Empty); | |
idx += 1; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
public class CsvWriter : CsvHelper, IDisposable | |
{ | |
private readonly TextWriter _writer; | |
public string Delimiter { get; private set; } | |
public bool IncludeTimestamp { get; set; } | |
public CsvWriter(string filename, string delimiter = null) | |
: this(new StreamWriter(filename, true), delimiter) | |
{ | |
} | |
public CsvWriter(TextWriter writer, string delimiter = null) | |
{ | |
_writer = writer; | |
Delimiter = delimiter ?? DefaultDelimiter; | |
} | |
public void WriteAll(object value, bool writeHeaders = true) | |
{ | |
if(value == null) | |
throw new System.ApplicationException("value"); | |
if (writeHeaders) | |
{ | |
var headers = GetHeaders(value); | |
if (IncludeTimestamp) | |
{ | |
headers = new[] { "Timestamp (UTC)" }.Concat(headers); | |
} | |
WriteLine(headers); | |
} | |
var rows = GetRows(value); | |
foreach (var row in rows) | |
{ | |
var values = row; | |
if (IncludeTimestamp) | |
values = new[] { DateTime.UtcNow.ToString("u") }.Concat(values); | |
WriteLine(values); | |
} | |
} | |
public void WriteLine(IEnumerable<string> values) | |
{ | |
var line = string.Join(Delimiter, values.Select(Escape)); | |
_writer.WriteLine(line); | |
} | |
private IEnumerable<IEnumerable<string>> GetRows(object data) | |
{ | |
IDictionary<string, string> dictionary; | |
if (data is IDictionary<string, object>) | |
{ | |
dictionary = (data as IDictionary<string, object>).ToHashTable(); | |
} | |
else | |
{ | |
dictionary = data as IDictionary<string, string>; | |
} | |
if (dictionary != null) | |
{ | |
yield return dictionary.Values; | |
yield break; | |
} | |
var enumerable = data as IEnumerable; | |
if (enumerable == null) | |
{ | |
yield return GetValues(data); | |
yield break; | |
} | |
foreach (var value in enumerable) | |
{ | |
yield return GetValues(value); | |
} | |
} | |
public void Dispose() | |
{ | |
if (_writer != null) | |
_writer.Dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment