Lesson # 15. Working with Files. Writing to file

Programming on c sharp in Microsoft Visual Studio. Working with Files. Writing to file

Labs and Tasks

Lab 1. Writing data to file
  
To do: Create a file L15Lab1.dat. Write 10 random generated numbers in the range from -10 to 15 to the file. Print these numbers to the console window first, and after that, count the number of negative numbers. Output the result. Use the BinaryWriter class.

Note 1: Create a method to create a file and fill it with the random numbers. The signature of the method must be as follows:

static void CreateRandomIntFile(string @s, int n = 10, int a = -10, int b = 15)
// @s is a variable storing the file path
// n is the number of numbers to write to the file
// a and b are the boundaries for random function

Use Debug.Assert method to check to see if n >= 0 and a <= b.

Note 2: Create a method to print out the data from the file:

static void PrintData(string path)

Note 3: Create a method to print out the number of negative elements:

static int CountNeg(string path)

Note 4: To write data to the file the BinaryWriter class must be used:

using (var fs = File.Create(s))
using (var br = new BinaryWriter(fs))

Note 5: There can be multiple try..catch blocks and each of them can handle different errors. Write your code so that one try..catch block handles errors that occur when opening a file, and another block handles errors that occur when working with an already opened file.

Then the program structure should be as follows:

...
try 
{
    using ( ... )
    using ( ... )
    { 
        try 
        {
            // work with opened file...
        }
        catch 
        {     
            // handling an error of working with already opened file
        }
   }
catch 
{
    // handling an error of opening file
}

Note 6: Check to see if the try..catch blocks work properly. Modulate an error while working with already opened file, and error with a name of the file. After checking, return the previous (right) code, and make the comments of the wrong code.

The resulting example:

Tests are done well: n >= 0 and a <= b
Random integers from the file:
1 9 -2 2 8 10 -7 -8 13 3
Number of negative numbers: 3
+++++++++++++++
Tests are done well: n >= 0 and a <= b
Random integers from the file:
10 5 -1 6 8 12 -9 -6 -8 -1 Error working with file: Чтение после конца потока невозможно.

Number of negative numbers: 5
+++++++++++++++

[Solution and Project name: Lesson_15Lab1, file name L15Lab1.cs]

✍ How to do:

  1. Create a new project with the name and file name as it is specified in the task.
  2. First, we're going to create a file and fill it with 10 random generated integers. To make it, we must use our own created method called CreateRandomIntFile. The method will have three parameters as we have it in Note 1 of our lab problem. Place the following code of the signature of the method after closing curly brace of the Main function:
  3. static void CreateRandomIntFile(string @s, int n = 10, int a = -10, int b = 15)
     {
       ...
     }
    
    @s is a verbatim string parameter that stores the path to the file we're going to create. The three parameters of the method are already initialized by the values: n = 10 means that we are going to write to the file 10 numbers; a =- 10 is the lower bound for the random generated numbers; b = 15 is the upper bound for the random generated numbers.
  4. Let's create the code for our method. First, we need to check to see if the provided values for the parameters are valid. So the n must be greater than zero, and the condition for the boundaries a < b must be equaled to true. In order not to type always Diagnostics namespace, we will include it. Type the code within the including section on the top of the screen:
  5. using System.Diagnostics;
    
  6. Make the same for IO namespace which is used to work with files, and for the Console class:
  7. using static System.Console;
    using System.IO;
    
  8. Return to the our method's code and add into the method the lines to debug the mentioned conditions:
  9. Debug.Assert(n >= 0);
    Debug.Assert(a <= b);
    Debug.Assert(s != " ");
    
  10. After, if the debugging doesn't throw the exception, the message has to be printed out:
  11. WriteLine("Tests are done well: n >= 0 and a <= b");
    
  12. All the rest code to create a file and to work with it we need to place into multiple try..catch block. Within the first try block, we're going to place two using statements: one of them is for using the File class, and another one is for using BinaryWriter class:
  13. try
        {
            using (var fs = File.Create(s))
            using (var br = new BinaryWriter(fs))
               {
                  ...
               }
        ...    
    
    File class provides static methods for the creation, copying, deletion, moving, and opening of a single file. It has the Create(string path) method that creates or overwrites a file in the specified path. fs variable will be associated with the created file.
    BinaryWriter class writes primitive types in binary to a stream. It has the method Write(int) that writes an integer to the current stream. br variable will be used as an instance of an object of the BinaryWriter class.
  14. The Catch block of this try..catch statement has to throw an exception when the file can't be created by any reasons:
  15. ...
            } // closing brace for outer try block
                catch (IOException e)
                {
                    Console.WriteLine($"Error opening the file: {e.Message}");
                }
    } // closing brace for CreateRandomIntFile method
    
  16. To generate the numbers we need to create an instance of an object of the Random class. Add the code after open curly brace:
  17. var rand = new Random();
    
  18. We have to place a code for writing the numbers to the file into the second, inner, try block:
  19. try
         {
             for (int i = 0; i < n; ++i)
                 br.Write(rand.Next(a, b));
         }
    
    Next method generates a random integer that is within a specified range (from a to b). Write method writes an integer to the current stream, as it was already mentioned.
  20. The Catch block of the inner try..catch statement has to throw an exception if something with writing to the file goes wrong:
  21.         } // closing brace for inner try block
                catch (IOException e)
                  {
                     Console.WriteLine($"Error writing to file: {e.Message}");
                  }
            ...
    
  22. Our CreateRandomIntFile method is ready. Now we need to create a PrintData method. PrintData method has one parameter, it is the string s that stores the verbatim path to the file. Let's make the code similar to that we had in the previous lab 2 of lesson #14:
  23.  static void PrintData(string s)
            {
                try
                {
                    using (var fs = File.Open(s, FileMode.Open))
                    using (var br = new BinaryReader(fs, Encoding.ASCII))
                    {
                        try
                        {
                            while (br.PeekChar() != -1)
                            {
                                var t = br.ReadInt32();
                                Write($"{t} ");
                            }
                            //WriteLine(br.ReadInt32());
                        }
                        catch (IOException e)
                        {
                            Console.WriteLine($"Error working with file: {e.Message}");
                        }
                    }
                }
                catch (IOException e)
                {
                    Console.WriteLine($"Error to read from the file: {e.Message}");
                }
            }
    
    br variable is an instance of an object of the BinaryReader class, which is used to have the possibility to read data from the file as binary values. The class has ReadInt32() method that reads an integer from the current stream.
  24. The PrintData method is ready. Now we're going to return to the Main function and do all of the calls of the methods:
  25. CreateRandomIntFile(@"L15Lab1.dat");
    WriteLine("Random integers from the file: ");
    PrintData(@"L15Lab1.dat");
    WriteLine();
    
  26. At the end of the program code, within the Main function, place the ReadKey method in order not to close the console window while running the program in a debug mode:
  27. ReadKey();
    
  28. Press F5 to run the program in a debugging mode and check the output.
  29. To count the number of negative nubers we're going to create a CountNeg(string s) method that takes string s parameter as a path to the file. Within this method we'll use a multiple try..catch blocks again:
  30. static int CountNeg(string s)
            {
                int count = 0;
                try
                {
                    using (var fs = File.Open(s, FileMode.Open))
                    using (var br = new BinaryReader(fs, Encoding.ASCII))
                    {
                        try
                        {
                            ...
                        }
                        catch (IOException e)
                        {
                            Console.WriteLine($"Error working with file: {e.Message}");
                        }
                    }
                }
                catch (IOException e)
                {
                    Console.WriteLine($"Error opening the file: {e.Message}");
                }
                return count;
            }
    
  31. Instead of ... we need to place the code, which has to iterate through all the numbers within the file and to count the number of negatives among them. We're going to use the while loop and PeekChar method that returns -1 if the file is empty:
  32. ...
                 int t;
                 if (br.PeekChar() != -1)
                     while (br.PeekChar() != -1)
                         {
                             t = br.ReadInt32();
                             if (t < 0)
                                 count++;
                          }
                 //WriteLine(br.ReadInt32());
    
  33. The CountNeg method is ready. Now we're going to call it within the Main method. Place the following code after the previous lines of the code within the Main method:
  34. WriteLine("Number of negative numbers: " + CountNeg(@"L15Lab1.dat"));
    
  35. Press F5 to run the program in a debugging mode and check the output.

