Skip to content

Instantly share code, notes, and snippets.

@ByteTerrace
Last active November 3, 2016 17:03
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 ByteTerrace/89575400b7ea7e5e3ee774a70f529526 to your computer and use it in GitHub Desktop.
Save ByteTerrace/89575400b7ea7e5e3ee774a70f529526 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace ByteTerrace
{
public class DelimitedReader : IEnumerable<IEnumerable<string>>, IDisposable
{
private const char DEFAULT_ESCAPE_CHAR = '"';
private const char DEFAULT_SEPARATOR_CHAR = ',';
private readonly char m_escapeChar;
private bool m_isDisposed;
private readonly char m_separatorChar;
private readonly TextReader m_textReader;
public char EscapeChar {
get {
return m_escapeChar;
}
}
public bool IsDisposed {
get {
return m_isDisposed;
}
private set {
m_isDisposed = value;
}
}
public char SeparatorChar {
get {
return m_separatorChar;
}
}
public TextReader TextReader {
get {
return m_textReader;
}
}
public DelimitedReader(TextReader textReader, char separatorChar = DEFAULT_SEPARATOR_CHAR, char escapeChar = DEFAULT_ESCAPE_CHAR) {
m_escapeChar = escapeChar;
m_separatorChar = separatorChar;
m_textReader = textReader;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!IsDisposed) {
if (disposing) {
TextReader.Dispose();
}
IsDisposed = true;
}
}
public IEnumerator<IEnumerable<string>> GetEnumerator() {
return ReadFields().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public IEnumerable<IEnumerable<string>> ReadFields() {
return ReadFields(TextReader, SeparatorChar, EscapeChar);
}
public static DelimitedReader Create(TextReader textReader, char separatorChar = DEFAULT_SEPARATOR_CHAR, char escapeChar = DEFAULT_ESCAPE_CHAR) {
return new DelimitedReader(textReader, separatorChar, escapeChar);
}
public static string ReadField(string input, int offset, int position, char escapeChar) {
if (input[offset] == escapeChar) {
if (position - offset != 2) {
return input.Substring(offset + 1, position - offset - 2);
}
else {
return string.Empty;
}
}
else {
return input.Substring(offset, position - offset);
}
}
public static IEnumerable<IEnumerable<string>> ReadFields(TextReader reader, char separatorChar = DEFAULT_SEPARATOR_CHAR, char escapeChar = DEFAULT_ESCAPE_CHAR) {
var charsToSeek = new char[] { escapeChar, separatorChar };
var endOfLine = 0;
var escaping = false;
var fields = new List<string>();
var line = string.Empty;
var offset = 0;
var position = -1;
while ((line = reader.ReadLine()) != null) {
endOfLine = line.Length;
while (position < endOfLine) {
if (escaping) {
position = line.IndexOf(escapeChar, ++position);
}
else {
position = line.IndexOfAny(charsToSeek, ++position);
}
if (position != -1) {
if (line[position] == escapeChar) {
escaping = !escaping;
if ((((position - offset) > 2)) && (line[position - 1] == escapeChar)) {
line = line.Remove(position, 1);
endOfLine--;
}
}
else {
fields.Add(ReadField(line, offset, position, escapeChar));
offset = position + 1;
}
}
else {
break;
}
}
if (offset == line.Length) {
fields.Add(string.Empty);
}
else {
fields.Add(ReadField(line, offset, line.Length, escapeChar));
}
yield return fields;
offset = 0;
fields.Clear();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment