The operation of file I/O in C++ is almost the same as that of regular I/O (but with a few nuances).

File I/O Classes

There is three main file I/O classes in C++:

outstream(is a child of the class );

fstream(is a child of the iostream class).

With these classes, you can perform unidirectional file input, unidirectional file output, and bidirectional file I/O. To use them, you just need to connect fstream.

Unlike the cout, cin, cerr, and clog streams, which are immediately usable, file streams must be explicitly set by the programmer. That is, to open a file for reading and/or writing, you need to create an object of the appropriate file I/O class, specifying the file name as a parameter. Then, using the insertion operators (<<) или извлечения (>>), you can write data to a file or read the contents of a file. After that, the final - you need to close the file: explicitly call close() method or just let the file I/O variable go out of scope (the file I/O class will close that file automatically for us).

File output

The ofstream class is used to write to a file. For example:

#include #include #include // to use exit() int main() ( using namespace std; // ofstream is used to write data to a file // Create a file SomeText.txt ofstream outf("SomeText.txt"); // If we can't open this file to write data to if (!outf) ( // Then print an error message and execute exit() cerr<< "Uh oh, SomeText.txt could not be opened for writing!" << endl; exit(1); } // Записываем в файл следующие две строчки outf << "See line #1!" << endl; outf << "See line #2!" << endl; return 0; // Когда outf выйдет из области видимости, то деструктор класса ofstream автоматически закроет наш файл }

#include

#include

#include // to use exit()

int main()

using namespace std ;

// ofstream is used to write data to file

// Create file SomeText.txt

ofstream outf("SomeText.txt" ) ;

// If we can't open this file to write data to it

if(!outf)

// Then print an error message and execute exit()

cerr<< << endl ;

exit(1) ;

// Write the following two lines to the file

outf<< "See line #1!" << endl ;

outf<< "See line #2!" << endl ;

return 0 ;

// When outf goes out of scope, the destructor of the ofstream class will automatically close our file

If you look in your project directory ( RMB on the tab with the name of your .cpp file in Visual Studio > "Open containing folder"), you will see a file called SomeText.txt, which contains the following lines:

See line #1!
See line #2!

Please note we can also use put() method to write one character to a file.

File input

#include #include #include #include // to use exit() int main() ( using namespace std; // ifstream is used to read the contents of the file // Try to read the contents of the file SomeText.txt ifstream inf("SomeText.txt"); // If we can't open this file to read its contents if (!inf) ( cerr<< "Uh oh, SomeText.txt could not be opened for reading!" << endl; exit(1); } // Пока есть данные, которые мы можем прочитать while (inf) { // То перемещаем эти данные в строку, которую затем выводим на экран string strInput; inf >> strInput; cout<< strInput << endl; } return 0; }

#include

#include

#include

#include // to use exit()

int main()

using namespace std ;

// ifstream is used to read the contents of the file

// If we can't open this file to read its contents

if(!inf)

// Then print the following error message and execute exit()

cerr<< << endl ;

exit(1) ;

// As long as there is data we can read

while(inf)

// Then we move this data to a string, which we then display on the screen

string strInput ;

inf >> strInput ;

cout<< strInput << endl ;

return 0 ;

// When inf goes out of scope, the destructor of the ifstream class will automatically close our file

See
line
#1!
See
line
#2!

Hmm, that's not quite what we wanted. As we already know from the previous lessons, the extraction operator works with "formatted data", i.e. it ignores all spaces, tabs and newlines. To read all the content as is, without splitting it into parts (as in the example above), we need to use getline() method:

#include #include #include #include // to use exit() int main() ( using namespace std; // ifstream is used to read the contents of files // We will try to read the contents of file SomeText.txt ifstream inf("SomeText.txt"); // If we can't open a file to read its contents if (!inf) ( // Then print the following error message and exit() cerr<< "Uh oh, SomeText.txt could not be opened for reading!" << endl; exit(1); } // Пока есть, что читать while (inf) { // То перемещаем то, что можем прочитать, в строку, а затем выводим эту строку на экран string strInput; getline(inf, strInput); cout << strInput << endl; } return 0; // Когда inf выйдет из области видимости, то деструктор класса ifstream автоматически закроет наш файл }

#include

#include

#include

#include // to use exit()

int main()

using namespace std ;

// ifstream is used to read the contents of files

ifstream inf("SomeText.txt" ) ;

// If we can't open the file to read its contents

if(!inf)

// Then print the following error message and execute exit()

cerr<< "Uh oh, SomeText.txt could not be opened for reading!"<< endl ;

exit(1) ;

while(inf)

string strInput ;

getline (inf , strInput ) ;

cout<< strInput << endl ;

return 0 ;

// When inf goes out of scope, the destructor of the ifstream class will automatically close our file

The result of running the program above is:

buffered output

Output in C++ can be buffered. This means that everything that is output to the file stream cannot immediately be written to disk (to a specific file). This is done primarily for performance reasons. When buffer data is written to disk, this is called clearing the buffer. One way to clear the buffer is to close the file. In this case, the entire contents of the buffer will be moved to disk, and then the file will be closed.

Output buffering is not usually a problem, but under certain circumstances it can cause problems for unwary newbies. For example, when data is stored in the buffer and the program terminates its execution prematurely (either as a result of a crash or by calling ). In such cases, file I/O class destructors are not executed, files are never closed, buffers are not flushed, and our data is lost forever. That's why it's a good idea to explicitly close all open files before calling exit().

You can also clear the buffer manually using ostream::flush() method or by sending std::flush to the output stream. Either of these methods can be useful to ensure that the contents of the buffer are immediately written to disk in the event of a program crash.

An interesting nuance: Because std::endl; also clears the output stream, then overusing it (resulting in unnecessary buffer flushes) can affect program performance (because buffer flushing can be expensive in some cases). For this reason, performance-conscious programmers often use \n instead of std::endl to insert a newline character into the output stream to avoid unnecessary buffer flushing.

File opening modes

What happens if we try to write data to an already existing file? Re-running the program above (the very first one) shows that the original file is completely overwritten when you run the program again. But what if we need to add data to the end of the file? It turns out that the file stream takes an optional second parameter that allows you to tell the programmer how to open the file. As this parameter, you can pass following flags(which are in the ios class):

app- opens the file in append mode;

ate- goes to the end of the file before reading/writing;

binary- opens the file in binary mode (instead of text mode);

in- opens file in read mode (default for ifstream);

out- opens file in write mode (default for ofstream);

trunc- deletes the file if it already exists.

You can specify multiple flags at once by using .

ifstream works in ios::in mode by default;

ofstream works in ios::out mode by default;

fstream runs in ios::in OR ios::out mode by default, which means you can either read the contents of a file or write data to a file.

Now let's write a program that will add two lines to the previously created SomeText.txt file:

#include #include // to use exit() #include int main() ( using namespace std; // Passing the ios:app flag to tell fstream we're going to append our data to the file's already existing data, // we're not going to overwrite the file. We don't need to pass the ios::out flag , // since ofstream defaults to ios::out ofstream outf("SomeText.txt", ios::app); // If we can't open a file to write data if (!outf) ( // Then output following error message and exit() cerr<< "Uh oh, SomeText.txt could not be opened for writing!" << endl; exit(1); } outf << "See line #3!" << endl; outf << "See line #4!" << endl; return 0; // Когда outf выйдет из области видимости, то деструктор класса ofstream автоматически закроет наш файл }

#include

#include // to use exit()

#include

int main()

using namespace std ;

// Passing the ios:app flag to tell fstream that we're going to add our data to the file's already existing data,

// we're not going to overwrite the file. We don't need to pass the ios::out flag,

// because ofstream defaults to ios::out mode

ofstream outf("SomeText.txt" , ios::app ) ;

// If we can't open the file to write data

if(!outf)

// Then print the following error message and execute exit()

cerr<< "Uh oh, SomeText.txt could not be opened for writing!"<< endl ;

exit(1) ;

The I / O mechanism developed by , does not correspond to the generally accepted object-oriented programming style today, in addition, it actively uses pointer operations, which are considered potentially unsafe in modern secure code execution environments. An alternative for application development is the standard I/O class mechanism provided by the C++ language standard.

Opening files

The most commonly used classes are ifstream for reading, ofstream for writing, and fstream for modifying files.

All threaded I/O classes are indirectly derived from the common ancestor ios , fully inheriting its functionality. For example, the open_mode enumerated data member specifies the file open mode, which is defined as follows:

Enum open_mode ( app, binary, in, out, trunc, ate );

Below are the possible values ​​of the flags and their purpose.

For example, to open a file called test.txt to read binary data, you would write:

ifstream file; file.open("test.txt", ios::in | ios::binary);

The logical OR operator (|) allows you to compose a mode with any combination of flags. So that when opening a file by writing, you do not accidentally overwrite an existing file with the same name, you must use the following form:

offstream file; file.open("test.txt", ios::out | ios::app);

It is assumed that the appropriate header file is connected to the project:

#include

To check if the file was successfully opened, you can use the construction

If (!file) ( // Handling a file open error )

Inclusion and Extraction Operators

Overridden in file handling classes include operator (<<) записывает данные в файловый поток. Как только вы открыли файл для записи, можно записывать в него текстовую строку целиком:

file<< "Это строка текста";

You can also write a text string in parts:

file<< "Это " << "строка " << "текста";

The endl statement ends the line input with a carriage return:

file<< "Это строка текста" << endl;

Using the include operator, it is easy to write the values ​​of variables or array elements to a file:

Ofstream file("Temp.txt"); char buff = "Text array contains variables"; int vx = 100; float pi = 3.14159; file<< buff << endl << vx << endl << pi << endl;

As a result of the code execution, three lines of the Temp.txt text file are generated:

Text array contains variables 100 3.14159

Note that the numeric values ​​are written to the file as text strings, not binary values.

extract operator(>>) does the opposite. It would seem that to extract the characters from the Temp.txt file written earlier, you need to write code like the following:

ifstream file("Temp.txt"); char buff; intvx; floatpi; file >> buff >> vx >> pi;

However, the extraction operator will stop at the first delimiter (space, tab, or newline) that it encounters. Thus, when parsing the sentence "Text array contains variables", only the word "Text" will be written to the array buff , the space is ignored, and the word "array" will become the value of the integer variable vx and the code execution will "go haywire" with an inevitable violation of the data structure. Next, when discussing the ifstream class, we will show how to correctly organize the reading of the file from the previous example.

ifstream class: reading files

As the name implies, the ifstream class is designed to input a file stream. The main methods of the class are listed below. Most of them are inherited from the istream class and overloaded with parent functionality. For example, the get function, depending on the call parameter, is able to read not only a single character, but also a character block.

Now it is clear how you need to modify the previous example so that using the data extraction operator gives the expected result:

ifstream file("Temp.txt"); char buff; intvx; floatpi; file.getline(buff, sizeof(buff)); file >> vx >> pi:

The getline method will read the first line of the file to the end, and the >> operator will assign values ​​to variables.

The following example shows adding data to a text file and then reading the entire file. The while (1) loop is used instead of while(!file2.eof()) for reasons discussed in .

#include #include using namespace std; int main() ( ofstream file; file.open("test.txt",ios::out|ios::app); if (!file) ( cout<< "File error - can"t open to write data!"; cin.sync(); cin.get(); return 1; } for (int i=0; i<10; i++) file << i << endl; file.close(); ifstream file2; file2.open("test.txt", ios::in); if (!file2) { cout << "File error - can"t open to read data!"; cin.sync(); cin.get(); return 2; } int a,k=0; while (1) { file2 >>a; if (file2.eof()) break; cout<< a << " "; k++; } cout << endl << "K=" << k << endl; file2.close(); cin.sync(); cin.get(); return 0; }

The following example loops through reading lines from the file test.txt and displaying them on the console.

#include #include using namespace std; int main() ( ifstream file; // create a stream object file file.open("test.txt"); // open file for reading if (!file) return 1; // return on error opening char str; // static line buffer // Read and display lines in a loop until eof while (!file.getline(str, sizeof(str)).eof()) cout<< str << endl; // вывод прочитанной строки на экран cin.sync(); cin.get(); return 0; }

This code under Windows OS also depends on the presence of a newline character in the last line of the file, it would be more reliable to do this:

While (1) ( if (file.eof()) break; file.getline(str, sizeof(str)); cout<< str << endl; }

Explicit calls to the open and close methods are optional. Indeed, calling the constructor with an argument allows you to open the file immediately, at the moment the file stream object is created:

ifstream file("test.txt");

Instead of the close method, you can use the delete operator, which will automatically call the file object's destructor and close the file. The while loop code provides proper end-of-file checking.

ofstream class: writing files

The ofstream class is designed to output data from a file stream. The main methods of this class are listed below.

The include operator described earlier is convenient for organizing writing to a text file:

Ofstream file("temp.txt"); if (!file) return; for (int i=1; i<=3; i++) file << "Строка " << i << endl; file.close();

Binaries

In principle, binary data is served like text data. The difference is that if binary data is written in a certain logical structure, then they must be read from the file into a variable of the same structure type.

The first parameter of the write and read methods (the address of the write/read block) must be of the type of a character pointer char * , so it is necessary to explicitly convert the type of the address of the void * structure. The second parameter specifies that the file's binary blocks have a constant byte size regardless of the actual record length. The following appendix gives an example of how to create and display data in a simple notebook. The records of the file are then read sequentially and displayed on the console.

#include #include #include using namespace std; struct Notes ( // data structure of the notebook char Name; // full name char Phone; // phone int Age; // age ); int main() ( setlocale(LC_ALL, "Russian"); Notes Note1= ("Grozny Ioann Vasilyevich", "not installed", 60 ); Notes Note2= ("Godunov Boris Fedorovich", "095-111-2233 ", 30 ); Notes Note3= ( "Peter Romanov ", "812-333-2211 ", 20 ); ofstream ofile("Notebook.dat", ios::binary); ofile.write((char*)&Note1, sizeof (Notes)); // 1st block ofile.write((char*)&Note2, sizeof(Notes)); // 2nd block ofile.write((char*)&Note3, sizeof(Notes)); / / 3rd block ofile.close(); // close the written file ifstream ifile("Notebook.dat", ios::binary); Notes Note; // structured variable char str; // static string buffer // Read and display lines in a loop until eof while (!ifile.read((char*)&Note, sizeof(Notes)).eof()) ( sprintf(str, "%s\tBody: %s\tAge: %d" , Note.Name, Note.Phone, Note.Age); cout<< str << endl; } ifile.close(); // закрыть прочитанный файл cin.sync(); cin.get(); return 0; }

As a result of executing this code, a binary file Notebook.dat is formed from three blocks of 80 bytes each (assuming that the characters are single-byte). Naturally, you can use other streaming methods and perform any operation on the fields of a particular data structure.

fstream class: random file access

Suppose that our notebook has accumulated 100 entries, and we want to count the 50th. Of course, you can organize a loop and read all the records from the first to the given one. Obviously a more targeted solution is to set the pos file position pointer directly to entry 50 and read it:

ifstream ifile("Notebook.dat", ios::binary); int pos = 49 * sizeof(Notes); ifile seek(pos); // search for the 50th entry Notes Note; //Notes - the "record" structure described above ifile.read((char*)&Note, sizeof(Notes));

Such search operations are effective if the file consists of records of a known and constant size. To replace the contents of an arbitrary entry, you need to open the output stream in modify mode:

Ofstream ofile("Notebook.dat", ios::binary | ios::ate); int pos = 49 * sizeof(Notes); ofile seekp(pos); // search for the 50th note Notes Note50 = ("Yeltsin Boris Nikolaevich", "095-222-3322", 64); ofile.write((char*)&Note, sizeof(Notes)); // replacement

If you do not specify the ios::ate (or ios::app) flag, then when you open the Notebook.dat binary file, its previous contents will be erased!

Finally, it is possible to open a file concurrently for reading/writing, using methods inherited by the fstream streaming class from its predecessors. Since the fstream class is derived from istream and ostream (parents of ifstream and ofstream respectively), all of the previously mentioned methods become available to the application.

The following example swaps the first and third entries in the Notebook.dat file.

#include #include #include using namespace std; struct Notes ( char Name; char Phone; int Age; ); int main() ( setlocale(LC_ALL, "Russian"); Notes Note1, Note3; // Open file for reading/writing simultaneously fstream file("Notebook.dat", ios::binary | ios::in | ios:: out); file.seekg(2 * sizeof(Notes)); // find and read Note3 file.read((char*)&Note3, sizeof(Notes)); file.seekg(0); // find and read Note1 file.read((char*)&Note1, sizeof(Notes)); file.seekg(0); // Note1<== Note3 file.write((char*)&Note3, sizeof(Notes)); file.seekg(2 * sizeof(Notes)); // Note3 <== Note1 file.write((char*)&Note1, sizeof(Notes)); char str; // Считывать и отображать записи в цикле, пока не eof file.seekg(0); // вернуться к началу файла while (!file.read((char*)&Note1, sizeof(Notes)).eof()) { sprintf(str, "%s\tТел: %s\tВозраст: %d", Note1.Name, Note1.Phone, Note1.Age); cout << str << endl; } file.close(); cin.sync(); cin.get(); return 0; }

The ios::in and ios::out flags must be specified in the file object's constructor to allow concurrent read and write operations. As a result of executing this code, the first and third records of the Notebook.dat binary file will be swapped.

There are additional examples on the topic.

Most computer programs work with files, and therefore there is a need to create, delete, write, read, open files. What is a file? A file is a named collection of bytes that can be stored on some storage device. Well, now it's clear that a file is some sequence of bytes that has its own unique name, such as a .txt file. Files with the same name cannot be in the same directory. The file name is understood not only as its name, but also as an extension, for example: file.txt and file.dat different files, although they have the same name. There is such a thing as the full name of files - this is the full address to the file directory with the file name, for example: D:\docs\file.txt . It is important to understand these basic concepts, otherwise it will be difficult to work with files.

To work with files, you need to include a header file . AT several classes defined and header files included file input and file output.

File I/O is similar to standard I/O, the only difference is that I/O is not done to the screen, but to a file. If input/output to standard devices is performed using the cin and cout objects, then to organize file I/O, it is enough to create your own objects that can be used similarly to the cin and cout operators.

For example, you need to create a text file and write the line Working with files in C++ into it. To do this, you need to do the following steps:

  1. create an object of class ofstream ;
  2. associate a class object with the file to be written to;
  3. write a line to a file;
  4. close the file.

Why is it necessary to create an object of the ofstream class and not the ifstream class? Because you need to write to a file, and if you needed to read data from a file, then an object of the ifstream class would be created.

// create an object to write to the file ofstream /*object name*/; // object of class ofstream

Let's call the object - fout , Here's what happens:

Ofstream fout;

Why do we need an object? The object is required to be able to write to the file. The object has already been created, but is not associated with the file to which the string is to be written.

fout.open("cppstudio.txt"); // associate object with file

Through the dot operation, we get access to the open() class method, in parentheses of which we indicate the file name. The specified file will be created in the current directory with the program. If a file with the same name exists, the existing file will be replaced by the new one. So, the file is open, it remains to write the desired line into it. It is done like this:

Fout<< "Работа с файлами в С++"; // запись строки в файл

Using the cast to stream operation in conjunction with the fout object, the string File Handling in C++ is written to a file. Since it is no longer necessary to change the contents of the file, it must be closed, that is, the object should be separated from the file.

fout.close(); // close the file

As a result, a file with the line Working with files in C++ was created.

Steps 1 and 2 can be combined, that is, in one line, create an object and associate it with a file. It is done like this:

Ofstream fout("cppstudio.txt"); // create an object of the ofstream class and associate it with the cppstudio.txt file

Let's combine all the code and get the following program.

// file.cpp: defines the entry point for the console application. #include "stdafx.h" #include using namespace std; int main(int argc, char* argv) ( ofstream fout("cppstudio.txt"); // create an object of the ofstream class for writing and associate it with the file cppstudio.txt fout<< "Работа с файлами в С++"; // запись строки в файл fout.close(); // закрываем файл system("pause"); return 0; }

It remains to check the correct operation of the program, and for this we open the file cppstudio.txt and look at its contents, it should be - Working with files in C++.

  1. create an object of the ifstream class and associate it with the file to be read from;
  2. read file;
  3. close the file.
// file_read.cpp: defines the entry point for the console application. #include "stdafx.h" #include #include using namespace std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // correct display of the Cyrillic alphabet char buff; // intermediate storage buffer of the text read from the file ifstream fin("cppstudio.txt"); // opened file to read fin >><< buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку system("pause"); return 0; }

The program shows two ways to read from a file, the first is using the transfer to stream operation, the second is using the function getline() . In the first case, only the first word is read, and in the second case, a string of 50 characters is read. But since there are less than 50 characters left in the file, the characters are read up to and including the last one. Note that reading a second time (line 17) continued after the first word, and not from the beginning, since the first word was read inline 14. The result of the program is shown in Figure 1.

Working with Files in C++ Press any key to continue. . .

Figure 1 - Working with files in C++

The program worked correctly, but this is not always the case, even if everything is in order with the code. For example, the name of a non-existent file was passed to the program, or an error was made in the name. What then? In this case, nothing will happen at all. The file will not be found, which means that it is not possible to read it. Therefore, the compiler will ignore the lines where the file is being manipulated. As a result, the program will exit correctly, but nothing will be shown on the screen. It would seem that this is a completely normal reaction to such a situation. But a simple user will not understand what the matter is and why a line from the file did not appear on the screen. So, to make everything very clear, C++ provides such a function - is_open() , which returns integer values: 1 - if the file was successfully opened, 0 - if the file was not opened. Let's finalize the program with the opening of the file, in such a way that if the file is not opened, a corresponding message is displayed.

// file_read.cpp: defines the entry point for the console application. #include "stdafx.h" #include #include using namespace std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // correct display of Cyrillic char buff; // intermediate storage buffer of text read from file ifstream fin("cppstudio.doc"); // ( INCORRECT FILE NAME ENTERED) if (!fin.is_open()) // if the file is not open cout<< "Файл не может быть открыт!\n"; // сообщить об этом else { fin >> buff; // read first word from file cout<< buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку } system("pause"); return 0; }

The result of the program is shown in Figure 2.

The file cannot be opened! Press any key to continue. . .

Figure 2 - Working with files in C++

As you can see from Figure 2, the program reported that the file could not be opened. Therefore, if the program is working with files, it is recommended to use this function, is_open() , even if you are sure that the file exists.

File opening modes

File opening modes determine how files are used. To set the mode, the ios_base class provides constants that determine the file opening mode (see Table 1).

File opening modes can be set directly when creating an object or when calling the open() function .

Ofstream fout("cppstudio.txt", ios_base::app); // open the file to add information to the end of the file fout. open("cppstudio.txt", ios_base::app); // open the file to add information to the end of the file

File open modes can be combined using a bitwise boolean operation or| , for example: ios_base::out | ios_base::trunc - open a file for writing, after clearing it.

Objects of the class ofstream , when associated with files, by default contain file open modes ios_base::out | ios_base::trunc . That is, the file will be created if it does not exist. If the file exists, then its contents will be deleted, and the file itself will be ready for recording. Objects of the ifstream class, when associated with a file, have by default the file opening mode ios_base::in - the file is open for reading only. The file opening mode is also called the flag, for readability we will use this term in the future. Table 1 does not list all the flags, but these should be enough to get you started.

Please note that the ate and app flags are very similar in description, they both move the pointer to the end of the file, but the app flag allows writing only to the end of the file, and the ate flag simply rearranges the flag to the end of the file and does not limit the recording space.

Let's develop a program that, using the sizeof() operation, will calculate the characteristics of the main data types in C++ and write them to a file. Characteristics:

  1. the number of bytes allocated for the data type
  2. the maximum value that a particular data type can store.

Writing to a file must be in the following format:

/* data type byte max value bool = 1 255.00 char = 1 255.00 short int = 2 32767.00 unsigned short int = 2 65535.00 int = 4 2147483647.00 unsigned int = 4 4294967295.00 long int = 4 2147483647.00 unsigned long int = 4 4294967295.00 float = 4 2147483647.00 long float = 8 9223372036854775800.00 double = 8 9223372036854775800.00 */

Such a program has already been developed earlier in the section, but there all information about data types was output to the standard output device, and we need to remake the program so that the information is written to a file. To do this, you need to open the file in write mode, with preliminary truncation of the current file information ( line 14). Once the file has been created and successfully opened (lines 16 - 20), instead of the cout statement, in line 22 use the fout object. thus, instead of a screen, information about data types will be written to a file.

// write_file.cpp: defines the entry point for the console application. #include "stdafx.h" #include #include // work with files #include // I/O manipulators using namespace std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // link the object to the file, while opening the file in write mode, deleting all data from it first ofstream fout("data_types.txt", ios_base::out | ios_base::trunc); if (!fout.is_open()) // if file has not been opened ( cout<< "Файл не может быть открыт или создан\n"; // напечатать соответствующее сообщение return 1; // выполнить выход из программы } fout << " data type " << "byte" << " " << " max value "<< endl // column headings <<"bool = " << sizeof(bool) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных bool*/ << (pow(2,sizeof(bool) * 8.0) - 1) << endl << "char = " << sizeof(char) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных char*/ << (pow(2,sizeof(char) * 8.0) - 1) << endl << "short int = " << sizeof(short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных short int*/ << (pow(2,sizeof(short int) * 8.0 - 1) - 1) << endl << "unsigned short int = " << sizeof(unsigned short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned short int*/ << (pow(2,sizeof(unsigned short int) * 8.0) - 1) << endl << "int = " << sizeof(int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных int*/ << (pow(2,sizeof(int) * 8.0 - 1) - 1) << endl << "unsigned int = " << sizeof(unsigned int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned int*/ << (pow(2,sizeof(unsigned int) * 8.0) - 1) << endl << "long int = " << sizeof(long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long int*/ << (pow(2,sizeof(long int) * 8.0 - 1) - 1) << endl << "unsigned long int = " << sizeof(unsigned long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных undigned long int*/ << (pow(2,sizeof(unsigned long int) * 8.0) - 1) << endl << "float = " << sizeof(float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных float*/ << (pow(2,sizeof(float) * 8.0 - 1) - 1) << endl << "long float = " << sizeof(long float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long float*/ << (pow(2,sizeof(long float) * 8.0 - 1) - 1) << endl << "double = " << sizeof(double) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных double*/ << (pow(2,sizeof(double) * 8.0 - 1) - 1) << endl; fout.close(); // программа больше не использует файл, поэтому его нужно закрыть cout << "Данные успешно записаны в файл data_types.txt\n"; system("pause"); return 0; }

It is impossible not to notice that the changes in the program are minimal, and all thanks to the fact that the standard input/output and file input/output are used in exactly the same way. At the end of the program,line 45we have explicitly closed the file, although this is not required, it is considered good programming practice. It is worth noting that all functions and manipulators used for formatting standard input/output are relevant for file input/output as well. Therefore, no errors occurred when the operator cout has been replaced by an object fout.

Last update: 31.10.2015

Two classes are designed to work with directories in the System.IO namespace: Directory and DirectoryInfo .

Directory class

The Directory class provides a number of static methods for managing directories. Some of these methods are:

    CreateDirectory(path) : creates a directory at the specified path

    Delete(path) : deletes the directory at the given path

    Exists(path) : Determines if the directory at the specified path exists. Returns true if exists, false if not.

    GetDirectories(path) : Gets a list of directories in path

    GetFiles(path) : Gets a list of files in directory path

    Move(sourceDirName, destDirName): moves directory

    GetParent(path) : get the parent directory

DirectoryInfo class

This class provides functionality for creating, deleting, moving, and other directory operations. In many ways, it is similar to Directory. Some of its properties and methods are:

    Create() : creates a directory

    CreateSubdirectory(path) : creates a subdirectory at the specified path

    Delete() : deletes a directory

    Exists property: determines if the directory exists

    GetDirectories() : Gets a list of directories

    GetFiles() : get a list of files

    MoveTo(destDirName) : moves a directory

    Parent property: getting the parent directory

    Root property: getting the root directory

Let's look at examples of the use of these classes

Getting a list of files and subdirectories

string dirName = "C:\\"; if (Directory.Exists(dirName)) ( Console.WriteLine("Subdirectories:"); string dirs = Directory.GetDirectories(dirName); foreach (string s in dirs) ( Console.WriteLine(s); ) Console.WriteLine( ); Console.WriteLine("Files:"); string files = Directory.GetFiles(dirName); foreach (string s in files) ( Console.WriteLine(s); ) )

Note the use of slashes in filenames. Either we use a double slash: "C:\\" , or a single one, but then we put the @ sign in front of the whole path: @"C:\Program Files"

Create a directory

string path = @"C:\SomeDir"; string subpath = @"program\avalon"; DirectoryInfo dirInfo = new DirectoryInfo(path); if (!dirInfo.Exists) ( dirInfo.Create(); ) dirInfo.CreateSubdirectory(subpath);

First, we check if there is such a directory, because if it exists, then it will not be possible to create it, and the application will throw an error. As a result, we will get the following path: "C:\SomeDir\program\avalon"

Getting information about a directory

string dirName = "C:\\Program Files"; DirectoryInfo dirInfo = new DirectoryInfo(dirName); Console.WriteLine($"Directory name: (dirInfo.Name)"); Console.WriteLine($"Full directory name: (dirInfo.FullName)"); Console.WriteLine($"Directory creation time: (dirInfo.CreationTime)"); Console.WriteLine($"Root Directory: (dirInfo.Root)");

Deleting a directory

If we simply apply the Delete method to a non-empty folder that contains any files or subdirectories, then the application will throw an error. Therefore, we need to pass an additional boolean type parameter to the Delete method, which will indicate that the folder should be deleted with all its contents:

String dirName = @"C:\SomeFolder"; try ( DirectoryInfo dirInfo = new DirectoryInfo(dirName); dirInfo.Delete(true); Console.WriteLine("Directory deleted"); ) catch (Exception ex) ( Console.WriteLine(ex.Message); )

String dirName = @"C:\SomeFolder"; Directory.Delete(dirName, true);

Moving a directory

string oldPath = @"C:\SomeFolder"; string newPath = @"C:\SomeDir"; DirectoryInfo dirInfo = new DirectoryInfo(oldPath); if (dirInfo.Exists && Directory.Exists(newPath) == false) ( dirInfo.MoveTo(newPath); )

When moving, keep in mind that the new directory into which we want to move all the contents of the old directory must not exist.

For ease of handling, information in storage devices is stored in the form of files.

File is a named area of ​​external memory allocated for storing an array of data. The data contained in the files are of the most diverse nature: programs in algorithmic or machine language; initial data for the operation of programs or results of program execution; arbitrary texts; graphics, etc.

Directory (folder, directory) - a named collection of bytes on a storage medium containing the names of subdirectories and files, used in the file system to simplify the organization of files.

file system is a functional part of the operating system that provides operations on files. Examples of file systems are FAT (FAT - File Allocation Table, file allocation table), NTFS, UDF (used on CDs).

There are three major versions of FAT: FAT12, FAT16, and FAT32. They differ in the bitness of records in the disk structure, i.e. the number of bits allocated to store the cluster number. FAT12 is mainly used for floppy disks (up to 4 KB), FAT16 for small disks, FAT32 for high-capacity FLASH drives (up to 32 GB).

Consider the structure of the file system using FAT32 as an example.

FAT32 file structure

External memory devices in the FAT32 system are not byte, but block addressing. Information is written to an external memory device in blocks or sectors.

Sector - the minimum addressable unit of information storage on external storage devices. Typically, the sector size is fixed at 512 bytes. To increase the address space of external memory devices, sectors are combined into groups called clusters.

A cluster is an association of several sectors, which can be considered as an independent unit with certain properties. The main property of a cluster is its size, measured in the number of sectors or the number of bytes.

The FAT32 file system has the following structure.

Clusters used for writing files are numbered starting from 2. As a rule, cluster #2 is used by the root directory, and starting from cluster #3, the data array is stored. Sectors used to store information above the root directory are not clustered.
The minimum file size on disk is 1 cluster.

The boot sector begins with the following information:

  • EB 58 90 - unconditional branch and signature;
  • 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
  • 00 02 - the number of bytes in the sector (usually 512);
  • 1 byte - the number of sectors in the cluster;
  • 2 bytes - the number of spare sectors.

In addition, the boot sector contains the following important information:

  • 0x10 (1 byte) – number of FAT tables (usually 2);
  • 0x20 (4 bytes) - the number of sectors on the disk;
  • 0x2C (4 bytes) – root directory cluster number;
  • 0x47 (11 bytes) – volume label;
  • 0x1FE (2 bytes) - Boot sector signature (55 AA).

The file system information sector contains:

  • 0x00 (4 bytes) – signature (52 52 61 41 );
  • 0x1E4 (4 bytes) – signature (72 72 41 61 );
  • 0x1E8 (4 bytes) – number of free clusters, -1 if not known;
  • 0x1EC (4 bytes) – number of the last recorded cluster;
  • 0x1FE (2 bytes) - signature (55 AA).

The FAT table contains information about the state of each cluster on the disk. The lower 2 bytes of the FAT table store F8 FF FF 0F FF FF FF FF (corresponding to the state of clusters 0 and 1, physically absent). Further, the state of each cluster contains the number of the cluster in which the current file continues or the following information:

  • 00 00 00 00 – the cluster is free;
  • FF FF FF 0F is the end of the current file.
  • 8 bytes - file name;
  • 3 bytes - file extension;

The root directory contains a set of 32-bit information records for each file containing the following information:

When working with long file names (including Russian names), the file name is encoded in the UTF-16 encoding system. In this case, 2 bytes are allocated for encoding each character. In this case, the file name is written in the form of the following structure:

  • 1 byte sequence;
  • 10 bytes contain the lower 5 characters of the file name;
  • 1 byte attribute;
  • 1 byte reserved;
  • 1 byte - DOS name checksum;
  • 12 bytes contain the lower 3 characters of the filename;
  • 2 bytes – number of the first cluster;
  • the remaining characters of the long name.

Working with Files in C

To the programmer, an open file is represented as a sequence of data being read or written. When a file is opened, it is associated with I/O flow. Output information is written to the stream, input information is read from the stream.

When a stream is opened for I/O, it is associated with the standard structure of type FILE , which is defined in stdio.h . The FILE structure contains the necessary information about the file.

Opening a file is done using the fopen() function, which returns a pointer to a structure of type FILE , which can be used for subsequent operations on the file.

FILE *fopen(name, type);


name is the name of the file to be opened (including the path),
type is a pointer to a string of characters that define how the file is accessed:
  • "r" - open file for reading (file must exist);
  • "w" - open an empty file for writing; if the file exists, its contents are lost;
  • "a" - open file for writing to the end (for appending); the file is created if it does not exist;
  • "r+" - open file for reading and writing (file must exist);
  • "w+" - open an empty file for reading and writing; if the file exists, its contents are lost;
  • "a+" - open the file for reading and appending, if the file does not exist, it is created.

The return value is a pointer to the open stream. If an error is found, NULL is returned.

The fclose() function closes the stream or streams associated with files opened with fopen(). The stream to be closed is determined by the argument of the fclose() function.

Return value: value 0 if the stream was closed successfully; the EOF constant if an error occurred.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include
int main() (
FILE *fp;
char name = "my.txt" ;
if ((fp = fopen(name, "r" )) == NULL )
{
printf( "Could not open file");
getchar();
return 0;
}
// open file succeeded
... // required actions on data
fclose(fp);
getchar();
return 0;
}

Reading a character from a file:

char fgetc(stream);


The function argument is a pointer to a stream of type FILE . The function returns the code of the read character. If the end of file is reached or an error occurs, the EOF constant is returned.

Writing a character to a file:

fputc(character, stream);

The function's arguments are a character and a pointer to a stream of type FILE . The function returns the code of the read character.

The fscanf() and fprintf() functions are similar to the scanf() and printf() functions, but operate on data files and have a file pointer as their first argument.

fscanf(stream, "InputFormat" , args);