Skip to content

Commit 68c5a17

Browse files
author
Oren (electricessence)
committed
Initial checkin of CSV classes.
1 parent 45a2fb2 commit 68c5a17

7 files changed

Lines changed: 433 additions & 0 deletions

File tree

CsvData.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
/*
6+
* Awaiting System.Data support...
7+
*/
8+
9+
namespace Open.Text.CSV
10+
{
11+
public static class CsvData
12+
{
13+
14+
public static DataTable GetDataTable(string csv, bool firstRowIsHeader = false)
15+
{
16+
string[][] data = CsvUtility.GetArray(csv, out int maxColumns);
17+
18+
var t = new DataTable();
19+
20+
if (data.Any())
21+
{
22+
if (firstRowIsHeader)
23+
{
24+
var firstRow = data.First();
25+
for (int i = 0; i < maxColumns; i++)
26+
t.Columns.Add(firstRow[i], typeof(string));
27+
}
28+
else
29+
{
30+
for (int i = 0; i < maxColumns; i++)
31+
t.Columns.Add("Column" + i.ToString("00"), typeof(string));
32+
}
33+
34+
for (int n = firstRowIsHeader ? 1 : 0; n < data.Length; n++)
35+
{
36+
var s = data[n];
37+
DataRow r = t.NewRow();
38+
for (int i = 0; i < s.Length; i++)
39+
r["Column" + i.ToString("00")] = s[i];
40+
t.Rows.Add(r);
41+
}
42+
}
43+
44+
return t;
45+
}
46+
47+
// NOTE: ID as a first column first row item without quotes will cause Excel to fail loading the file.
48+
49+
public static void WriteCsvHeaderRow(TextWriter writer, DataTable t)
50+
{
51+
if (t == null) throw new ArgumentNullException("t");
52+
53+
foreach (DataColumn c in t.Columns)
54+
{
55+
CsvWriter.WriteCsvValue(writer, c.ColumnName, c.Ordinal == 0 && c.ColumnName == "ID");
56+
}
57+
writer.WriteLineNoTabs();
58+
}
59+
60+
public static void WriteCsvHeaderRow(TextWriter writer, IDataReader reader)
61+
{
62+
if (reader == null) throw new ArgumentNullException("reader");
63+
64+
int fc = reader.FieldCount;
65+
for (int i = 0; i < fc; i++)
66+
{
67+
var name = reader.GetName(i);
68+
CsvWriter.WriteValue(writer, name, i == 0 && name == "ID");
69+
}
70+
writer.WriteLineNoTabs();
71+
}
72+
73+
public static void WriteCsvRows(TextWriter writer, IEnumerable<DataRow> rows)
74+
{
75+
if (rows == null) throw new ArgumentNullException("rows");
76+
77+
foreach (DataRow row in rows)
78+
{
79+
foreach (DataColumn c in row.Table.Columns)
80+
{
81+
CsvWriter.WriteValue(writer, row[c]);
82+
}
83+
writer.WriteLineNoTabs();
84+
}
85+
}
86+
87+
public static void WriteCsvRows(TextWriter writer, IDataReader reader)
88+
{
89+
if (reader == null) throw new ArgumentNullException("reader");
90+
91+
var fc = reader.FieldCount;
92+
while (reader.Read())
93+
{
94+
for (int i = 0; i < fc; i++)
95+
CsvWriter.WriteValue(writer, reader.IsDBNull(i) ? null : reader.GetValue(i));
96+
writer.WriteLineNoTabs();
97+
}
98+
}
99+
100+
public static void WriteCsv(TextWriter writer, DataTable t, string sort = null)
101+
{
102+
if (t == null) throw new ArgumentNullException("t");
103+
104+
WriteCsvHeaderRow(writer, t);
105+
writer.WriteCsvRows(writer, t.Select(null, sort));
106+
}
107+
}
108+
}

CsvReader.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using Open.Disposable;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Text;
6+
7+
namespace Open.Text.CSV
8+
{
9+
public class CsvReader : DisposableBase
10+
{
11+
public CsvReader(StreamReader source)
12+
{
13+
_source = source;
14+
}
15+
16+
StreamReader _source;
17+
18+
int _maxColumns;
19+
public int MaxColumns => _maxColumns;
20+
21+
protected override void OnDispose(bool calledExplicitly)
22+
{
23+
_source = null; // The intention here is if this object is disposed, then prevent further reading.
24+
}
25+
26+
public string[] ReadRow()
27+
{
28+
return GetRow(_source, ref _maxColumns);
29+
}
30+
31+
public string[][] ReadRows()
32+
{
33+
var rows = GetRows(_source, out int maxColumns);
34+
if (maxColumns > _maxColumns) _maxColumns = maxColumns;
35+
return rows;
36+
}
37+
38+
public static bool TryGetRow(StreamReader source, out string[] row, ref int maxColumns)
39+
{
40+
if (source == null)
41+
throw new ArgumentNullException("source");
42+
43+
if (!source.EndOfStream)
44+
{
45+
row = CsvUtility.GetLine(source.ReadLine(), ref maxColumns);
46+
return true;
47+
}
48+
49+
row = null;
50+
return false;
51+
}
52+
53+
public static string[] GetRow(StreamReader source, ref int maxColumns)
54+
{
55+
TryGetRow(source, out string[] row, ref maxColumns);
56+
return row;
57+
}
58+
59+
public static string[][] GetRows(StreamReader source, out int maxColumns)
60+
{
61+
if (source == null)
62+
throw new ArgumentNullException("source");
63+
64+
maxColumns = 0;
65+
var lines = new List<string[]>();
66+
while (TryGetRow(source, out string[] row, ref maxColumns))
67+
lines.Add(row);
68+
69+
return lines.ToArray();
70+
}
71+
72+
public static string[][] GetRowsFromFile(string filepath, out int maxColumns)
73+
{
74+
if (filepath == null)
75+
throw new ArgumentNullException("filepath");
76+
if (String.IsNullOrWhiteSpace(filepath))
77+
throw new ArgumentException("Cannot be empty or only whitespace.", "filepath");
78+
79+
if (File.Exists(filepath))
80+
using (var s = new StreamReader((new FileInfo(filepath)).OpenRead()))
81+
return GetRows(s, out maxColumns);
82+
83+
maxColumns = 0;
84+
return new string[0][];
85+
}
86+
}
87+
}

CsvUtility.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text.RegularExpressions;
6+
7+
namespace Open.Text.CSV
8+
{
9+
public static partial class CsvUtility
10+
{
11+
public const string LINE_PATTERN = "((?:\")([^\"]+)(?:\")|([^,\"]+))(?:\\s*)(?:,|$)";
12+
public static readonly Regex LinePattern = new Regex(LINE_PATTERN);
13+
internal static readonly string[] StringArrayEmpty = new string[0];
14+
15+
public static string[] GetLine(string line, ref int maxColumns)
16+
{
17+
if (line == null)
18+
return StringArrayEmpty;
19+
20+
var mc = LinePattern.Matches(line);
21+
int c = mc.Count;
22+
if (c > maxColumns)
23+
maxColumns = c;
24+
25+
var result = new string[c];
26+
for (int i = 0; i < c; i++)
27+
result[i] = mc[i].Groups[1].Value.Trim('"');
28+
29+
return result;
30+
}
31+
32+
public static string[][] GetArray(string csv, out int maxColumns)
33+
{
34+
maxColumns = 0;
35+
string[] lines = csv == null ? new string[0] : csv.Split('\n');
36+
37+
var result = new List<string[]>(lines.Length);
38+
foreach (string line in lines)
39+
result.Add(GetLine(line, ref maxColumns));
40+
41+
return result.ToArray();
42+
}
43+
44+
public static string[][] GetArray(string csv)
45+
{
46+
return GetArray(csv, out int maxColumns);
47+
}
48+
49+
50+
public const string NEWLINE = "\r\n";
51+
public readonly static Regex QUOTESNEEDED = new Regex("^\\s+|[,\n]|\\s+$");
52+
53+
public static string WrapQuotes(string value)
54+
{
55+
if (value == null)
56+
return String.Empty;
57+
58+
return '"' + value.Replace("\"", "\"\"") + '"';
59+
}
60+
61+
public static string FormatValue(string value, bool forceQuotes = false)
62+
{
63+
if (value == null)
64+
return String.Empty;
65+
66+
if (!String.IsNullOrEmpty(value) && forceQuotes || QUOTESNEEDED.IsMatch(value))
67+
return WrapQuotes(value);
68+
69+
return value;
70+
}
71+
72+
public static string ExportValue(object value, bool forceQuotes = false)
73+
{
74+
string result = String.Empty;
75+
if (value != null)// && value != DBNull.Value)
76+
{
77+
if (value is DateTime datetime)
78+
{
79+
result = datetime.TimeOfDay == TimeSpan.Zero ?
80+
datetime.ToString("d") : // Use short date.
81+
datetime.ToString();
82+
}
83+
else
84+
result = value.ToString();
85+
}
86+
return FormatValue(result, forceQuotes) + ",";
87+
}
88+
89+
}
90+
}

