Содержание:
Lesson # 14. Theory
Lecture in pdf format
Files classification
- Component type:
- text files (consists of strings in some encoding)
- binary files
- Access type:
- Arbitrary: access time = Θ(1), components have the same size
(bytes in the simplest case) - Sequential: components have different sizes, for seeking to the
i
-th component it’s necessary to pass the precedingi-1
components (example: text files) - Operations type:
- reading only
- writing only
- reading and writing
Operations with files
With closed files: open check existence delete copy rename move |
With already opened files: close write info read info check the end of file (EOF) seek to the n-th position |
Main .NET classes & namespaces
-
Namespaces:
- using System.IO;
- using System.Text;
- File – static class, operations with closed files
- FileMode – mode of file opening
- FileStream
- StreamReader/StreamWriter – streams for reading-writing to text files
- BinaryReader/BinaryWriter — streams for reading-writing to binary files
- Encoding – file encodings
In .net we have a namespace called System.IO
, that’s where a lot of classes to work with files and directories are located.
Classes:
File
andFileInfo
classes both provide methods for creating, copying, deleting, moving and opening files. They have very similar interfaces, the only difference isFileInfo
provides instance methods, whereasFile
provides static methods.- The difference is if you’re gonna have a small number of operations, it’s more convenient to access the static methods of the
File
class. If you’re going to have many operations, it’s more efficient to create aFileInfo
class and access all its instance methods. Directory
class provides static methods, whereasDirectoryInfo
provides instance methods.- We also have the
Path
class which provides methods to work with a string that contains a file or directory path information.
FileMode
enum typeMain methods of File class
-
Operations with closed files:
using System.IO; // File.Exists("a.dat") Console.WriteLine(File.Exists("a.dat")); // true or false // File.Exists("d:\\a.dat") Console.WriteLine(File.Exists("d:\\a.dat")); // true or false // File.Exists(@"d:\a.dat") Console.WriteLine(File.Exists(@"d:\a.dat"); // true or false File.Delete("a.dat"); File.Copy("d:\\a.dat", "d:\\b.dat"); // b-file will be created, if it exists - the exception will occur File.Copy("a.dat", @"d:\a.dat"); File.Move("a.dat", @"..\a.dat"); |
try .. catch statement
try { // code with possible exception } catch (Exception e) { // to handle of an exception } |
Example:
try { var path = @"d:\a.dat"; var fs = new FileStream(path, FileMode.Open); ... } catch (FileNotFoundException e) { Console.WriteLine($"File with a name {e.FileName} is not found"); } catch (FileLoadException e) { Console.WriteLine($"File with a name {e.FileName} can't be accessed"); } |
Using statement
Example of working with binary files:
using System.IO; // ... string path = @"Lesson14.dat"; // data within the file: 1 2 3 4 var fs = new FileStream(path, FileMode.Open); var br = new BinaryReader(fs); var a=br.ReadInt32(); Console.WriteLine(a); // 1 var b = br.ReadInt32(); Console.WriteLine(b); // 2 var c = br.ReadInt32(); Console.WriteLine(c); // 3 fs.Close(); |
Using
statement provides an automatic call ofClose()
method while working withFileStream
class.
Example:
var path = @"d:\a.dat"; using (var fs = new FileStream(path, FileMode.Create)) { fs.WriteByte(1); fs.WriteByte(2); fs.WriteByte(3); } using (var fs = new FileStream(path, FileMode.Open)) { Console.WriteLine(fs.ReadByte()); Console.WriteLine(fs.ReadByte()); Console.WriteLine(fs.ReadByte()); } |
Labs and Tasks
Stream adapters for binary files
Handling errors while working with files. Using statement. Reading data from a file
To do:
- Create a program to output the contents of the file to the console window (the numbers can be outputted separated by whitespaces within a single line). You should use
BinaryReader
class. - Download the file L01.dat and place it into your work folder (
~/Lesson_14Lab1/bin/Debug
). There are 15 integer numbers written in the file. - You should use the
using
statement while working with an already opened file. Then, regardless of the success of working with an open file, it will always be closed. - Develop a program with exception handling:
- To prevent the program from «crashing» and not scare the user, add exception handling — the
try..catch
block. Theusing
statement must be inside thetry..catch
block.
... try { ... } catch (IOException e) { Console.WriteLine($"Error to read from the file: {e.Message}"); }
- To prevent the program from «crashing» and not scare the user, add exception handling — the
- Change the name of the file within the program code. Run the application and check the output. Change the name again to the previous one.
- It isn’t necessary to create methods here.
using (var fs = new FileStream(path, FileMode.Create)) { ... } |
Expected output:
The data from the file: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +++++++++++++ The data from the file: error to read from file: Файл 'C:\Users\User\Documents\Visual Studio 2015\Projects\L14_Lab1\bin\Debug\L02.dat' не найден
[Solution and Project name: Lesson_14Lab1
, file name L14Lab1.cs
]
✍ How to do:
- Create a new project with the name and file name as it is specified in the task.
- Download the file L01.dat and place it into your work folder (
~/Lesson_14Lab1/bin/Debug
). - To avoid always printing the class
Console
, we can include its declaration right at the beginning of the editor window: - Below this code, include namespace
System.IO
for working with files: - Within the
Main
function, declare a variable for storing the path to the file. It is better to use verbatim string (@
symbol): - To read something from the file, you first need to open the file. To open the file, it is required to specify the path to the file in your system and the mode — how the operating system should open a file.
- So we need to initialize a new instance of the
FileStream
class with the specified path and mode. The enumFileMode
specifies how the operating system should open a file. We need to useOpen
mode, because we don’t need to write anything to the file. The variablefs
will store a stream input: - After that, we need to create a variable (
br
) as an instance of theBinaryReader
class, to have the possibility to read data from the file as binary values: - In order not to have the error if the file does not exist or if the name of the file is misspelled we will use
try...catch
block. Cut out theusing
statements and paste them inside the try block: - Besides, inside
try
block, we’re going to read the bytes or characters from the file and print them out. For such a purpose, thePeekChar
method ofBinaryReader
class is used: - Within the
catch
block, we’re going to print out a message in a case when some error occurs, e.g. in the case when the file name is incorrect: - At the end of the program code, place the
ReadKey
method in order not to close the console window while running the program in a debug mode: - Press F5 to run the program in a debugging mode and check the output.
- To see the result when the error occurs, change the file name, e.g.:
- Press F5 to run the program in a debugging mode and check the output.
- Return the correct name to the file.
- Add comments with the text of the task and save the project. Download file
.cs
to the moodle system.
... using static System.Console; ...
using System.IO; ...
L01.dat";path = @"
using (var fs = new FileStream(path, FileMode.Open))
FileStream
Class provides a Stream (a sequence of bytes) for/from a file, supporting both synchronous and asynchronous read and write operations.... using (var br = new BinaryReader(fs))
... try { using (var fs = new FileStream(path, FileMode.Open)) using (var br = new BinaryReader(fs)) }
// within the try block, after using statements: while (br.PeekChar() != -1) { var x = br.ReadInt32(); Write($"{x} "); }
BinaryReader
class reads primitive data types as binary values in a specific encoding.PeekChar
method returns the next available character, or -1 if no more characters are available or the stream does not support seeking.// under try block: catch (IOException e) { WriteLine($"Error to read from file: {e.Message}"); }
IOException
stores the exception that is thrown when an I/O error occurs.Message
is an attribute of the Exception
class that returns the error message that explains the reason for the exception, or an empty string («»).
// after the closing brace of the catch block:
ReadKey();
ReadKey
method obtains the next character or function key pressed by the user. The pressed key is displayed in the console window.L02.dat";path = @"
To do: Copy the file L01.dat
from the previous Lab and paste it into the directory of this task (~/Lesson_14Task1/bin/Debug
).
1. Create a program to print out the squared numbers from the file to the console window (the numbers can be outputted separated by whitespace within a single line), and also calculate the number of even digits among the numbers. You should use the BinaryReader
class. Make a program using try..catch
block.
Note: It isn’t necessary to create methods here.
Expected output:
Squared numbers: 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 The quantity of even numbers: 7 +++++++++++++++ Error to read from file: Файл 'c:\users\user\documents\visual studio 2015\Projects\L_14Task1\bin\Debug\L02.dat' не найден.
[Solution and Project name: Lesson_14Task1
, file name L14Task1.cs
]
To do:
- Download the file L01.dat. There are integer numbers written in the file. Print these numbers to the console window by
N
numbers in each line (N
is entered (N > 0
) and has a default value =10
). You must useBinaryReader
class. For an empty file, output the «empty file» message. - There can be multiple
try..catch
blocks and each of them can handle different errors. Write your code so that onetry..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:
- Create a method with two parameters to do the task. The signature of the method:
- 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.
... 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 } |
static void PrintFileInRows(string path, int N=10) |
Expected output:
Please input N: 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +++++++++++++ Please input N: 3 Error to read from the file: Файл 'c:\users\user\documents\visual studio 2015\Projects\L_14Lab2\bin\Debug\L02.dat' не найден. +++++++++++++ Please input N: 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Error working with file: Чтение после конца потока невозможно.
[Solution and Project name: Lesson_14Lab2
, file name L14Lab2.cs
]
✍ Algorithm:
- Create a new project with the name and file name as it is specified in the task.
- Download the file L01.dat and place it into your work folder (
~/Lesson_14Lab2/bin/Debug
). - To avoid always printing the class
Console
, we can include its declaration right at the beginning of the editor window: - Below this code, include namespace
System.IO
for working with files: - Within the
Main
function, declare a variable for storing the path to the file. It is better to use verbatim string (@
symbol): - We’re going to output the numbers from the file within some lines:
N
numbers in each line. So, within the Main function, we need to ask the user to enterN
: - All the rest what we’re going to do with the file we have to place inside a method. So we must declare a method with the name
PrintFileInRows
with two parameters — the path to the file and the number of the digits to output within each line (N
): - To read something from the file, you first need to open the file. But according to the requirement of the task, we must have two
try..catch
blocks within our program, one of them handles errors that occur when opening a file, and another block handles errors that occur when working with an already opened file. - So let’s create
try..catch
blocks first, and then we’ll place the rest of the code inside them: - To read something from the file, you first need to open the file. To open the file, it is required to specify the path to the file in your system and the mode — how the operating system should open a file. Place the following code after the open curly brace of the first
try..catch
block: - After that, we’re going to work with the file, it means that we’ll read some data from a file and to do something with it. So we need to create a variable (
br
) as an instance of theBinaryReader
class, to have the possibility to read data from the file as binary values. The following code has to be placed within the second try..catch block, which is inside ofusing
statement: - According to requirements of the task we must output «empty file» message if there is no any data within the file. We’re going to use PeekChar method that returns
-1
if the file is empty: - After that, we’re going to read numbers one by one from the file and output them to the console. But we need to output
N
numbers within each line. So we’ll use the counteri
to count how many numbers we’ve already read. If the number of read numbers is equal toN
, so we’ll go to the next line: - Within the first
catch
block, we’re going to print out a message in a case when some error occurs, while working with the file: - Within the second
catch
block, we’re going to print out a message in a case when some error occurs, while opening the file: - At the end of the program code, place the
ReadKey
method in order not to close the console window while running the program in a debug mode: - Press F5 to run the program in a debugging mode and check the output.
- To see the result when the error occurs, change the file name, e.g.:
- Press F5 to run the program in a debugging mode and check the output.
- Return the correct name to the file.
- To see the result when the error occurs while working with the file, add the next line after the while statement:
- Press F5 to run the program in a debugging mode and check the output.
- Comment the incorrect line of the code.
- Download file
.cs
to the moodle system.
... using static System.Console; ...
using System.IO; ...
L01.dat";path = @"
... WriteLine($"Please input N:"); var n = .Parse(ReadLine());
// after the closing brace of the Main function static PrintFileInRows( path, n = 10) { ... }
void
keyword.// within the curly braces of the PrintFileInRows method try { /* here will be a code, that can have errors that occur when opening the file*/ { try { /* here will be a code, that can have errors that occur while working with the file*/ } catch (IOException e) { // error message when working with the file } } } catch (IOException e) { // error message when opening the file } }
using ( f = File.Open(path, FileMode.Open)) { ... }
// after open curly brace of using statement try { br = new BinaryReader(f);
if (br.PeekChar() == -1) { WriteLine("empty file"); }
// after previous if statement i = 0; while (br.PeekChar() != -1) { if (i >= n) { WriteLine(); i = 0; } Write(br.ReadInt32() + " "); i++; }
// within the first catch block: catch (IOException e) { WriteLine($"Error working with file: {e.Message}"); }
IOException
stores the exception that is thrown when an I/O error occurs.Message
is an attribute of the Exception
class that returns the error message that explains the reason for the exception, or an empty string («»).// within the first catch block: catch (IOException e) { Console.WriteLine($"Error to read from the file: {e.Message}"); }
// after the closing brace of the catch block:
ReadKey();
ReadKey
method obtains the next character or function key pressed by the user. The pressed key is displayed in the console window.L02.dat";path = @"
// after closing curly brace of the while statement: WriteLine(br.ReadInt32());
To do: Download the file Task02.dat. Paste it into the directory of this task (~/Lesson_14Task2/bin/Debug
). There are integer numbers written in the file. Create a program to print these numbers to the console window first, and after that, count the number of numbers, that are smaller than their left neighbor (smaller than the previous number in the sequence of numbers).
Note 1: Create a method to print out the data from the file:
static void PrintData(string path) |
Note 2: Create a method to print out the number of numbers smaller than their left neighbor:
static int CountLessThanLeft(string path) |
Note 3: You may need to use a character encoding while reading from the file:
using (var fs = new FileStream(path, FileMode.Open)) using (var br = new BinaryReader(fs, Encoding.ASCII)) |
Note 4: 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 5: 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.
Expected output:
The numbers within the file: -35 25 28 -37 13 -9 -24 12 -35 37 -24 20 29 -11 -45 -8 43 12 12 44 The number of numbers smaller than their left neighbor: 8 +++++++++++++++ The numbers within the file: -35 25 28 -37 13 -9 -24 12 -35 37 -24 20 29 -11 -45 -8 43 12 12 44 Error working with file: Чтение после конца потока невозможно. The number of numbers smaller than their left neighbor: 8 +++++++++++++++
[Solution and Project name: Lesson_14Task2
, file name L14Task2.cs
]
FileStream Class: working with bytes
FileStream class to work with bytes
To do: Create a file with a name myFile.txt
on your root folder d
or c
(@"D:\myFile.txt"
). Create a program to open the file using FileStream
class and put there the numbers: 1 2 3 4 5 6 7 8 9 10 (you should use loop statement and WriteByte()
method). Then, read the data from the file and output even numbers to the console window (ReadByte()
method should be used).
Expected output:
result: 2 4 6 8 10
[Solution and Project name: Lesson_14Task3
, file name L14Task3.cs
]
FileStream class to work with text
To do: Create a file with a name myFile.txt
on your root folder d
or c
(@"D:\myFile.txt"
). Create a program to open the file using FileStream
class and put there some entered string (don’t forget to convert it into a byte array -> Encoding.Default.GetBytes()
). Then, read the data from the file and output it to the console window (by converting data again into string -> Encoding.Default.GetString(...)
).
Expected output:
Enter some string: Hello, world! String from the file: Hello, world!
[Solution and Project name: Lesson_14Task4
, file name L14Task4.cs
]