Task 1:

To do: Create a file L15Task1.dat. Write 10 random generated numbers in the range from -5 to 15 to the file. Print these numbers to the console window. Print true if there is an integer equaled K among the numbers in the file. Print false otherwise. K is entered number.

Note 1: There has to be created three methods: 1) to create the file and fill it with random numbers; 2) to print data from the file; 3) to find out if there is a desired number among those within the file. The signature of the last method:

static bool IfK(string path, int k)

     
Note 2: When creating an instance of BinaryReader class it is better to use encoding:

using (var br = new BinaryReader(fs,ASCIIEncoding.ASCII))

The resulting example:

Tests are done well: n >= 0 and a <= b
Random integers from the file:
10 13 -4 1 6 7 -3 -2 8 2
Please, enter the number to find:
5
the desired number was found: False
+++++++++++++++++++++++++++++
Tests are done well: n >= 0 and a <= b
Random integers from the file:
2 -1 4 -1 -5 -1 12 6 1 9
Please, enter the number to find:
4
the desired number was found: True

[Solution and Project name: Lesson_15Task1, file name L15Task1.cs]

Lab 2. Working with two files simultaneously
  
To do: Write a method to create a file named L15Lab2.dat and fill it with an array of integers. After, create a new file (named L15Lab2Plus.dat) and write there all the numbers from the L15Lab2.dat file that go up to the first negative element (not including the negative element). Print out the elements from both files. In this program, you are allowed not to use multiple try..catch blocks.

Note 1: The signature of the method to create and fill the file must be as follows:

static void CreateFile(string path, params int[] a)

Note 2: Create one more method to add element to a new file. The method must have two parameters: the first parameter is the path of the first file, from which we're going to read the elements, and the second parameter is the path file, into which we're going to write the elements. The signature of the method to create and fill the file must be as follows:

static void CreatePlusFile(string oldPath, string newPath)

The resulting example:

The first file:
1 2 3 4 5 -1 -2 -3 4 5 6 0 1 -4 1
The second file:
1 2 3 4 5
+++++++++++++++
The first file:
1 2 -3 -4 5 -6 7 -8 9 -10 6 0 1 -4 1
The second file:
1 2
+++++++++++++++

[Solution and Project name: Lesson_15Lab1, file name L15Lab1.cs]

✍ How to do:

  1. Create a new project with the name and file name as it is specified in the task.
  2. First, we're going to include all the namespaces and classes we'll need for our project. In order not to type always Diagnostics namespace, which is used for debugging process, and IO namespace, which is used to work with files, we'll include them into our project. We'll do the same with the Console class. Type the code within the including section on the top of the screen:
  3. using System.Diagnostics;
    using static System.Console;
    using System.IO;
    
  4. After, we need to create a file and fill it with an single-dimentioanal array. We're going to use our method CreateFile for this purpose and we have the signature of it in our lab's task. Place the following code of the signature of the method after closing curly brace of the Main function:
  5. static void CreateFile(string path, params int[] a) {
       ...
     }
    
    We don't know how many elements there will be inside the array, that is why we use params int[] a as one of the parameters of our method (params keyword gives a possibility to pass a varying number of parameters).
  6. We'll use using statments to open the file to write something to it (FileMode.OpenOrCreate) and to write data in binary to a stream (BinaryWriter class):
  7. using (var fs = File.Open(path, FileMode.OpenOrCreate))
    using (var bw = new BinaryWriter(fs))
        {
            ...
        } // end of using statements
    
  8. Inside using statements block we need to write the elements of the a[] array to the file. The elements are provided by the parameter of the method. Type the following code insted of ...:
  9.  for (int i = 0; i < a.Length; i++)
         {
              bw.Write(a[i]);
         }
    
  10. To print out the data from a file to the console window we'll create a PrintData method, similar to what we had in the previous lab. But in this program we don't need to use the multiple try..catch block. So the code will be much shorter:
  11. static void PrintData(string path)
            {
                using (var fs = File.Open(path, FileMode.Open))
                using (var br = new BinaryReader(fs, Encoding.ASCII))
                {
                    while (br.PeekChar() != -1)
                    {
                        Console.Write($"{br.ReadInt32()} ");
                    }
                    Console.WriteLine();
                }
            } // the end of the PrintData method
    
  12. Let's call our methods from within our Main function. But first, let's create two variables to store the paths to our files. Place the code into the Main function:
  13. string path1 = @"L15Lab2.dat";
    string path2 = @"L15Lab2Plus.dat";
    
  14. Call the method CreateFile with the elements for the array as one of the parameters. And after, call the PrintData method to see the file's content within our console:
  15. CreateFile(path1, 1, -2, 3, -4, 5, -6, 7, -8, 9, -10);
    Console.WriteLine("The first file:");
    PrintData(path1);
    
  16. Add the ReadKey() method and run the program by pressing F5 button.
  17.  ReadKey();
    
  18. Now we're going to create one more method to place there the positive numbers one by one from L15Lab2.dat file till we meet the negative number. The method will have two parameters, they are the paths to the files. The oldPath parameter is a path to the file we're going to read data from, and newPath - is a path to the file we're going to write data to:
  19. static void CreatePlusFile(string oldPath, string newPath)
    {
    ...
    } // the end of the CreatePlusFile method
    
  20. Instead of ... signs place the code to delete the L15Lab2Plus.dat file in order if it exists. We're going to have an empty file before working with it:
  21. File.Delete(newPath);
    
  22. After, we're going to use four using statements: two of them are for reading data from the L15Lab2.dat files, and two - for writing data to the L15Lab2Plus.dat file:
  23.   using (var oldfs = File.Open(oldPath, FileMode.OpenOrCreate))
      using (var oldbr = new BinaryReader(oldfs, Encoding.ASCII))
      using (var newfs = File.Open(newPath, FileMode.OpenOrCreate))
      using (var newbw = new BinaryWriter(newfs,Encoding.ASCII))
       {
          ...
       } // end of using statements
    
  24. We need to read only positive numbers one by one. We'll use the while loop. When we meet the first negative number we'll exit the loop. To do it use the break statement:
  25.   while (oldbr.PeekChar() != -1)
              {
                  int i = oldbr.ReadInt32();
                     if (i < 0)
                      {
                          break;
                      }
                      newbw.Write(i);
              }
    
  26. We have our method ready. Let's call it within the Main function and print out the file's content to the console window:
  27. CreatePlusFile(path1, path2);
    Console.WriteLine("The second file:");
    PrintData(path2);
    
  28. Run the program and check the output.
Task 2:

To do: Create a file L15Task2.dat to print out first N Fibonacci numbers (0, 1, 2, 3, 5, 8 ... N). N is entered number. In this program, you are allowed not to use try..catch blocks.

The resulting example:

Please, enter n:
14
Result is:
1 1 2 3 5 8 13 21 34 55 89 144 233 377
+++++++++++++++++++++++++++++
Please, enter n:
5
Result is:
1 1 2 3 5

[Solution and Project name: Lesson_15Task2, file name L15Task2.cs]