CsvWriter.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Open.Disposable;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Text;
6+
7+
namespace Open.Text.CSV
8+
{
9+
public class CsvWriter : DisposableBase
10+
{
11+
internal TextWriter Target;
12+
13+
public CsvWriter(TextWriter target)
14+
{
15+
target = Target;
16+
}
17+
18+
protected override void OnDispose(bool calledExplicitly)
19+
{
20+
Target = null; // The intention here is if this object is disposed, then prevent further writing.
21+
}
22+
23+
public void WriteRow(object[] row, bool forceQuotes = false)
24+
{
25+
WriteRow(Target, row, forceQuotes);
26+
}
27+
28+
public void WriteRows(object[][] rows, bool forceQuotes = false)
29+
{
30+
WriteRows(Target, rows, forceQuotes);
31+
}
32+
33+
public static void WriteValue(TextWriter writer, object value = null, bool forceQuotes = false)
34+
{
35+
writer.Write(CsvUtility.ExportValue(value, forceQuotes));
36+
}
37+
38+
public static void WriteRow(TextWriter writer, object[] row, bool forceQuotes = false)
39+
{
40+
if (row == null) throw new ArgumentNullException("row");
41+
42+
foreach (var o in row)
43+
WriteValue(writer, o, forceQuotes);
44+
45+
writer.WriteLineNoTabs();
46+
}
47+
48+
public static void WriteRows(TextWriter writer, object[][] rows, bool forceQuotes = false)
49+
{
50+
if (rows == null) throw new ArgumentNullException("rows");
51+
52+
foreach (var row in rows)
53+
WriteRow(writer, row, forceQuotes);
54+
}
55+
}
56+
}

ExcelCsvExtensions.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Open.Text.CSV.Excel
5+
{
6+
public static class ExcelCsvExtensions
7+
{
8+
public static void WriteCsvExcelValue(this TextWriter writer, string value)
9+
{
10+
CsvWriter.WriteValue(writer, "=" + CsvUtility.WrapQuotes(value));
11+
}
12+
13+
public static void WriteCsvExcelHyperlink(this TextWriter writer, Uri link, string text = null)
14+
{
15+
if (link == null) throw new ArgumentNullException("link");
16+
17+
writer.WriteCsvExcelHyperlink(link.ToString(), text);
18+
}
19+
20+
public static void WriteCsvExcelHyperlink(this TextWriter writer, string link, string text = null)
21+
{
22+
WriteCsvExcelValue(writer, "=HYPERLINK(" + CsvUtility.WrapQuotes(link) + (text == null ? String.Empty : ("," + CsvUtility.WrapQuotes(text))) + ")");
23+
}
24+
25+
public static void WriteExcelValue(this CsvWriter writer, string value)
26+
{
27+
CsvWriter.WriteValue(writer.Target, "=" + CsvUtility.WrapQuotes(value));
28+
}
29+
30+
public static void WriteExcelHyperlink(this CsvWriter writer, Uri link, string text = null)
31+
{
32+
if (link == null) throw new ArgumentNullException("link");
33+
34+
WriteExcelHyperlink(writer, link.ToString(), text);
35+
}
36+
37+
public static void WriteExcelHyperlink(this CsvWriter writer, string link, string text = null)
38+
{
39+
WriteCsvExcelHyperlink(writer.Target, link, text);
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)