Skip to content

Instantly share code, notes, and snippets.

@DevPoint
Last active October 2, 2024 11:02
Show Gist options
  • Save DevPoint/ddba1ed0be307b05e6ac7a483a63a4e5 to your computer and use it in GitHub Desktop.
Save DevPoint/ddba1ed0be307b05e6ac7a483a63a4e5 to your computer and use it in GitHub Desktop.
C# implementation of a CSV reader which generates a two dimensional cells array of strings out of an CSV file
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace Devpoint
{
public class CsvReader
{
private readonly StringBuilder _cellBuilder;
private readonly List<string> _cellCache;
public CsvReader()
{
_cellBuilder = new StringBuilder(200);
_cellCache = new List<string>(20);
}
public string[] ReadRow(
string inputLine, char delimiter, char textQualifier = '"')
{
if (!string.IsNullOrEmpty(inputLine))
{
// parse input line
int inputIndex = 0;
bool textQualifierOpened = false;
while (inputIndex < inputLine.Length)
{
char c = inputLine[inputIndex];
if (c == delimiter && !textQualifierOpened)
{
_cellCache.Add(_cellBuilder.ToString());
_cellBuilder.Clear();
inputIndex += 1;
}
else if (c == '\r' && !textQualifierOpened)
{
_cellCache.Add(_cellBuilder.ToString());
_cellBuilder.Clear();
inputIndex += 1;
if (inputIndex < inputLine.Length &&
inputLine[inputIndex] == '\n')
{
inputIndex += 1;
}
}
else if (c == '\n' && !textQualifierOpened)
{
_cellCache.Add(_cellBuilder.ToString());
_cellBuilder.Clear();
inputIndex += 1;
}
else if (c == textQualifier)
{
if (inputIndex + 1 < inputLine.Length &&
inputLine[inputIndex + 1] == textQualifier)
{
_cellBuilder.Append(c);
inputIndex += 2;
}
else
{
textQualifierOpened = !textQualifierOpened;
inputIndex += 1;
}
}
else if (char.IsHighSurrogate(c))
{
_cellBuilder.Append(c);
_cellBuilder.Append(inputLine[inputIndex + 1]);
inputIndex += 2;
}
else
{
_cellBuilder.Append(c);
inputIndex += 1;
}
}
// add remaining to cell cache
if (_cellBuilder.Length > 0)
{
_cellCache.Add(_cellBuilder.ToString());
_cellBuilder.Clear();
}
}
// return result
string[] result = _cellCache.ToArray();
_cellCache.Clear();
return result;
}
public string[] ReadRow(
StreamReader input, char delimiter, char textQualifier = '"')
{
return ReadRow(input.ReadLine(), delimiter, textQualifier);
}
public async Task<string[]> ReadRowAsync(
StreamReader input, char delimiter, char textQualifier = '"')
{
return ReadRow(await input.ReadLineAsync(), delimiter, textQualifier);
}
public string[][] ReadRows(
StreamReader input, char delimiter, char textQualifier = '"')
{
var result = new List<string[]>(50);
string inputLine = input.ReadLine();
while (!string.IsNullOrEmpty(inputLine))
{
result.Add(ReadRow(inputLine, delimiter, textQualifier));
inputLine = input.ReadLine();
}
return result.ToArray();
}
public async Task<string[][]> ReadRowsAsync(
StreamReader input, char delimiter, char textQualifier = '"')
{
var result = new List<string[]>(50);
string inputLine = await input.ReadLineAsync();
while (!string.IsNullOrEmpty(inputLine))
{
result.Add(ReadRow(inputLine, delimiter, textQualifier));
inputLine = await input.ReadLineAsync();
}
return result.ToArray();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment