Skip to content

Instantly share code, notes, and snippets.

@meisinger
Created May 1, 2014 03:33
Show Gist options
  • Save meisinger/5a27dc0f51bf6f3162a7 to your computer and use it in GitHub Desktop.
Save meisinger/5a27dc0f51bf6f3162a7 to your computer and use it in GitHub Desktop.
example of a simple csv reader
public sealed class CsvReader : IDisposable
{
private const char DefaultFieldChar = ',';
private const char DefaultQuoteChar = '"';
private const char DefaultEscapeChar = '\\';
private readonly List<CsvLine> collection;
private readonly StringBuilder buffer;
private readonly Stream stream;
private readonly StreamReader reader;
private readonly char fieldChar;
private readonly char quoteChar;
private readonly char escapeChar;
private readonly int skipToData;
private bool skippedToData;
private bool hasMoreData;
private int lineNumber;
public bool HasDataToRead
{
get { return hasMoreData; }
}
public CsvReader(Stream stream, char fieldChar, char quoteChar, char escapeChar, int skipToData)
{
this.stream = stream;
this.fieldChar = fieldChar;
this.quoteChar = quoteChar;
this.escapeChar = escapChar;
this.skipToData = skipToData;
reader = new StreamReader(stream);
buffer = new StringBuilder();
collection = new List<CsvLine>();
hasMoreData = true;
}
public void Dispose()
{
reader.Close();
reader.Dispose();
}
public IEnumerable<CsvLine> ReadToEnd()
{
while (hasMoreData)
{
var tokens = Read();
if (tokens != null)
collection.Add(tokens);
}
return collection;
}
public CsvLine Read()
{
var line = GetNextLine();
if (hasMoreData)
return new CsvLine {
LineNumber = lineNumber,
Line = ParseLine(line)
};
return null;
}
private string GetNextLine()
{
if (!skippedToData)
{
for (var index = 0; index < skipToData; index++)
{
lineNumber++;
reader.ReadLine();
}
skippedToData = true;
}
var line = reader.ReadLine();
if (line == null)
hasMoreData = false;
else
lineNumber++;
return line;
}
private string[] ParseLine(string line)
{
if (line == null)
return new string[0];
var tokens = new List<string>();
var inQuotes = false;
buffer.Length = 0;
do
{
if (inQuotes)
{
buffer.Append('\n');
line = GetNextLine();
if (line == null)
break;
}
var lineChars = line.ToCharArray();
for (var index = 0; index < lineChars.Length; index++)
{
var lineChar = lineChars[index];
var skipped = false;
var escaped = false;
var incremented = false;
if (lineChar.Equals(escapeChar))
{
lineChar = lineChars[index + 1];
escaped = true;
incremented = true;
}
if (lineChar.Equals(quoteChar))
{
if (!escaped)
{
skipped = true;
inQuotes = !inQuotes;
}
}
else if ((lineChar.Equals(fieldChar)) && !inQuotes)
{
tokens.Add(buffer.ToString().Trim());
buffer.Length = 0;
continue;
}
if (!skipped)
buffer.Append(lineChar);
if (incremented)
index++;
}
} while (inQuotes);
tokens.Add(buffer.ToString().Trim());
return tokens.ToArray();
}
public sealed class CsvLine
{
private string[] collection;
public string this[int index]
{
get
{
if (index >= collection.Length)
return string.Empty;
return collection[index];
}
}
public int Length
{
get
{
if (collection == null)
return 0;
return collection.Length;
}
}
public string[] Line
{
get { return collection; }
set { collection = value; }
}
public int LineNumber { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment