Lightweight CSV reader for Unity

Managing game data in Unity scene objects can get really painful especially when more than one people needs to edit the same thing. It’s usually better to have some data in CSV file where it can be controlled centrally.

When facing this problem I couldn’t find CSV reader for Unity that would have been exactly what I need. i.e. not be very buggy, huge or require new assembly dependencies.
So here is very simple CSV reader for Unity. It is bare bones but still robust enough parse quoted text and comma’s inside text. Reader assumes that csv files are in Resources folder of your Unity project.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class CSVReader
{
	static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
	static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";
	static char[] TRIM_CHARS = { '\"' };

	public static List<Dictionary<string, object>> Read(string file)
	{
		var list = new List<Dictionary<string, object>>();
		TextAsset data = Resources.Load (file) as TextAsset;

		var lines = Regex.Split (data.text, LINE_SPLIT_RE);

		if(lines.Length <= 1) return list;

		var header = Regex.Split(lines[0], SPLIT_RE);
		for(var i=1; i < lines.Length; i++) {

			var values = Regex.Split(lines[i], SPLIT_RE);
			if(values.Length == 0 ||values[0] == "") continue;

			var entry = new Dictionary<string, object>();
			for(var j=0; j < header.Length && j < values.Length; j++ ) {
				string value = values[j];
				value = value.TrimStart(TRIM_CHARS).TrimEnd(TRIM_CHARS).Replace("\\", "");
				object finalvalue = value;
				int n;
				float f;
				if(int.TryParse(value, out n)) {
					finalvalue = n;
				} else if (float.TryParse(value, out f)) {
					finalvalue = f;
				}
				entry[header[j]] = finalvalue;
			}
			list.Add (entry);
		}
		return list;
	}
}

Drop this in your Scripts folder as CSVReader.cs and make folder Resources under your Assets folder. Put there any CSV files you want to use.

Example CSV file example.csv in Resources folder.

name,age,speed,description
cat,2,4.5,"cat stalks, jumps and meows"
dog,2,5.5,dog barks
fish,1,1.1,fish swims

How to use

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

public class Main : MonoBehaviour {

	void Awake() {

		List<Dictionary<string,object>> data = CSVReader.Read ("example");

		for(var i=0; i < data.Count; i++) {
			print ("name " + data[i]["name"] + " " +
			       "age " + data[i]["age"] + " " +
			       "speed " + data[i]["speed"] + " " +
			       "desc " + data[i]["description"]);
		}

	}

	// Use this for initialization
	void Start () {
	}

	// Update is called once per frame
	void Update () {

	}
}

See full example code in Github: https://github.com/tikonen/blog/tree/master/csvreader

19 Responses to Lightweight CSV reader for Unity

  1. Sangil Yoon says:

    Thx so much~! it was really helpful. I’ve looked tons of CSVReaders on the internet and I guess this one is most powerful and the smallest in the world

  2. ariff says:

    how do we run and observe the output? since i can’t seem to start the Main.cs with or without debugging. Should we press play in unity? Then where we can see the printed data in example.csv? Please reply. Thank you!

    • tikonen says:

      You don’t understand principles of Unity, please follow basic tutorials before attempting more advanced stuff.

  3. rianluthfi says:

    Thank you for you’re sharing, its very helpful

  4. rianluthfi says:

    Hello may i ask question.
    how you can read CSV from outside unityfile, thanks before

  5. xyzDave says:

    Great bit of code, used it in an app to convert CSV data.
    Minor fix if you are interested – I noticed it wont handle a string containing quotes, especially if the quoted section is at the end e.g. “Hello said \”Bob\””. The trim strips all quotes at the end.
    Add a function:
    public static string UnquoteString(string str)
    {
    if (String.IsNullOrEmpty(str))
    return str;

    int length = str.Length;
    if (length > 1 && str[0] == ‘\”‘ && str[length – 1] == ‘\”‘)
    str = str.Substring(1, length – 2);

    return str;
    }
    then change the inner loop to be:

    for(var j=0; j < header.Length && j < values.Length; j++ )
    {
    string value = values[j].Replace("\"\"","\"");
    value = UnquoteString(value);
    value = value.Replace("\\", "");
    ….

    Seems to work nicely.
    Thanks again for the code.

  6. thanks for writing this mate!

  7. leland says:

    I am currently reading a CSV file which was splitted with semi-colons with the script you provided, but the splitting doesn’t seem to work.
    Could do please drop me a little hint here? thank you !

    • tikonen says:

      The cvs reader assumes comma (‘,’) separated columns. You need to modify the regexp to this:
      “static string SPLIT_RE = @”;(?=(?:[^””]*””[^””]*””)*(?![^””]*””))”;”

  8. Nico says:

    I need to give my players the ability to provide their own CSV file in either the persistentDataPath or the streamingDataPath. Because of this, using Resources.Load(file) is not a possible solution for me. Is there another way to feed your reader a file/file path and it still function? I read it’s impossible to generate a TextAsset at runtime, so maybe a way to deal with a pure string of a csv file. Considering TextAsset.text is a string, would it work if I just replace “data.text” with a serialized version of a file in another file path? Thanks for reading!

    • tikonen says:

      Correct, you can pass string from any source to the Regex.Split as long as it presents a valid CSV data (newline separated lines and comma separated columns.). For example “col1,col2\n1,2\n3,4\n” would be parsed correctly.

  9. Antonio Cham says:

    Hello the csv works amazing, just when it have a
    line break in the same cell, then the csv stop to read in that place and it mess everything, how to make it can read the break line in the same cell?

    • tikonen says:

      This parser does not support newlines in cells. Generally you should avoid newlines in CSV files as most tools are not able to handle them, at least easily. Common workaround is to use markups. e.g. “This isline break” and then replace the markup after parsing. For example: val.Replace(“”, “\n”);

  10. Rupert Stamp says:

    Thanks for this. The reader seems to combine ints. So a table value is 10,11,12 and the reader creates an entry of 101112.
    Seems to work great for strings though! Any help?

    • tikonen says:

      You can’t use the separator character as a cell value. You need to modify splitter regulator expression to either use ‘;’ as column separator to to support escape coding for the separator.

      Splitter regular expression for ‘;’ separator is simple change, just replace , with ; in the expression: “;(?=(?:[^””]*””[^””]*””)*(?![^””]*””))”

      Splitter regular expression for escape coding: “/(?<!\\),(?=(?:[^""]*""[^""]*"")*(?![^""]*""))/". This allows you to escape code cell values with ','. For example value "10,11,12" would be coded as "10\,11\,12"

      • Rupert Stamp says:

        Thanks for the prompt reply!
        Hmm, okay. The strange thing is we have cell data such as `London,Paris,Turkey`
        Which works and is seperated correctly using the expression in your original example.
        If the next cell contains numbers e.g `10,11,12` then this data outputs as `101112` and is not seperated

      • tikonen says:

        Maybe the string is quoted? Parser supports commas inside quotes. Your data row in CSV might look like this:
        something, “London,Paris,Turkey”,10,11,12

  11. Jesse Stuck says:

    Hi

    Thank you for your CSV reader! it’s an amazing tool.

    I am just wondering, is it possible to use this to construct a meshed object using data that is imported? If so, how would i go about doing this? as in reading and extracting the datapoints from the list of dictionaries?

    Many thanks!

    • tikonen says:

      Meshes are usually quite big and composed of separate parts (vertices, faces and edges). CSV would not be good medium in my opinion. I would look into some binary format. Maybe you can use C# BinaryFormatter and FileStreams.

Leave a reply to rianluthfi Cancel reply