Search Unity

How to load an .CSV from disk during Runtime?

Discussion in 'Scripting' started by Rusoski, Aug 3, 2017.

  1. Rusoski

    Rusoski

    Joined:
    Nov 6, 2013
    Posts:
    63
    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:

     
  2. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    Just use StreamReader.

    Code (csharp):
    1.  
    2.                    using (var reader = new StreamReader(filename))
    3.                    {
    4.                        while (!reader.EndOfStream)
    5.                        {
    6.                            var line = reader.ReadLine();
    7.                            // do something with line... could even do a yield here if you're reading a large file
    8.                        }
    9.                    }
    10.  
    There are about a dozen .Net file APIs (like FileStream, etc.), but I happen to like StreamReader when reading text files.
     
    Rusoski likes this.
  3. Rusoski

    Rusoski

    Joined:
    Nov 6, 2013
    Posts:
    63
    Thanks I will give it a try!
     
  4. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    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.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class CSV {
    7.     public int headers;  //number of lines that are headers in the csv file
    8.     public List<List<string>> CSVData;
    9.  
    10.     //creates a CSV object using a text file or TextAsset, optional number of header lines, optional delimiter, and optional encoding
    11.     //the header lines is just so we can add human readable stuff to the top of the file, default to 2 lines
    12.     //the delimiter defaults to a comma, but can be set to other characters
    13.     public CSV (string CSVFilePath, int numOfHeaderLines = 2, char delimiter = ',', string encoding = "UTF-8")
    14.     {
    15.         string wholeFile = System.IO.File.ReadAllText(CSVFilePath, returnEncoding(encoding));  //open the text file and return the whole file contents
    16.         initialize(wholeFile, numOfHeaderLines, delimiter);
    17.      
    18.     }
    19.  
    20.     //constructor used with a TextAsset
    21.     public CSV (TextAsset CSVTextAsset, int numOfHeaderLines = 2, char delimiter = ',')
    22.     {
    23.         string wholeFile = CSVTextAsset.text;
    24.         initialize(wholeFile, numOfHeaderLines, delimiter);
    25.     }
    26.  
    27.     //constructor used when we are creating a brand new CSV from scratch
    28.     //must provide the header lines as a list of list of strings
    29.     public CSV (List<List<string>> headerLines)
    30.     {
    31.         CSVData = new List<List<string>>();
    32.         int numOfHeaders = 0;
    33.         foreach (List<string> line in headerLines)
    34.         {
    35.             numOfHeaders++;
    36.             CSVData.Add(line);
    37.         }
    38.         headers = numOfHeaders;
    39.     }
    40.  
    41.     private void initialize(string text, int numOfHeaderLines, char delimiter)
    42.     {
    43.         headers = numOfHeaderLines;
    44.         CSVData = new List<List<string>>();
    45.         List<string> eachLine;
    46.         eachLine = new List<string>();
    47.         eachLine.AddRange(text.Split("\n"[0]));
    48.         //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
    49.         eachLine.Reverse();
    50.         eachLine.RemoveAt(0);
    51.         eachLine.Reverse();
    52.         //Now split the individual lines into elements
    53.         foreach (string line in eachLine)
    54.         {
    55.             List<string> singleLine = new List<string>();
    56.             singleLine.AddRange(line.Split(delimiter));  //need to modify this area to handle quotation marks
    57.             CSVData.Add(singleLine);
    58.         }
    59.     }
    60.  
    61.     //using this method because the CSV constructor couldn't take a System.Text.Encoding object as a default value, so used a string instead
    62.     //So we call this to return a selected encoding to use on an external text file
    63.     private System.Text.Encoding returnEncoding(string encoding)
    64.     {
    65.         System.Text.Encoding returnValue;
    66.         switch (encoding)
    67.         {
    68.             case "ASCII":
    69.             case "ascii":
    70.                 returnValue = System.Text.Encoding.ASCII;
    71.                 break;
    72.             case "Unicode":
    73.             case "UTF16":
    74.             case "UTF-16":
    75.                 returnValue = System.Text.Encoding.Unicode;
    76.                 break;
    77.             case "UTF32":
    78.             case "UTF-32":
    79.                 returnValue = System.Text.Encoding.UTF32;
    80.                 break;
    81.             case "UTF8":
    82.             case "UTF-8":
    83.                 returnValue = System.Text.Encoding.UTF8;
    84.                 break;
    85.             case "UTF7":
    86.             case "UTF-7":
    87.                 returnValue = System.Text.Encoding.UTF7;
    88.                 break;
    89.             case "BigEndianUnicode":
    90.                 returnValue = System.Text.Encoding.BigEndianUnicode;
    91.                 break;
    92.             default:
    93.                 returnValue = System.Text.Encoding.UTF8;
    94.                 break;
    95.         }
    96.         return returnValue;
    97.     }
    98.  
    99.     //call this to dump csv back to disk
    100.     public void WriteToDisk(string CSVFilePath, char delimiter = ',', string encoding = "UTF8")
    101.     {
    102.         List<string> toWrite = new List<string>();
    103.         foreach (List<string> line in CSVData)
    104.         {
    105.             bool first = true;  //we don't throw the delimiter in on the first item in a line
    106.             string lineString = "";
    107.             foreach (string item in line)
    108.             {
    109.                 if (!first)
    110.                 {
    111.                     lineString = lineString + delimiter.ToString();
    112.                 }
    113.                 else
    114.                 {
    115.                     first = false;
    116.                 }
    117.                 lineString = lineString + item;  //add item to the line string
    118.             }
    119.             toWrite.Add(lineString);
    120.         }
    121.         System.IO.File.WriteAllLines(CSVFilePath, toWrite.ToArray(), returnEncoding(encoding));
    122.     }
    123.  
    124.     //add a new line here that is already a list of strings
    125.     public void AddLine(List<string> newLine)
    126.     {
    127.         CSVData.Add(newLine);
    128.     }
    129. }
    130.  

     
    adriaanwormgoor, TonyLi and Rusoski like this.
  5. Rusoski

    Rusoski

    Joined:
    Nov 6, 2013
    Posts:
    63
    Thanks for sharing, aslo this part looks important:

    Code (CSharp):
    1. private System.Text.Encoding returnEncoding(string encoding)
    2.     {
    3.         System.Text.Encoding returnValue;
    4.         switch (encoding)
    5.         {
    6.             case "ASCII":
    7.             case "ascii":
    8.                 returnValue = System.Text.Encoding.ASCII;
    9.                 break;
    10.             case "Unicode":
    11.             case "UTF16":
    12.             case "UTF-16":
    13.                 returnValue = System.Text.Encoding.Unicode;
    14.                 break;
    15.             case "UTF32":
    16.             case "UTF-32":
    17.                 returnValue = System.Text.Encoding.UTF32;
    18.                 break;
    19.             case "UTF8":
    20.             case "UTF-8":
    21.                 returnValue = System.Text.Encoding.UTF8;
    22.                 break;
    23.             case "UTF7":
    24.             case "UTF-7":
    25.                 returnValue = System.Text.Encoding.UTF7;
    26.                 break;
    27.             case "BigEndianUnicode":
    28.                 returnValue = System.Text.Encoding.BigEndianUnicode;
    29.                 break;
    30.             default:
    31.                 returnValue = System.Text.Encoding.UTF8;
    32.                 break;
    33.         }
    34.         return returnValue;
    35.     }
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Do keep Joe-Censored's comment on line 56 in mind:
    Code (csharp):
    1. //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.
     
    BlackPete likes this.
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Yep I put that comment in there as sort of a to do list for further improvement. Thanks for pointing it out.
     
  8. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    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.