public interface ICsvParser
{
IEnumerable<List<string>> Parse(string csv, char separator = ';');
}
public class CsvParser : ICsvParser
{
public IEnumerable<List<string>> Parse(string csv, char separator = ';')
{
if (csv == null) { throw new ArgumentNullException(nameof(csv)); }
if (string.IsNullOrEmpty(csv)) { yield break; }
var doubleQuote = '"';
var carriageReturn = '\r';
var lineFeed = '\n';
var eof = false;
var i = 0;
resume:
var isQuote = false;
var isEscapeSequence = false;
var isLineBreak = false;
var buffer = new StringBuilder();
var line = new List<string>();
for (; i < csv.Length; i++)
{
var current = csv[i];
if (isLineBreak)
{
if (current == lineFeed)
{
i++; // Skip the line-feed.
goto yield;
}
throw new ArgumentException($"Invalid character at {i}. Expected '\\n' but found '{current}'.");
}
else
{
if (isEscapeSequence)
{
if (current == doubleQuote)
{
buffer.Append(current);
}
else
{
isQuote = !isQuote;
if (current == separator)
{
line.Add(buffer.ToString());
buffer.Clear();
}
else
{
buffer.Append(current);
}
}
isEscapeSequence = false;
}
else
{
if (current == doubleQuote)
{
isEscapeSequence = true;
}
else
{
if (current == separator && !isQuote)
{
line.Add(buffer.ToString());
buffer.Clear();
}
else
{
if (current == carriageReturn)
{
isLineBreak = true;
}
else
{
buffer.Append(current);
}
}
}
}
}
}
eof = true;
yield:
// Current buffer is not added yet.
line.Add(buffer.ToString());
yield return line;
var eof = (i == csv.Length);
if (!eof)
{
goto resume;
}
}
}
Mod Moved Comments To Chat