How to load an .CSV from disk during Runtime?

I have managed to read a .CSV file from the resources folder with Resources.Load(), but I really need it to be read at runtime, because some values will change.

I saw this tutorial:

Just use StreamReader.

                   using (var reader = new StreamReader(filename))
                   {
                       while (!reader.EndOfStream)
                       {
                           var line = reader.ReadLine();
                           // do something with line... could even do a yield here if you're reading a large file
                       }
                   }

There are about a dozen .Net file APIs (like FileStream, etc.), but I happen to like StreamReader when reading text files.

1 Like

Thanks I will give it a try!

I wrote this to use in a few projects. I don’t remember if I’ve tested the writing CSV to disk part, but it probably works. My guess is you’re currently using a TextAsset to read from. It is pretty easy to just read a file outside of your project from disk though and reuse the majority of your code.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class CSV {
    public int headers;  //number of lines that are headers in the csv file
    public List<List<string>> CSVData;

    //creates a CSV object using a text file or TextAsset, optional number of header lines, optional delimiter, and optional encoding
    //the header lines is just so we can add human readable stuff to the top of the file, default to 2 lines
    //the delimiter defaults to a comma, but can be set to other characters
    public CSV (string CSVFilePath, int numOfHeaderLines = 2, char delimiter = ',', string encoding = "UTF-8")
    {
        string wholeFile = System.IO.File.ReadAllText(CSVFilePath, returnEncoding(encoding));  //open the text file and return the whole file contents
        initialize(wholeFile, numOfHeaderLines, delimiter);
     
    }

    //constructor used with a TextAsset
    public CSV (TextAsset CSVTextAsset, int numOfHeaderLines = 2, char delimiter = ',')
    {
        string wholeFile = CSVTextAsset.text;
        initialize(wholeFile, numOfHeaderLines, delimiter);
    }

    //constructor used when we are creating a brand new CSV from scratch
    //must provide the header lines as a list of list of strings
    public CSV (List<List<string>> headerLines)
    {
        CSVData = new List<List<string>>();
        int numOfHeaders = 0;
        foreach (List<string> line in headerLines)
        {
            numOfHeaders++;
            CSVData.Add(line);
        }
        headers = numOfHeaders;
    }

    private void initialize(string text, int numOfHeaderLines, char delimiter)
    {
        headers = numOfHeaderLines;
        CSVData = new List<List<string>>();
        List<string> eachLine;
        eachLine = new List<string>();
        eachLine.AddRange(text.Split("\n"[0]));
        //I'm getting an extra line at the end of the csv file, so this little hack reverses the list, deletes the first line, then reverses again
        eachLine.Reverse();
        eachLine.RemoveAt(0);
        eachLine.Reverse();
        //Now split the individual lines into elements
        foreach (string line in eachLine)
        {
            List<string> singleLine = new List<string>();
            singleLine.AddRange(line.Split(delimiter));  //need to modify this area to handle quotation marks
            CSVData.Add(singleLine);
        }
    }

    //using this method because the CSV constructor couldn't take a System.Text.Encoding object as a default value, so used a string instead
    //So we call this to return a selected encoding to use on an external text file
    private System.Text.Encoding returnEncoding(string encoding)
    {
        System.Text.Encoding returnValue;
        switch (encoding)
        {
            case "ASCII":
            case "ascii":
                returnValue = System.Text.Encoding.ASCII;
                break;
            case "Unicode":
            case "UTF16":
            case "UTF-16":
                returnValue = System.Text.Encoding.Unicode;
                break;
            case "UTF32":
            case "UTF-32":
                returnValue = System.Text.Encoding.UTF32;
                break;
            case "UTF8":
            case "UTF-8":
                returnValue = System.Text.Encoding.UTF8;
                break;
            case "UTF7":
            case "UTF-7":
                returnValue = System.Text.Encoding.UTF7;
                break;
            case "BigEndianUnicode":
                returnValue = System.Text.Encoding.BigEndianUnicode;
                break;
            default:
                returnValue = System.Text.Encoding.UTF8;
                break;
        }
        return returnValue;
    }

    //call this to dump csv back to disk
    public void WriteToDisk(string CSVFilePath, char delimiter = ',', string encoding = "UTF8")
    {
        List<string> toWrite = new List<string>();
        foreach (List<string> line in CSVData)
        {
            bool first = true;  //we don't throw the delimiter in on the first item in a line
            string lineString = "";
            foreach (string item in line)
            {
                if (!first)
                {
                    lineString = lineString + delimiter.ToString();
                }
                else
                {
                    first = false;
                }
                lineString = lineString + item;  //add item to the line string
            }
            toWrite.Add(lineString);
        }
        System.IO.File.WriteAllLines(CSVFilePath, toWrite.ToArray(), returnEncoding(encoding));
    }

    //add a new line here that is already a list of strings
    public void AddLine(List<string> newLine)
    {
        CSVData.Add(newLine);
    }
}
3 Likes

Thanks for sharing, aslo this part looks important:

private System.Text.Encoding returnEncoding(string encoding)
    {
        System.Text.Encoding returnValue;
        switch (encoding)
        {
            case "ASCII":
            case "ascii":
                returnValue = System.Text.Encoding.ASCII;
                break;
            case "Unicode":
            case "UTF16":
            case "UTF-16":
                returnValue = System.Text.Encoding.Unicode;
                break;
            case "UTF32":
            case "UTF-32":
                returnValue = System.Text.Encoding.UTF32;
                break;
            case "UTF8":
            case "UTF-8":
                returnValue = System.Text.Encoding.UTF8;
                break;
            case "UTF7":
            case "UTF-7":
                returnValue = System.Text.Encoding.UTF7;
                break;
            case "BigEndianUnicode":
                returnValue = System.Text.Encoding.BigEndianUnicode;
                break;
            default:
                returnValue = System.Text.Encoding.UTF8;
                break;
        }
        return returnValue;
    }

Do keep Joe-Censored’s comment on line 56 in mind:

//need to modify this area to handle quotation marks

This is a valid single row (that spans two lines in the text file):

field one,“field
two”

And these are valid, too:

field one,“field,two”

field one,“”“Hi!” she said""

Just something to keep in mind if your CSV file contains content like this, or if you’re exporting to CSV from something that does this under the hood like Excel or Google Sheets.

1 Like

Yep I put that comment in there as sort of a to do list for further improvement. Thanks for pointing it out.

Thanks for sharing your code! I only pointed it out as a reminder because Excel gave me no end of headaches with all the weird forms that fields in quotes can take. A simple recursive descent parser is probably the easiest way to handle it.