Skip to content

Instantly share code, notes, and snippets.

@LuviKunG
Last active May 19, 2022 05:25
Show Gist options
  • Save LuviKunG/bf21d25826de824b5009cd580e904432 to your computer and use it in GitHub Desktop.
Save LuviKunG/bf21d25826de824b5009cd580e904432 to your computer and use it in GitHub Desktop.
CSV Reader and Table Data. (by LuviKunG)
using System.Collections.Generic;
using System.IO;
using System.Text;
/*
CSV Reader (with parser).
Created by @LuviKunG
https://github.com/LuviKunG
https://gist.github.com/LuviKunG
*/
namespace Serialization
{
/// <summary>
/// CSV Reader static class that use to parse any text or file into <see cref="TableData"/> class.
/// </summary>
public static class CSVReader
{
/// <summary>
/// Parse from file.
/// </summary>
/// <param name="path">Uri file path.</param>
/// <returns><see cref="TableData"/> class that contains value in the table.</returns>
public static TableData ParseFromFile(string path)
{
string text = File.ReadAllText(path);
return Parse(text);
}
/// <summary>
/// Parse from raw string text.
/// </summary>
/// <param name="text">Raw string text.</param>
/// <returns><see cref="TableData"/> class that contains value in the table.</returns>
public static TableData Parse(string text)
{
// Cache the longest row while parsing for easy to assign to the table.
int longestRow = 0;
// Create list as column of every row.
List<List<string>> list = new List<List<string>>();
// Create current row.
List<string> row = new List<string>();
// Using string builder for efficiency.
StringBuilder sb = new StringBuilder();
// Hold flag that is in quote mode or not.
bool quote = false;
// Use for loop for every character because needs to validate every char algorithm.
for (int i = 0; i < text.Length; ++i)
{
// If quotes.
if (text[i] == '\"')
{
// But if next is quote, so put qoute.
if (i + 1 < text.Length && text[i + 1] == '\"')
{
sb.Append('\"');
++i;
}
// If not, toggle quote mode.
else
quote = !quote;
continue;
}
// If isn't in quote mode, will do validation.
if (!quote)
{
// If end of cell.
if (text[i] == ',')
{
row.Add(sb.ToString());
sb.Clear();
continue;
}
// If end of rows with check \n\r or \r\n.
if ((text[i] == '\n' && i + 1 < text.Length && text[i + 1] == '\r') || (text[i] == '\r' && i + 1 < text.Length && text[i + 1] == '\n'))
{
row.Add(sb.ToString());
sb.Clear();
list.Add(row);
// Calculate longest row from current active row.
longestRow = row.Count > longestRow ? row.Count : longestRow;
row = new List<string>();
++i;
continue;
}
// If end of rows with check \n or \r.
if (text[i] == '\n' || text[i] == '\r')
{
row.Add(sb.ToString());
sb.Clear();
list.Add(row);
// Calculate longest row from current active row.
longestRow = row.Count > longestRow ? row.Count : longestRow;
row = new List<string>();
continue;
}
}
// If nothing need to validate, just append the current character.
sb.Append(text[i]);
}
// Add last value that in string builder to last row.
row.Add(sb.ToString());
// Clear string builder char array cache.
sb.Clear();
// Add last active row.
list.Add(row);
// Calculate longest row from current active row.
longestRow = row.Count > longestRow ? row.Count : longestRow;
// Create table class and put every value in list of columns and row.
TableData csvTable = new TableData(longestRow, list.Count);
for (int c = 0; c < list.Count; ++c)
for (int r = 0; r < list[c].Count; ++r)
csvTable[r, c] = list[c][r];
return csvTable;
}
}
}
using System;
using System.IO;
using System.Text;
/*
CSV Writer.
Created by @LuviKunG
https://github.com/LuviKunG
https://gist.github.com/LuviKunG
*/
namespace Serialization
{
/// <summary>
/// CSV Writer static class that use to convert any table into <see cref="TableData"/> class.
/// </summary>
public static class CSVWriter
{
private const char CHAR_SPACE = ' ';
private const char CHAR_COMMA = ',';
private const char CHAR_SINGLE_QUOTE = '\'';
private const char CHAR_DOUBLE_QUOTE = '\"';
/// <summary>
/// Save content from table to file path.
/// </summary>
/// <param name="table"><see cref="TableData"/> content.</param>
/// <param name="path">Uri path to save.</param>
public static void SaveToFile(TableData table, string path)
{
string content = Convert(table);
File.WriteAllText(path, content);
}
/// <summary>
/// Convert table to CSV content.
/// </summary>
/// <param name="table"><see cref="TableData"/> content.</param>
/// <returns>CSV String content.</returns>
public static string Convert(TableData table)
{
// Local function.
string ParseEscapeCSVString(string str)
{
string newStr = str.Trim();
for (int index = 0; index < newStr.Length; ++index)
{
if (newStr[index] == CHAR_SINGLE_QUOTE || newStr[index] == CHAR_DOUBLE_QUOTE)
{
newStr.Insert(index, CHAR_DOUBLE_QUOTE.ToString());
index += 2;
}
}
return newStr;
}
StringBuilder sb = new StringBuilder();
for (int c = 0; c < table.Cols; ++c)
{
for (int r = 0; r < table.Rows; ++r)
{
if (r != 0)
sb.Append(CHAR_COMMA);
string val = ParseEscapeCSVString(table[r, c]);
if (!(val.IndexOf(CHAR_SPACE) < 0)) // Contains space.
{
sb.Append(CHAR_DOUBLE_QUOTE);
sb.Append(val);
sb.Append(CHAR_DOUBLE_QUOTE);
}
else
sb.Append(val);
}
sb.Append(Environment.NewLine);
}
return sb.ToString();
}
}
}
using System;
using System.Text;
/*
Table Data.
Created by @LuviKunG
https://github.com/LuviKunG
https://gist.github.com/LuviKunG
*/
namespace Serialization
{
/// <summary>
/// Table class that holding value within columns and rows.
/// </summary>
public class TableData
{
private string[,] table;
/// <summary>
/// Get value from table.
/// </summary>
/// <param name="r">Row.</param>
/// <param name="c">Column.</param>
/// <returns>Value.</returns>
public string this[int r, int c]
{
get => table[r, c];
set => table[r, c] = value;
}
/// <summary>
/// Row of the table.
/// </summary>
public int Rows => table.GetLength(0);
/// <summary>
/// Column of the table.
/// </summary>
public int Cols => table.GetLength(1);
/// <summary>
/// Create table data.
/// rows and columns must be greater than zero.
/// </summary>
/// <param name="rows"></param>
/// <param name="cols"></param>
public TableData(int rows, int cols)
{
if (rows < 1 || cols < 1)
throw new ArgumentException($"Cannot set {nameof(rows)} or {nameof(cols)} below than one.");
table = new string[rows, cols];
}
/// <summary>
/// Resize the table by rows.
/// If new size is less than the old size, any outside value in the table will be lost.
/// </summary>
/// <param name="amount">Size of row.</param>
public void ResizeRow(int amount)
{
if (amount < 1)
throw new ArgumentException($"Cannot set {nameof(amount)} below than one.");
Resize(amount, Cols);
}
/// <summary>
/// Resize the table by columns.
/// If new size is less than the old size, any outside value in the table will be lost.
/// </summary>
/// <param name="amount">Size of column.</param>
public void ResizeColumn(int amount)
{
if (amount < 1)
throw new ArgumentException($"Cannot set {nameof(amount)} below than one.");
Resize(Rows, amount);
}
/// <summary>
/// Resize the table.
/// If new size is less than the old size, any outside value in the table will be lost.
/// </summary>
/// <param name="rows">Size of row.</param>
/// <param name="cols">Size of column.</param>
public void Resize(int rows, int cols)
{
if (rows < 1 || cols < 1)
throw new ArgumentException($"Cannot set {nameof(rows)} or {nameof(cols)} below than one.");
string[,] newTable = new string[rows, cols];
for (int c = 0; c < newTable.GetLength(1) && c < Cols; ++c)
for (int r = 0; r < newTable.GetLength(0) && r < Rows; ++r)
newTable[r, c] = table[r, c];
table = newTable;
}
/// <summary>
/// To String.
/// This will show every value in the table.
/// </summary>
/// <returns>String value as the table.</returns>
public override string ToString()
{
return ToString(',');
}
/// <summary>
/// To String.
/// This will show every value in the table.
/// </summary>
/// <param name="separator">Character that used for separator of every row.</param>
/// <returns>String value as the table.</returns>
public string ToString(char separator)
{
StringBuilder sb = new StringBuilder();
for (int c = 0; c < Cols; c++)
{
sb.Append('[');
for (int r = 0; r < Rows; r++)
{
if (r != 0)
sb.Append(separator);
sb.Append(table[r, c]);
}
sb.Append(']');
sb.AppendLine();
}
return sb.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment