2

I'm decoding csv-file from base64 string:

byte[] input;
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, new FromBase64Transform(), CryptoStreamMode.Write))
using (var tr = new StreamWriter(cs))
{
    tr.Write(data);
    tr.Flush();
    input = ms.ToArray();
}

How to simply read decoded file by string? For example, similar way like how we can read a file from HttpWebResponse stream:

using (StreamReader input = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(1251), true))
{
    while (!input.EndOfStream)
    {
         string row = input.ReadLine();
    }
}
shmnff
  • 647
  • 2
  • 15
  • 31
  • @Nkosi the way of passing the memory stream to the reader is exactly what I'm asking for – shmnff Feb 27 '18 at 05:30
  • @Nkosi Your solution was workable even not so perfect. I have to parse csv file from base64 string and write data to database. – shmnff Feb 27 '18 at 05:54
  • You know, technically, the csv format allows line breaks within cells. If your input may come from converted spreadsheet files, you may look into [a more advanced approach](https://stackoverflow.com/a/48809517/395685) than reading line by line. – Nyerguds Feb 27 '18 at 06:09

2 Answers2

4

Do not bother converting to array, just reset memory stream position to 0 and then pass the memory stream to the reader and then read to end.

using (var ms = new MemoryStream()) {
    using (var cs = new CryptoStream(ms, new FromBase64Transform(), CryptoStreamMode.Write)) {
        using (var tr = new StreamWriter(cs)) {
            tr.Write(data);
            tr.Flush();
            ms.Position = 0;
            using (var reader = new StreamReader(ms, Encoding.GetEncoding(1251), true)) {
                string csv = reader.ReadToEnd();
                //OR
                //while (!reader.EndOfStream) {
                //    var line = reader.ReadLine();
                //}
            }
        }
    }
}

Here is another option

byte[] input;
using (var ms = new MemoryStream()) {
    using (var cs = new CryptoStream(ms, new FromBase64Transform(), CryptoStreamMode.Write)) {
        using (var tr = new StreamWriter(cs)) {
            tr.Write(data);
            tr.Flush();
            input = ms.ToArray();
        }
    }
}
using (var ms = new MemoryStream(input)) {
    using (var reader = new StreamReader(ms, Encoding.GetEncoding(1251), true)) {
        string csv = reader.ReadToEnd();
        //OR
        //while (!reader.EndOfStream) {
        //    var line = reader.ReadLine();
        //}
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I had been reading into a MemoryStream, then needed to read from it and was constantly getting ReadLine() return null in my stream reader. The 'Yyou've got to be joking' moment I had when I saw you set the position to 0 in the memory stream... Can't beleive it doesn't default to reading from the beginning of the memory stream in the stream reader. Thank you so much for this answer! – Stevo Apr 05 '19 at 01:38
2

You can initialize the MemoryStream from an array to begin with; then you don't have to write anything to a stream. Since Base64, by definition, is pure ascii text, you just convert the input string to bytes using ASCII encoding.

Though, if you're parsing CSV, there are better output options than just text read line by line. Technically, CSV format can contain line breaks inside fields, a feature supported by pretty much anything that writes CSV from spreadsheet files (like MS Excel). To support this, the line-by-line-reading approach is too simple. The .Net framework contains a native CSV reader, though, albeit hidden quite well in the Microsoft.VisualBasic classes. Since .Net is one framework, though, there's nothing preventing you from adding the reference and using it in C#. The class is TextFieldParser, from Microsoft.VisualBasic.FileIO.

public static List<String[]> ParseBase64Csv(String data, Encoding encoding, Char separator, Boolean ignoreEmptyLines)
{
    List<String[]> splitLines = new List<String[]>();
    using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(data)))
    using (FromBase64Transform tr = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces))
    using (CryptoStream cs = new CryptoStream(ms, tr, CryptoStreamMode.Read))
    using (StreamReader sr = new StreamReader(cs, encoding))
    using (TextFieldParser tfp = new TextFieldParser(sr))
    {
        tfp.TextFieldType = FieldType.Delimited;
        tfp.Delimiters = new String[] { separator.ToString() };
        while (true)
        {
            try
            {
                String[] curLine = tfp.ReadFields();
                if (curLine == null)
                    break;
                if (ignoreEmptyLines && (curLine.Length == 0 || curLine.All(x => String.IsNullOrEmpty(x) || String.IsNullOrEmpty(x.Trim()))))
                    continue;
                splitLines.Add(curLine);
            }
            catch (MalformedLineException mfle)
            {
                // do something with errors here.
            }
        }
    }
    return splitLines;
}
Nyerguds
  • 5,360
  • 1
  • 31
  • 63