--- 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 ![](imgs/12%20Files%20and%20Streams_0.png) ## 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: ![](imgs/12%20Files%20and%20Streams_1.png)
## 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: ![](imgs/12%20Files%20and%20Streams_2.png) * 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: ![](imgs/12%20Files%20and%20Streams_3.png) * 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)); ```