---
marp: true
paginate: true
math: mathjax
theme: buutti
title: 12. Files and Streams
---
# Files and Streams
## Overview
* Files and Streams
* Reading from a File
* Writing to a File
* JSON Serialization
* JSON Deserialization
## Files and streams
* The .NET library [contains classes](https://learn.microsoft.com/en-us/dotnet/api/system.io?view=net-7.0
) for manipulating files and directories
* The `FileStream` class contains methods for read and write operations for files
* The goal is usually to turn the data in your code into an outside resource, such as a .txt file, and vice versa

## Using the `File` Class
* To get started, add the following namespace to your project:
```csharp
using System.IO;
```
* `System.IO` provides basic directory and folder support, and allows reading and writing to files and data streams
* Check if a file exists with the `.Exists()` method:
```csharp
string path = @"C:\\Users\\Public\\TestFolder\\TestFile.txt";
if (!File.Exists(path))
{
Console.WriteLine($"The file {path} does not exist.");
}
```
## Reading from a file
* Get the entire document as a string with `File.ReadAllText`
* Get all lines of a document as a string array with `File.ReadAllLines`
```csharp
string path = @"C:\\Users\\Public\\TestFolder\\TestFile.txt";
if (!File.Exists(path))
{
Console.WriteLine($"The file {path} does not exist.");
}
else
{
var allLines = File.ReadAllLines(path);
foreach (string line in allLines)
Console.WriteLine(line);
}
```
## Writing to a file
* Writing to a file is done with `File.WriteAllText` and `File.WriteAllLines` methods:
```csharp
string path = @"C:\Users\Public\TestFolder\TestFile.txt";
string[] lyrics = new string[]
{
"Hello",
"Is it me you're looking for",
"I can see it in your eyes",
"I can see it in your smile"
};
if (File.Exists(path))
File.Delete(path); // If file exists, deletes the old file
File.WriteAllLines(path, lyrics); // Creates a new file with lyrics
```
## Exercise 1: A simple parser
1. Create a console application which keeps asking the user for a string. Each string is added into a list of strings.
2. If the user returns an empty string, the program asks for a path to a .txt file.
3. If the file exists, its contents are replaced with each string written to a separate line. If not, a new file with the contents is created.
## JSON
* [JSON](https://www.json.org/json-en.html) (JavaScript Object Notation) is a way to represent code objects in text form, a "data-interchange format"
* Enables reading and writing series of objects into a text file
* Consists of two types of structures
* Objects, or unordered name-value pairs
* Ordered lists of values
* Language-independent, although the JSON notation resembles the C-family of languages (C#, Java, JavaScript etc.)
### JSON Serialization: set-up
* .NET projects do not directly support JSON serialization
* A *__NuGet package__* for that exists, called `Newtonsoft.Json`
* Install the package from _Tools > NuGet Package Manager > Manage NuGet Packages for Solution…_
* Select _Browse_
* The _Newtonsoft.Json_ should be the topmost package. If not, search for it from the search bar
* Select the package, check the checkbox next to _Project_ and click _Install_
### JSON Serialization: an example
You can *__serialize__* an object (e.g. a list) into a JSON string with the `JsonConvert.SerializeObject()` method:
```csharp
class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
static void Main(string[] args)
{
List
products = new List
{
new Product{Id=1, Name="Awesome Product"},
new Product{Id=2, Name="Terrible Product"}
};
string jsonString = JsonConvert.SerializeObject(products);
File.WriteAllText(@"C:\Users\OMISTAJA\source\repos\Example\Products.json", jsonString);
}
```
The contents of the output file Products.json are as follows:

## JSON Deserialization
*__Deserializing__* the contents of a JSON file into an object in your program can be done with the `JsonConvert.DeserializeObject()` method:
```csharp
string json = File.ReadAllText(
@"C:\Users\OMISTAJA\source\repos\Luentoesimerkit\Luentoesimerkit\db.json");
List deserializedProducts = new List();
deserializedProducts = JsonConvert.DeserializeObject>(json);
foreach(Product product in deserializedProducts)
{
Console.WriteLine(product.Name);
}
```
## Exercise 2: Serializing Your Classes
1. Create a class `Note` with the properties `int Id`, `DateTime TimeStamp` and `string Text`.
2. Create a main loop that keeps asking the user for a new note.
3. After the user has entered each note, a new `Note` object is created with a running `Id` (1, 2, 3…), `TimeStamp` set to current time and `Text` as the inputted note. Each new note is added to a list of notes `allNotes`.
4. Every time a new note is submitted, the `allNotes` list is serialized into a json file `notes.json`. Set the path to the current project path (you can get the path by right-clicking the project in solution explorer and selecting _Copy Full Path_. Remember to change the file name!)
## Extra: FileStream
* The files have to be opened before reading or writing and closed after
* Behind the scenes, the `ReadAll...` and `WriteAll...` methods automatically do the opening and closing, as described in the Microsoft documentation:

* Writing to or reading only a part of the file requires opening and closing the file manually
* This creates a *__FileStream__* object that can be used for accessing parts of the file
## FileStream (continued)
* When inserting string data into a file, it needs to be converted into bytes
```csharp
string path = @"C:\Users\Public\TestFolder\TestWriteFile.txt";
int nOfLines = 4;
FileStream fs = null;
if (!File.Exists(path)) // If file does not exist
fs = File.Create(path); // create a new file and open it
else // If file exists
fs = File.OpenWrite(path); // open an existing file for writing
for (int i = 0; i < nOfLines; ++i)
{
string input = Console.ReadLine() + "\\n"; // Read user input, add newline
byte[] byteInput = new UTF8Encoding(true).GetBytes(input); // Get the string as a sequence of bytes
fs.Write(byteInput, 0, byteInput.Length); // Write the bytes at the end of the file
}
fs.Close(); // Close the file
```
## Extra: The `using` statement
* From the Microsoft Documentation about the FileStream class:

* This means that wrapping the file handling in an `using` block handles the opening and closing of the stream for you
---
The following reads the first 20 characters of a file and prints them to the console
```csharp
string path = @"C:\Users\Public\TestFolder\TestFile.txt";
int nOfBytesToPrint = 20;
byte[] byteArray = new byte[nOfBytesToPrint];
if (!File.Exists(path))
Console.WriteLine($"The file {path} does not exist.");
else
using (FileStream fs = File.OpenRead(path))
{
fs.Read(byteArray, 0, nOfBytesToPrint);
};
// No need to close the file since it was handled inside of a using block
Console.WriteLine(Encoding.UTF8.GetString(byteArray, 0, byteArray.Length));
```