How do I pass a collection of strings as a TextReader?
Asked Answered
L

2

6

I am using the CSVHelper library, which can extract a list of objects from a CSV file with just three lines of code:

var streamReader = // Create a reader to your CSV file.
var csvReader = new CsvReader( streamReader );
List<MyCustomType> myData = csvReader.GetRecords<MyCustomType>();

However, by file has nonsense lines and I need to skip the first ten lines in the file. I thought it would be nice to use LINQ to ensure 'clean' data, and then pass that data to CsvFReader, like so:

public TextReader GetTextReader(IEnumerable<string> lines)
{
    // Some magic here. Don't want to return null;
    return TextReader.Null;
}
public IEnumerable<T> ExtractObjectList<T>(string filePath) where T : class
{
    var csvLines = File.ReadLines(filePath)
                        .Skip(10)
                        .Where(l => !l.StartsWith(",,,"));
    var textReader = GetTextReader(csvLines);
    var csvReader = new CsvReader(textReader);
    csvReader.Configuration.ClassMapping<EventMap, Event>();
    return csvReader.GetRecords<T>();
}

But I'm really stuck into pushing a 'static' collection of strings through a stream like a TextReaer.

My alternative here is to process the CSV file line by line through CsvReader and examine each line before extracting an object, but I find that somewhat clumsy.

Laborer answered 31/3, 2013 at 6:49 Comment(2)
I would probably go with the 2nd option, then you don't need to keep the entire file in memory (like you are doing now)Remuneration
Yes, having thought about it, I agree. The file is for parkade, those that use a a non-cash system, entrances and exists from all over the country and will surely be quite big. Although that is the Unseen Option. The 2nd Option above still does an all at once process of proper records.Laborer
K
11

The StringReader Class provides a TextReader that wraps a String. You could simply join the lines and wrap them in a StringReader:

public TextReader GetTextReader(IEnumerable<string> lines)
{
    return new StringReader(string.Join("\r\n", lines));
}
Kerb answered 31/3, 2013 at 7:5 Comment(2)
What I have just found out. I would still like to do the whole task in one phat LINQ query, as I am want to do, but this is fine, thanks.Laborer
Better use Environment.NewLine as the separatorRuggiero
H
1

An easier way would be to use CsvHelper to skip the lines.

// Skip rows.
csvReader.Configuration.IgnoreBlankLines = false;
csvReader.Configuration.IgnoreQuotes = true;
for (var i = 0; i < 10; i++)
{
    csvReader.Read();
}
csvReader.Configuration.IgnoreBlankLines = false;
csvReader.Configuration.IgnoreQuotes = false;

// Carry on as normal.
var myData = csvReader.GetRecords<MyCustomType>;

IgnoreBlankLines is turned off in case any of those first 10 rows are blank. IgnoreQuotes is turned off so you don't get any BadDataExceptions if those rows contain a ". You can turn them back on after for normal functionality again.

If you don't know the amount of rows and need to test based on row data, you can just test csvReader.Context.Record and see if you need to stop. In this case, you would probably need to manually call csvReader.ReadHeader() before calling csvReader.GetRecords<MyCustomType>().

Hegelianism answered 4/1, 2019 at 3:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.