1

I've got a function that makes something equivalent to a web request, and it returns a formatted CSV. My goal is to now import this data into CsvHelper. However, I can't seem to get CSVParser to read from static text, only from a stream.

I could write the output to a file then read it back, but I feel that doesn't make much sense here.

I'm not tied down at all to CsvHelper, however I can't seem to find a CSV library that supports this behavior. How should I do this?

var csvString = functionThatReturnsCsv()
/* as string:
columnA,columnB
dataA,dataB
*/

// my goal
???.parse(csvString)
  • Note (to anyone visiting this question and looking at the answers) that using a StringReader (as shown by David Specht's answer) is the approach that is more convenient, and leads to simpler and ultimately more readable code compared to converting the string to a byte array to a MemoryStream. After all, any CSV parser would need to translate the byte stream back into text characters anyway. (A CSV parser not accepting a text source in some form - for example, a TextReader - is not really worth considering, imo) –  Jun 06 '19 at 19:24

2 Answers2

4

You can convert the string to a Stream in-memory and then use that as the source for your CSV reader:

public static Stream StringAsStream(string value)
{
    return StringAsStream(value, System.Text.Encoding.UTF8);
}

public static Stream StringAsStream(string value, System.Text.Encoding encoding)
{
    var bytes = encoding.GetBytes(value);
    return new MemoryStream(bytes);
}

Usage:

using (var stream = StringAsStream("hello"))
{
    // csv reading code here
}

or

using (var stream = StringAsStream("hello", Encoding.Ascii))
{
    // csv reading code here
}

Try it online

Note If you are reading from a source that can return a Stream (like a web request), you should use that Stream rather than doing this.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • Can I ask why you're using a using directive here rather than just using the var in the code? – I Yam Who I Yam Jun 05 '19 at 01:58
  • While you don't need to use Dispose with MemoryStream, it's good practice for any Stream you consume since they typically represent unmanaged resources (the garbage collector can't collect these). `using` will automatically clal `Dispose` once the using block completes - even in the event of an exception. See [here](https://stackoverflow.com/questions/75401/what-are-the-uses-of-using-in-c-sharp) for more info on what using does. – ProgrammingLlama Jun 05 '19 at 02:19
  • Ok. So if I wanted to make a function that would return the stdout of another program, I would return the standard output from the using definition, correct? (like this https://rextester.com/VII34075) – I Yam Who I Yam Jun 05 '19 at 02:21
  • 1
    That's what I ended up doing and it worked. Thanks for your help! – I Yam Who I Yam Jun 05 '19 at 03:49
3

You could use StringReader. The CsvReader constructor takes a TextReader argument rather than a Stream. If you did have a stream instead of a string, just replace StringReader with StreamReader.

public static void Main(string[] args)
{    
    using (var reader = new StringReader(FunctionThatReturnsCsv()))
    using (var csv = new CsvReader(reader))
    {
        var results = csv.GetRecords<Foo>().ToList();
    }            
}

public static string FunctionThatReturnsCsv()
{
    return "columnA,columnB\ndataA,dataB";
}

public class Foo
{
    public string columnA { get; set; }
    public string columnB { get; set; }
}          
David Specht
  • 7,784
  • 1
  • 22
  • 30