Skip to content

Commit 2d112e1

Browse files
author
Oren (electricessence)
committed
Updated.
1 parent 0a3f62e commit 2d112e1

6 files changed

Lines changed: 101 additions & 72 deletions

File tree

CsvReader.cs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,75 +3,90 @@
33
using System.Collections.Generic;
44
using System.Diagnostics.Contracts;
55
using System.IO;
6+
using System.Threading.Tasks;
67

78
namespace Open.Text.CSV
89
{
910
public class CsvReader : DisposableBase
1011
{
1112
public CsvReader(StreamReader source)
1213
{
13-
_source = source;
14+
_source = source ?? throw new ArgumentNullException(nameof(source));
1415
}
1516

1617
StreamReader _source;
1718

18-
int _maxColumns;
19-
public int MaxColumns => _maxColumns;
2019

2120
protected override void OnDispose(bool calledExplicitly)
2221
{
2322
_source = null; // The intention here is if this object is disposed, then prevent further reading.
2423
}
2524

26-
public string[] ReadRow()
27-
{
28-
return GetRow(_source, ref _maxColumns);
29-
}
25+
public IEnumerable<string> ReadNextRow()
26+
=> GetNextRow(_source);
27+
28+
public ValueTask<IEnumerable<string>> ReadNextRowAsync()
29+
=> GetNextRowAsync(_source);
3030

31-
public string[][] ReadRows()
31+
public IEnumerable<IEnumerable<string>> ReadRows()
3232
{
33-
var rows = GetRows(_source, out var maxColumns);
34-
if (maxColumns > _maxColumns) _maxColumns = maxColumns;
35-
return rows;
33+
var s = _source;
34+
if (s == null)
35+
throw new ObjectDisposedException(GetType().ToString());
36+
Contract.EndContractBlock();
37+
38+
while (TryGetNextRow(s, out var row))
39+
yield return row;
3640
}
3741

38-
public static bool TryGetRow(StreamReader source, out string[] row, ref int maxColumns)
42+
public static bool TryGetNextRow(StreamReader source, out IEnumerable<string> row)
3943
{
4044
if (source == null)
4145
throw new ArgumentNullException(nameof(source));
4246
Contract.EndContractBlock();
4347

4448
if (!source.EndOfStream)
4549
{
46-
row = CsvUtility.GetLine(source.ReadLine(), ref maxColumns);
50+
row = CsvUtility.GetLine(source.ReadLine());
4751
return true;
4852
}
4953

5054
row = null;
5155
return false;
5256
}
5357

54-
public static string[] GetRow(StreamReader source, ref int maxColumns)
58+
public static ValueTask<IEnumerable<string>> GetNextRowAsync(StreamReader source)
59+
{
60+
if (source == null)
61+
throw new ArgumentNullException(nameof(source));
62+
Contract.EndContractBlock();
63+
64+
if (source.EndOfStream)
65+
return new ValueTask<IEnumerable<string>>(default(IEnumerable<string>));
66+
67+
return GetNextRowAsyncCore(source);
68+
}
69+
70+
static async ValueTask<IEnumerable<string>> GetNextRowAsyncCore(StreamReader source)
71+
=> source.EndOfStream ? null : CsvUtility.GetLine(await source.ReadLineAsync());
72+
73+
public static IEnumerable<string> GetNextRow(StreamReader source)
5574
{
56-
TryGetRow(source, out var row, ref maxColumns);
75+
TryGetNextRow(source, out var row);
5776
return row;
5877
}
5978

60-
public static string[][] GetRows(StreamReader source, out int maxColumns)
79+
public static IEnumerable<IEnumerable<string>> GetRows(StreamReader source)
6180
{
6281
if (source == null)
6382
throw new ArgumentNullException(nameof(source));
6483
Contract.EndContractBlock();
6584

66-
maxColumns = 0;
67-
var lines = new List<string[]>();
68-
while (TryGetRow(source, out var row, ref maxColumns))
69-
lines.Add(row);
70-
71-
return lines.ToArray();
85+
while (TryGetNextRow(source, out var row))
86+
yield return row;
7287
}
7388

74-
public static string[][] GetRowsFromFile(string filepath, out int maxColumns)
89+
public static IEnumerable<IEnumerable<string>> GetRowsFromFile(string filepath)
7590
{
7691
if (filepath == null)
7792
throw new ArgumentNullException(nameof(filepath));
@@ -80,11 +95,11 @@ public static string[][] GetRowsFromFile(string filepath, out int maxColumns)
8095
Contract.EndContractBlock();
8196

8297
if (File.Exists(filepath))
98+
{
8399
using (var s = new StreamReader((new FileInfo(filepath)).OpenRead()))
84-
return GetRows(s, out maxColumns);
85-
86-
maxColumns = 0;
87-
return new string[0][];
100+
foreach (var line in GetRows(s))
101+
yield return line;
102+
}
88103
}
89104
}
90105
}

CsvRow.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics.Contracts;
4-
using System.Linq;
54

65
namespace Open.Text.CSV
76
{
87
public class CsvRow
98
{
10-
public CsvRow(string[] headerRow)
9+
public CsvRow(in ReadOnlyMemory<string> headerRow)
1110
{
12-
_headerRow = headerRow ?? throw new ArgumentNullException(nameof(headerRow));
11+
_headerRow = headerRow;
1312
}
1413

15-
readonly string[] _headerRow;
14+
readonly ReadOnlyMemory<string> _headerRow;
1615

17-
public string[] HeaderRow => _headerRow.ToArray();
16+
public ReadOnlySpan<string> HeaderRow => _headerRow.Span;
1817

19-
public string[] GetRow(IDictionary<string, object> values)
18+
public IEnumerable<string> GetRow(IDictionary<string, object> values)
2019
{
2120
if (values == null) throw new ArgumentNullException(nameof(values));
2221
Contract.EndContractBlock();
2322

24-
return _headerRow
25-
.Select(key => values.TryGetValue(key, out var value) ? CsvUtility.ExportValue(value) : null)
26-
.ToArray();
23+
var len = _headerRow.Length;
24+
var header = _headerRow.ToArray();
25+
26+
for (var i = 0; i < len; i++)
27+
{
28+
yield return values.TryGetValue(header[i], out var value) ? CsvUtility.ExportValue(value) : null;
29+
}
2730
}
2831
}
2932
}

CsvUtility.cs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Globalization;
4+
using System.Linq;
45
using System.Text.RegularExpressions;
56

67
namespace Open.Text.CSV
@@ -9,44 +10,52 @@ public static class CsvUtility
910
{
1011
public const string LINE_PATTERN = "((?:\")([^\"]+)(?:\")|([^,\"]+))(?:\\s*)(?:,|$)";
1112
public static readonly Regex LinePattern = new Regex(LINE_PATTERN);
12-
internal static readonly string[] StringArrayEmpty = new string[0];
1313

14-
public static string[] GetLine(string line, ref int maxColumns)
15-
{
16-
if (line == null)
17-
return StringArrayEmpty;
14+
public static IEnumerable<string> GetLine(string line)
15+
=> string.IsNullOrEmpty(line)
16+
? Enumerable.Empty<string>()
17+
: GetLineCore(line);
1818

19+
static IEnumerable<string> GetLineCore(string line)
20+
{
1921
var mc = LinePattern.Matches(line);
2022
var c = mc.Count;
21-
if (c > maxColumns)
22-
maxColumns = c;
2323

24-
var result = new string[c];
2524
for (var i = 0; i < c; i++)
26-
result[i] = mc[i].Groups[1].Value.Trim('"');
27-
28-
return result;
25+
yield return mc[i].Groups[1].Value.Trim('"');
2926
}
3027

31-
public static string[][] GetArray(string csv, out int maxColumns)
32-
{
33-
maxColumns = 0;
34-
var lines = csv == null ? new string[0] : csv.Split('\n');
28+
//static string[] GetLineCore(in ReadOnlySpan<char> span)
29+
//{
30+
// if (span.IsEmpty)
31+
// return Array.Empty<string>();
3532

36-
var result = new List<string[]>(lines.Length);
37-
// ReSharper disable once LoopCanBeConvertedToQuery
38-
foreach (var line in lines)
39-
result.Add(GetLine(line, ref maxColumns));
33+
// var line = span.ToString();
34+
// var mc = LinePattern.Matches(line);
35+
// var c = mc.Count;
4036

41-
return result.ToArray();
42-
}
37+
// var result = new string[c];
38+
// for (var i = 0; i < c; i++)
39+
// {
40+
// var g = mc[i].Groups[1];
41+
// var s = span.Slice(g.Index, g.Length).Trim('"');
42+
// result[i] = s.ToString();
43+
// }
44+
45+
// return result;
46+
//}
4347

44-
public static string[][] GetArray(string csv)
48+
public static IEnumerable<IEnumerable<string>> GetLines(string csv)
49+
=> string.IsNullOrEmpty(csv)
50+
? Enumerable.Empty<IEnumerable<string>>()
51+
: GetLinesCore(csv);
52+
53+
static IEnumerable<IEnumerable<string>> GetLinesCore(string csv)
4554
{
46-
return GetArray(csv, out _);
55+
foreach(var line in csv.SplitAsEnumerable('\n'))
56+
yield return GetLineCore(line);
4757
}
4858

49-
5059
public const string NEWLINE = "\r\n";
5160
public static readonly Regex QUOTESNEEDED = new Regex("^\\s+|[,\n]|\\s+$");
5261

CsvWriter.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Open.Disposable;
22
using System;
3+
using System.Collections.Generic;
34
using System.Diagnostics.Contracts;
45
using System.IO;
56

@@ -19,12 +20,12 @@ protected override void OnDispose(bool calledExplicitly)
1920
Target = null; // The intention here is if this object is disposed, then prevent further writing.
2021
}
2122

22-
public void WriteRow(object[] row, bool forceQuotes = false)
23+
public void WriteRow<T>(IEnumerable<T> row, bool forceQuotes = false)
2324
{
2425
WriteRow(Target, row, forceQuotes);
2526
}
2627

27-
public void WriteRows(object[][] rows, bool forceQuotes = false)
28+
public void WriteRows<T>(IEnumerable<IEnumerable<T>> rows, bool forceQuotes = false)
2829
{
2930
WriteRows(Target, rows, forceQuotes);
3031
}
@@ -34,7 +35,7 @@ public static void WriteValue(TextWriter writer, object value = null, bool force
3435
writer.Write(CsvUtility.ExportValue(value, forceQuotes));
3536
}
3637

37-
public static void WriteRow(TextWriter writer, object[] row, bool forceQuotes = false)
38+
public static void WriteRow<T>(TextWriter writer, IEnumerable<T> row, bool forceQuotes = false)
3839
{
3940
if (row == null) throw new ArgumentNullException(nameof(row));
4041
Contract.EndContractBlock();
@@ -45,7 +46,7 @@ public static void WriteRow(TextWriter writer, object[] row, bool forceQuotes =
4546
writer.WriteLineNoTabs();
4647
}
4748

48-
public static void WriteRows(TextWriter writer, object[][] rows, bool forceQuotes = false)
49+
public static void WriteRows<T>(TextWriter writer, IEnumerable<IEnumerable<T>> rows, bool forceQuotes = false)
4950
{
5051
if (rows == null) throw new ArgumentNullException(nameof(rows));
5152
Contract.EndContractBlock();

ExcelCsvExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public static class ExcelCsvExtensions
77
{
88
public static void WriteCsvExcelValue(this TextWriter writer, string value)
99
{
10-
CsvWriter.WriteValue(writer, "=" + CsvUtility.WrapQuotes(value));
10+
CsvWriter.WriteValue(writer, '=' + CsvUtility.WrapQuotes(value));
1111
}
1212

1313
public static void WriteCsvExcelHyperlink(this TextWriter writer, Uri link, string text = null)
@@ -19,12 +19,12 @@ public static void WriteCsvExcelHyperlink(this TextWriter writer, Uri link, stri
1919

2020
public static void WriteCsvExcelHyperlink(this TextWriter writer, string link, string text = null)
2121
{
22-
WriteCsvExcelValue(writer, "=HYPERLINK(" + CsvUtility.WrapQuotes(link) + (text == null ? string.Empty : ("," + CsvUtility.WrapQuotes(text))) + ")");
22+
WriteCsvExcelValue(writer, $"=HYPERLINK({CsvUtility.WrapQuotes(link)}{(text == null ? string.Empty : (',' + CsvUtility.WrapQuotes(text)))})");
2323
}
2424

2525
public static void WriteExcelValue(this CsvWriter writer, string value)
2626
{
27-
CsvWriter.WriteValue(writer.Target, "=" + CsvUtility.WrapQuotes(value));
27+
CsvWriter.WriteValue(writer.Target, '=' + CsvUtility.WrapQuotes(value));
2828
}
2929

3030
public static void WriteExcelHyperlink(this CsvWriter writer, Uri link, string text = null)

Open.Text.CSV.csproj

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ Part of the "Open" set of libraries.</Description>
1616
<RepositoryUrl>https://github.com/electricessence/Open.Text.CSV/</RepositoryUrl>
1717
<RepositoryType>git</RepositoryType>
1818
<PackageTags>csv, csharp, dotnetcore, textwriter, streamreader</PackageTags>
19-
<Version>1.1.0</Version>
20-
<AssemblyVersion>1.1.0.0</AssemblyVersion>
21-
<FileVersion>1.1.0.0</FileVersion>
22-
<PackageReleaseNotes>Updated to .NET Standard for compatability.</PackageReleaseNotes>
19+
<Version>2.0.0</Version>
20+
<AssemblyVersion>2.0.0.0</AssemblyVersion>
21+
<FileVersion>2.0.0.0</FileVersion>
22+
<PackageReleaseNotes></PackageReleaseNotes>
2323
</PropertyGroup>
2424

2525
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -44,7 +44,8 @@ Part of the "Open" set of libraries.</Description>
4444

4545
<ItemGroup>
4646
<PackageReference Include="Open.Disposable" Version="1.2.1" />
47-
<PackageReference Include="Open.Text" Version="1.1.0" />
47+
<PackageReference Include="Open.Text" Version="2.0.4" />
48+
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.2" />
4849
</ItemGroup>
4950

5051
</Project>

0 commit comments

Comments
 (0)