Skip to content

Commit e1a2d94

Browse files
committed
fix(CsvReader.cs): Add a read method to read without skipping headers
1 parent 33970e5 commit e1a2d94

2 files changed

Lines changed: 103 additions & 6 deletions

File tree

src/CsvHelper/CsvReader.cs

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,72 @@ protected virtual void ValidateHeader(ClassMap map, List<InvalidHeader> invalidH
237237
}
238238
}
239239

240+
/// <summary>
241+
/// Advances the reader to the header record. Does not skip over records and should not be used to read records.
242+
/// You need to call <see cref="Read"/> then <see cref="ReadHeader"/>
243+
/// for the headers to be read.
244+
/// </summary>
245+
/// <returns>True if there are more records, otherwise false.</returns>
246+
public virtual bool PreHeaderRead()
247+
{
248+
// Don't forget about the async method below!
249+
250+
bool hasMoreData = parser.Read();
251+
hasBeenRead = true;
252+
253+
currentIndex = -1;
254+
255+
if (detectColumnCountChanges && hasMoreData)
256+
{
257+
if (prevColumnCount > 0 && prevColumnCount != parser.Count)
258+
{
259+
var csvException = new BadDataException(string.Empty, parser.RawRecord, context, "An inconsistent number of columns has been detected.");
260+
261+
var args = new ReadingExceptionOccurredArgs(csvException);
262+
if (readingExceptionOccurred?.Invoke(args) ?? true)
263+
{
264+
throw csvException;
265+
}
266+
}
267+
268+
prevColumnCount = parser.Count;
269+
}
270+
271+
return hasMoreData;
272+
}
273+
274+
/// <summary>
275+
/// Advances the reader to the header record. Does not skip over records and should not be used to read records.
276+
/// You need to call <see cref="ReadAsync"/> then <see cref="ReadHeader"/>
277+
/// for the headers to be read.
278+
/// </summary>
279+
/// <returns>True if there are more records, otherwise false.</returns>
280+
public virtual async Task<bool> PreHeaderReadAsync()
281+
{
282+
bool hasMoreData = await parser.ReadAsync().ConfigureAwait(false);
283+
hasBeenRead = true;
284+
285+
currentIndex = -1;
286+
287+
if (detectColumnCountChanges && hasMoreData)
288+
{
289+
if (prevColumnCount > 0 && prevColumnCount != parser.Count)
290+
{
291+
var csvException = new BadDataException(string.Empty, parser.RawRecord, context, "An inconsistent number of columns has been detected.");
292+
293+
var args = new ReadingExceptionOccurredArgs(csvException);
294+
if (readingExceptionOccurred?.Invoke(args) ?? true)
295+
{
296+
throw csvException;
297+
}
298+
}
299+
300+
prevColumnCount = parser.Count;
301+
}
302+
303+
return hasMoreData;
304+
}
305+
240306
/// <inheritdoc/>
241307
public virtual bool Read()
242308
{
@@ -843,7 +909,7 @@ public virtual IEnumerable<T> GetRecords<T>()
843909

844910
if (hasHeaderRecord && headerRecord == null)
845911
{
846-
if (!Read())
912+
if (!PreHeaderRead())
847913
{
848914
yield break;
849915
}
@@ -924,7 +990,7 @@ public virtual IEnumerable<object> GetRecords(Type type)
924990

925991
if (hasHeaderRecord && headerRecord == null)
926992
{
927-
if (!Read())
993+
if (!PreHeaderRead())
928994
{
929995
yield break;
930996
}
@@ -989,7 +1055,7 @@ public virtual IEnumerable<T> EnumerateRecords<T>(T record)
9891055

9901056
if (hasHeaderRecord && headerRecord == null)
9911057
{
992-
if (!Read())
1058+
if (!PreHeaderRead())
9931059
{
9941060
yield break;
9951061
}
@@ -1046,7 +1112,7 @@ public virtual IEnumerable<T> EnumerateRecords<T>(T record)
10461112

10471113
if (hasHeaderRecord && headerRecord == null)
10481114
{
1049-
if (!await ReadAsync().ConfigureAwait(false))
1115+
if (!await PreHeaderReadAsync().ConfigureAwait(false))
10501116
{
10511117
yield break;
10521118
}
@@ -1128,7 +1194,7 @@ public virtual async IAsyncEnumerable<object> GetRecordsAsync(Type type, [Enumer
11281194

11291195
if (hasHeaderRecord && headerRecord == null)
11301196
{
1131-
if (!await ReadAsync().ConfigureAwait(false))
1197+
if (!await PreHeaderReadAsync().ConfigureAwait(false))
11321198
{
11331199
yield break;
11341200
}
@@ -1194,7 +1260,7 @@ public virtual async IAsyncEnumerable<T> EnumerateRecordsAsync<T>(T record, [Enu
11941260

11951261
if (hasHeaderRecord && headerRecord == null)
11961262
{
1197-
if (!await ReadAsync().ConfigureAwait(false))
1263+
if (!await PreHeaderReadAsync().ConfigureAwait(false))
11981264
{
11991265
yield break;
12001266
}

tests/CsvHelper.Tests/Reading/ShouldSkipRecordTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,36 @@ public void ShouldSkipWithEmptyRows()
8383
Assert.Equal("1", csv.GetField(0));
8484
Assert.Equal("2", csv.GetField(1));
8585
}
86+
87+
[Fact]
88+
public void ShouldSkipIgnoreHeader()
89+
{
90+
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
91+
{
92+
ShouldSkipRecord = skipArgs => !skipArgs.Row[0]!.StartsWith("A")
93+
};
94+
95+
var parser = new ParserMock(config)
96+
{
97+
{ "Name" },
98+
{ "Arnold" },
99+
{ "Andrew" },
100+
{ "Betty" },
101+
{ "Frank" }
102+
};
103+
104+
var csv = new CsvReader(parser);
105+
var csvReader = new CsvReader(parser);
106+
var typeDef = new
107+
{
108+
Name = string.Empty
109+
};
110+
111+
var records = csvReader.GetRecords(typeDef).ToList();
112+
113+
Assert.Equal(2, records.Count);
114+
Assert.Equal("Arnold", records[0].Name);
115+
Assert.Equal("Andrew", records[1].Name);
116+
}
86117
}
87118
}

0 commit comments

Comments
 (0)