Files: Reading in Data

When we run a program, we often need that program to process some data from the user. Sometimes we have user input, and other times the data exists in the code itself:

string input = Console.ReadLine(); //Input from the user
 
int attack = 27; //Data just exists in code
int defense = 32;
int speed = 15;

But using Console.ReadLine for ALL of the input needed for a program would not be practical. So… how will our programs get user data? Turns out, most programs (including MS Word, Minecraft, and even Chrome) read data in from files. A good example is save files! These are literally just files that are created by the game whenever you hit “Save”. Each game uses their own bespoke format to store information about the current game state (inventory, location, progression). When the game is started again later, this file is opened and its contents converted into program data, so the player isn’t starting the game from scratch.

What’s a really simple way of reading/writing data into a file? Probably the easiest is to use the File class, which is a static class we can use for reading from files and writing data to them:

//All of the text is read from a file, into a string variable
string saveData = File.ReadAllText(@"C:\Users\ethan\GameFolder\save.txt");
 
//All of the text is read from a file, into an array of strings
string[] levelData = File.ReadAllLines(@"C:\Users\ethan\GameFolder\stage1.lvl");
 
//Later in our program... currentGameSave.saveData is just a string
File.WriteAllText(@"C:\Users\ethan\GameFolder\save.txt", currentGameSave.saveData);

File.ReadAllText and File.ReadAllLines are useful functions for reading in text files, and using their contents in our C# programs. We can parse the text contents into relevant types to store in variables, arrays, and classes. Once our program is done running, we can do the reverse: we can turn our data into text and write it all out into files, using File.WriteAllText.

But… how do we actually organize the data in our file? Well, that’s up to us to decide! For example, maybe we’re trying to record the number of Pokemon caught for each species in Red/Blue:

//PokemonList.txt
Bulbasaur
Ivysaur
Venusaur
...
//PokemonCatchCount.txt
3
2
1
...

Since all of the data is across different lines, we can use File.ReadAllLines to easily store each text line into its own string:

string[] pokemonNames = new string[151];
pokemonNames = File.ReadAllLines(@"C:\Users\ethan\GameFolder\PokemonList.txt");
 
string[] pokemonCatchCount = new string[151];
pokemonCatchCount = File.ReadAllLines(@"C:\Users\ethan\GameFolder\PokemonCatchCount.txt");
 
//Later after altering the data while game is running:
File.WriteAllLines(@"C:\Users\ethan\GameFolder\PokemonCatchCount.txt", pokemonCatchCount);

This is one way to store the data… but then if either of these files has a missing/extra line, ALL of the data is screwed up. Is there a better way to store this information? What if we could store everything in a single file? A couple examples are below:

//PokemonData.txt
Bulbasaur
3
Ivysaur
1
Venusaur
1
//PokemonData.txt
Bulbasaur 3
Ivysaur 1
Venusaur 1

You could also format the text so that the arrangement of the characters tells the programmer what the data is. A good example is a CSV (comma-separated value) file. This data format is everywhere, most often used by Microsoft Excel:

Charmander, 2, Fire
Charmeleon, 1, Fire
Charizard, 1, Fire, Flying
string[] pokemonData = new string[151];
pokemonData = File.ReadAllLines(@"C:\Users\ethan\GameFolder\PokemonData.txt");

But we have a problem… how do we separate different pieces of text on a single line? For example:

Charmander, 2, Fire

In our program, we used File.ReadAllLines to get the text. So this line of text would be in THIS variable, in our program:

//The value of this variable is "Charmander, 2, Fire"
pokemonData[0]

What we would ideally like is for each of these things be its own separate string! So… can we do File.ReadWords or something? Not quite. But we CAN split our string into multiple smaller ones!

String.Split

The split functionality of strings is just what we need here. Consider this string, which is separated by periods:

string myName = "Ethan.Andrew.Hall";

To split this string into separate strings, we could do the following:

string[] nameParts = myName.Split('.');

And we’d be left with:

nameParts[0] --> "Ethan"
nameParts[1] --> "Andrew"
nameParts[2] --> "Hall"

If we wanted to put each of these fields into a different variable in our program, we would do the following:

string[] pokemonData = new string[151];
pokemonData = File.ReadAllLines(@"C:\Users\ethan\GameFolder\PokemonData.txt");
 
for(int i = 0; i < 151; ++i)
{
	char[] separators = new char[1] {','};
	string[] dataStrings = pokemonData[i].Split(separators);
}

Data and File Formats

Remember that all of the formats we’ve just looked at are text. We could choose to store our data in binary! But editing this data isn’t very designer-friendly, so you’ll probably never deal with that kind of data yourself. An example:

123456789; //as an int, this is 4 bytes
"123456789"; //as a string, this is 9 * (1 byte per character)

Other formats exist, like JSON or XML. JSON is a popular text format, which you will absolutely see more of at DigiPen in your game project classes. JSON just specifies key value pairs, like so:

{
	"widget": 
	{
	    "debug": "on",
	    "window": 
	    {
	        "title": "Sample Konfabulator Widget",  
	        "name": "main_window",  
	        "width": 500,  
	        "height": 500
	    },  
	    "image": 
	    { 
	        "src": "Images/Sun.png",
	        "name": "sun1",  
	        "hOffset": 250,  
	        "vOffset": 250,  
	        "alignment": "center"
	    },  
	    "text": 
	    {
	        "data": "Click Here",
	        "size": 36,
	        "style": "bold",  
	        "name": "text1",  
	        "hOffset": 250,  
	        "vOffset": 100,  
	        "alignment": "center",
	        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
	    }
	}
}

You will need a particular class to parse through JSON or XML data. Some of these classes are included in the .Net Framework, and others may need to be downloaded separately.