Created
November 26, 2016 14:06
-
-
Save zapthedingbat/467b04dfd4a8d34c2e3f84c7c338d2f0 to your computer and use it in GitHub Desktop.
This file contains hidden or 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; | |
using System.Text; | |
namespace ZapTheDingbat.IO | |
{ | |
public class CsvReader : IEnumerable<IReadOnlyList<string>>, IEnumerator<IReadOnlyList<string>> | |
{ | |
private readonly TextReader _reader; | |
private readonly IList<string> _currentRow; | |
private readonly StringBuilder _currentValue; | |
private IReadOnlyList<string> _current; | |
private State _state; | |
public CsvReader(TextReader reader) | |
{ | |
_reader = reader; | |
_state = State.DataStart; | |
_currentRow = new List<string>(); | |
_currentValue = new StringBuilder(); | |
} | |
private enum State | |
{ | |
DataStart, | |
DataStartQuoteStart, | |
DataStartQuote, | |
DataQuoted, | |
DataQuotedQuoteStart, | |
Data, | |
DataEnd, | |
NewLineStart, | |
} | |
public bool MoveNext() | |
{ | |
var c = _reader.Read(); | |
while (c != -1) | |
{ | |
if (MoveNextChar((char)c)) | |
{ | |
_current = _currentRow.ToArray(); | |
_currentRow.Clear(); | |
return true; | |
} | |
c = _reader.Read(); | |
} | |
return false; | |
} | |
public IReadOnlyList<string> Current => _current; | |
object IEnumerator.Current => Current; | |
private bool MoveNextChar(char c) | |
{ | |
if (_state == State.DataStart) | |
{ | |
_currentValue.Clear(); | |
switch (c) | |
{ | |
case '"': | |
_state = State.DataStartQuoteStart; | |
break; | |
case ',': | |
_state = State.DataStart; | |
_currentRow.Add(_currentValue.ToString()); | |
_currentValue.Clear(); | |
break; | |
default: | |
_state = State.Data; | |
_currentValue.Append(c); | |
break; | |
} | |
} | |
else if (_state == State.DataStartQuoteStart) | |
{ | |
if (c == '"') | |
{ | |
_state = State.DataStartQuote; | |
} | |
else | |
{ | |
_state = State.DataQuoted; | |
_currentValue.Append(c); | |
} | |
} | |
else if (_state == State.DataStartQuote) | |
{ | |
switch (c) | |
{ | |
case '"': | |
_state = State.DataQuoted; | |
_currentValue.Append(c); | |
break; | |
case ',': | |
_state = State.DataStart; | |
_currentRow.Add(_currentValue.ToString()); | |
_currentValue.Clear(); | |
break; | |
default: | |
_state = State.DataEnd; | |
break; | |
} | |
} | |
else if (_state == State.DataEnd) | |
{ | |
if (c == ',') | |
{ | |
_state = State.DataStart; | |
_currentRow.Add(_currentValue.ToString()); | |
_currentValue.Clear(); | |
} | |
} | |
else if (_state == State.Data) | |
{ | |
switch (c) | |
{ | |
case ',': | |
_state = State.DataStart; | |
_currentRow.Add(_currentValue.ToString()); | |
_currentValue.Clear(); | |
break; | |
case '\r': | |
_state = State.NewLineStart; | |
break; | |
default: | |
_currentValue.Append(c); | |
break; | |
} | |
} | |
else if (_state == State.DataQuoted) | |
{ | |
if (c == '"') | |
{ | |
_state = State.DataQuotedQuoteStart; | |
} | |
else | |
{ | |
_currentValue.Append(c); | |
} | |
} | |
else if (_state == State.DataQuotedQuoteStart) | |
{ | |
switch (c) | |
{ | |
case '"': | |
_state = State.DataQuoted; | |
_currentValue.Append(c); | |
break; | |
case ',': | |
_state = State.DataStart; | |
_currentRow.Add(_currentValue.ToString()); | |
_currentValue.Clear(); | |
break; | |
default: | |
_state = State.DataEnd; | |
break; | |
} | |
} | |
else if (_state == State.NewLineStart) | |
{ | |
if (c != '\n') | |
{ | |
throw new FormatException("Invalid NewLine seperator"); | |
} | |
_currentRow.Add(_currentValue.ToString()); | |
_currentValue.Clear(); | |
_state = State.DataStart; | |
return true; | |
} | |
return false; | |
} | |
public void Dispose() | |
{ | |
_reader.Dispose(); | |
} | |
public void Reset() | |
{ | |
throw new NotSupportedException(); | |
} | |
public IEnumerator<IReadOnlyList<string>> GetEnumerator() | |
{ | |
return this; | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return GetEnumerator(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment