Skip to content

Commit d24e067

Browse files
authored
Fix NullReferenceException for empty headers (#69)
* Add reader tests for empty header rows * Fix NullReferenceException for empty header rows
1 parent 4355ed8 commit d24e067

3 files changed

Lines changed: 145 additions & 4 deletions

File tree

src/CSVReader.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ public CSVReader(StreamReader source, CSVSettings settings = null)
334334
if (_settings.HeaderRowIncluded)
335335
{
336336
var line = source.ReadLine();
337-
if (_settings.AllowSepLine)
337+
if (line != null && _settings.AllowSepLine)
338338
{
339339
var newDelimiter = CSV.ParseSepLine(line);
340340
if (newDelimiter != null)
@@ -345,7 +345,7 @@ public CSVReader(StreamReader source, CSVSettings settings = null)
345345
}
346346
}
347347

348-
Headers = CSV.ParseLine(line, _settings);
348+
Headers = CSV.ParseLine(line, _settings) ?? Array.Empty<string>();
349349
}
350350
else
351351
{
@@ -371,7 +371,7 @@ public CSVReader(Stream source, CSVSettings settings = null)
371371
if (_settings.HeaderRowIncluded)
372372
{
373373
var line = _stream.ReadLine();
374-
if (_settings.AllowSepLine)
374+
if (line != null && _settings.AllowSepLine)
375375
{
376376
var newDelimiter = CSV.ParseSepLine(line);
377377
if (newDelimiter != null)
@@ -382,7 +382,7 @@ public CSVReader(Stream source, CSVSettings settings = null)
382382
}
383383
}
384384

385-
Headers = CSV.ParseLine(line, _settings);
385+
Headers = CSV.ParseLine(line, _settings) ?? Array.Empty<string>();
386386
}
387387
else
388388
{

tests/BasicParseTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ public void ParseSepLineTest()
185185
Assert.AreEqual(',', CSV.ParseSepLine("sep=,"));
186186
Assert.AreEqual(',', CSV.ParseSepLine("sep = ,"));
187187
Assert.AreEqual(null, CSV.ParseSepLine("sep="));
188+
Assert.AreEqual(null, CSV.ParseSepLine("sep= "));
189+
Assert.AreEqual(null, CSV.ParseSepLine("sep = "));
188190
Assert.Throws<Exception>(() =>
189191
{
190192
CSV.ParseSepLine("sep= this is a test since separators can't be more than a single character");

tests/ReaderTest.cs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,145 @@ public void TestIssue62()
375375
Assert.AreEqual(desiredLines, outputLines);
376376
}
377377

378+
[Test]
379+
public void TestEmptyHeaderRow()
380+
{
381+
var settings = new CSVSettings()
382+
{
383+
LineSeparator = "\n",
384+
FieldDelimiter = ',',
385+
HeaderRowIncluded = true
386+
};
387+
388+
using (var cr = CSVReader.FromString("", settings))
389+
{
390+
Assert.AreEqual(0, cr.Headers.Length);
391+
Assert.AreEqual(0, cr.Count());
392+
}
393+
394+
using (var cr = CSVReader.FromString(" ", settings))
395+
{
396+
Assert.AreEqual(1, cr.Headers.Length);
397+
Assert.AreEqual(" ", cr.Headers[0]);
398+
Assert.AreEqual(0, cr.Count());
399+
}
400+
401+
using (var cr = CSVReader.FromString("\t", settings))
402+
{
403+
Assert.AreEqual(1, cr.Headers.Length);
404+
Assert.AreEqual("\t", cr.Headers[0]);
405+
Assert.AreEqual(0, cr.Count());
406+
}
407+
408+
using (var cr = CSVReader.FromString("\n", settings))
409+
{
410+
Assert.AreEqual(0, cr.Headers.Length);
411+
Assert.AreEqual(0, cr.Count());
412+
}
413+
414+
using (var cr = CSVReader.FromString(" \n", settings))
415+
{
416+
Assert.AreEqual(1, cr.Headers.Length);
417+
Assert.AreEqual(" ", cr.Headers[0]);
418+
}
419+
420+
using (var cr = CSVReader.FromString("\n ", settings))
421+
{
422+
Assert.AreEqual(0, cr.Headers.Length);
423+
var lines = cr.ToArray();
424+
Assert.AreEqual(1, lines.Length);
425+
Assert.AreEqual(1, lines[0].Length);
426+
Assert.AreEqual(" ", lines[0][0]);
427+
}
428+
}
429+
430+
[Test]
431+
public void TestSepLineEmptyHeaderRow()
432+
{
433+
var settings = new CSVSettings()
434+
{
435+
LineSeparator = "\n",
436+
FieldDelimiter = ',',
437+
HeaderRowIncluded = true,
438+
AllowSepLine = true
439+
};
440+
441+
// if without an = sign, "sep" is just a header
442+
using (var cr = CSVReader.FromString("sep", settings))
443+
{
444+
Assert.AreEqual(1, cr.Headers.Length);
445+
Assert.AreEqual("sep", cr.Headers[0]);
446+
Assert.AreEqual(0, cr.Count());
447+
}
448+
449+
// if with an = sign but no character specified, then "sep=" is also treated as a header
450+
// note that this is different from Excel's behaviour
451+
// Excel interprets this as a field delimiter setter, but it doesn't set it to any character
452+
// so all lines end up getting displayed in plaintext
453+
using (var cr = CSVReader.FromString("sep=", settings))
454+
{
455+
Assert.AreEqual(1, cr.Headers.Length);
456+
Assert.AreEqual("sep=", cr.Headers[0]);
457+
Assert.AreEqual(0, cr.Count());
458+
}
459+
460+
using (var cr = CSVReader.FromString("sep= ", settings))
461+
{
462+
Assert.AreEqual(1, cr.Headers.Length);
463+
Assert.AreEqual("sep= ", cr.Headers[0]);
464+
Assert.AreEqual(0, cr.Count());
465+
}
466+
467+
using (var cr = CSVReader.FromString("sep = ", settings))
468+
{
469+
Assert.AreEqual(1, cr.Headers.Length);
470+
Assert.AreEqual("sep = ", cr.Headers[0]);
471+
Assert.AreEqual(0, cr.Count());
472+
}
473+
474+
using (var cr = CSVReader.FromString("sep=;", settings))
475+
{
476+
Assert.AreEqual(0, cr.Headers.Length);
477+
Assert.AreEqual(0, cr.Count());
478+
}
479+
480+
using (var cr = CSVReader.FromString("sep=\n\nline1,line2", settings))
481+
{
482+
Assert.AreEqual(1, cr.Headers.Length);
483+
Assert.AreEqual("sep=", cr.Headers[0]);
484+
var lines = cr.ToArray();
485+
Assert.AreEqual(2, lines.Length);
486+
Assert.AreEqual(1, lines[0].Length);
487+
Assert.AreEqual("", lines[0][0]);
488+
Assert.AreEqual(2, lines[1].Length);
489+
Assert.AreEqual("line1", lines[1][0]);
490+
Assert.AreEqual("line2", lines[1][1]);
491+
}
492+
493+
using (var cr = CSVReader.FromString("sep=;\n\nline1;line2", settings))
494+
{
495+
Assert.AreEqual(0, cr.Headers.Length);
496+
var lines = cr.ToArray();
497+
Assert.AreEqual(1, lines.Length);
498+
Assert.AreEqual(2, lines[0].Length);
499+
Assert.AreEqual("line1", lines[0][0]);
500+
Assert.AreEqual("line2", lines[0][1]);
501+
}
502+
503+
using (var cr = CSVReader.FromString("sep=\n", settings))
504+
{
505+
Assert.AreEqual(1, cr.Headers.Length);
506+
Assert.AreEqual("sep=", cr.Headers[0]);
507+
Assert.AreEqual(0, cr.Count());
508+
}
509+
510+
using (var cr = CSVReader.FromString("sep=;\n", settings))
511+
{
512+
Assert.AreEqual(0, cr.Headers.Length);
513+
Assert.AreEqual(0, cr.Count());
514+
}
515+
}
516+
378517
#if HAS_ASYNC_IENUM
379518
[Test]
380519
public async Task TestAsyncReader()

0 commit comments

Comments
 (0)