15

Using a TextFieldParser from Microsoft.VisualBasic.FileIO it is possible to parse a CSV file like below:

using (TextFieldParser parser = new TextFieldParser(CSVPath))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    parser.HasFieldsEnclosedInQuotes = true;
    while (!parser.EndOfData) { string[] fields = parser.ReadFields(); }
}

However this relies on initialising the TextFieldParser with a CSV file path. Is it possible to have the same effect but while passing in a string that contains the data record itself?

For example with a CSV data record with the value of Data1,6.5,"Data3 ""MoreData""" (note the last data enclosed in quotes because of the escaped quotation marks) saved in a string variable, could I convert the data to a string array like this:

[0] = "Data1"
[1] = "6.5"
[2] = "Data3 \"MoreData\""
Callum Watkins
  • 2,844
  • 4
  • 29
  • 49

3 Answers3

30

A StringReader that contains the raw string can be passed into a new TextFieldParser and processed in the same way.

StringReader sr = new StringReader("Data1,6.5,\"Data3,\"\"MoreData\"\"\"");
using (var parser = new TextFieldParser(sr))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    parser.HasFieldsEnclosedInQuotes = true;
    while (!parser.EndOfData)
    {
        Console.WriteLine("Line:");
        var fields = parser.ReadFields();
        foreach (var field in fields)
        {
            Console.WriteLine("\tField: " + field);
        }
    }
}

Output to console:

Line:
    Field: Data1
    Field: 6.5
    Field: Data3,"MoreData"
Callum Watkins
  • 2,844
  • 4
  • 29
  • 49
8

You can also instantiate TextFieldParser from a Stream or from a TextReader. It doesn't have to be a string path. So, you can stream it whatever you like really, so long as you can get it into a stream. Could simply be a MemoryStream.

https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser%28v=vs.110%29.aspx

E.g.

using (var stream = new MemoryStream())
{
    var input = "A, B, C, D\r\n";
    input += "Jeremy,Paul,Linda,Joe\r\n";
    var bytes = System.Text.Encoding.Default.GetBytes(input);
    stream.Write(bytes, 0, bytes.Length);
    stream.Seek(0, SeekOrigin.Begin);
    using (var parser = new TextFieldParser(stream))
    {
        parser.TextFieldType = FieldType.Delimited;
        parser.SetDelimiters(",");
        parser.HasFieldsEnclosedInQuotes = true;
        while (!parser.EndOfData)
        {
            Console.WriteLine("Line:");
            var fields = parser.ReadFields();
            foreach (var field in fields)
            {
                Console.WriteLine("\tField: " + field);
            }
        }
    }
}
ManoDestra
  • 6,325
  • 6
  • 26
  • 50
  • 1
    Did you try with the proposed input? If yes and you made it work please add the relevant code.... – Steve Mar 24 '16 at 20:42
  • Not so easy... use an input data as in the OP. You cannot parse that text with the TextFieldParser... try with this: _Data1,6.5,\"Data3,\"MoreData\""_ (At least I am unable to do it) – Steve Mar 24 '16 at 21:39
  • 1
    Worked fine for me using my code, although the string you need to enter must be correct: `"Data1,6.5,\"Data3\",\"MoreData\"\r\n"`. The one you mentioned above was missing an escaped quotation mark after Data3. – ManoDestra Mar 24 '16 at 21:43
  • It is the missing quotation mark that creates the problem. Again look at the expected output. Data3,"MoreData" as the third element of the array, not Data3 as third and "MoreData" as fourth – Steve Mar 24 '16 at 21:45
  • My question may have been slightly confusing at first sight because of the way CSV files use quotation marks around fields with commas or quotation marks in them. There are only 3 fields, and the last is wrapped in quotation marks. – Callum Watkins Mar 24 '16 at 21:47
  • Well, that's broken content in that case. The parser itself won't handle an attempt at nested content like that. If you give it correctly formatted CSV data, then it's fine. The comma would perhaps have to be escaped to enable parsing of that string content. – ManoDestra Mar 24 '16 at 21:48
  • @ManoDestra As can be seen [here](http://stackoverflow.com/questions/769621/dealing-with-commas-in-a-csv-file) the formatting is actually correct, as the whole of the last field is wrapped in quotation marks, and so any commas are escaped, and to escape any further quotation marks, double quotes are used. – Callum Watkins Mar 24 '16 at 21:51
  • Well, after the edit I can agree with you, albeit you could simply use a StringReader to simplify code here... – Steve Mar 24 '16 at 21:51
  • @Steve Yes a StringReader does also work very nicely, I will add it as another answer. – Callum Watkins Mar 24 '16 at 21:54
-2

Using TextFieldParser is the easiest, simplest way to go and as said in accepted answer, you totally can instantiate it from a stream.

Despite that, I would like to complete the accepted answer with a valuable piece of information for all of you who landed here after a G**gle search :

I found a really simple parser reading CSV data with this piece of code :

var res = new List<string[]>();

using (TextFieldParser parser = new TextFieldParser(filepath))
{
    parser.CommentTokens = new string[] { "#" };
    parser.SetDelimiters(new string[] { ";" });
    parser.HasFieldsEnclosedInQuotes = true;

    // Skip over header line.
    parser.ReadLine();

    while (!parser.EndOfData)
    {
        res.Add(parser.ReadFields());
    }
}

be really carefull with parser.ReadLine() as it may give unwanted result if at least one of your header's fields contains CRLF. In such a case your first read line will contain the remaining part of your headers right after the first CRLF.

So please note that it will be much better to read your whole file with ReadFields which take great care of well formated fields (cf. the CSV RFC at https://www.rfc-editor.org/rfc/rfc4180) , including headers, then ignoring your first line if needed.

4180 RFC is complete enough to give you the way to go if you want to implement a proper CSV writer too.

Have fun with CSV guys.

Community
  • 1
  • 1