Last active
November 3, 2016 17:03
-
-
Save ByteTerrace/89575400b7ea7e5e3ee774a70f529526 to your computer and use it in GitHub Desktop.
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; | |
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