# Файлове (част 2). Двоични файлове. ## Режими за работа с файлове | Режим:| Ефект: | |--------|------------------------------------------------------------------------------| |ios::in |Отваря файл за четене (по подразбиране на ifstream) | |ios::out |Отваря файл за четене (по подразбиране на ofstream) | |ios::binary | Отваря файл в двоичен режим | |ios::trunc | Ако файлът съществува, съдържанието му се изтрива след отваряне на поток | |ios::app | Отваря файлът за вмъкване. Поставя put указателят в края. Не се допуска вмъкване преди края на потока | |ios::ate | Отваря файлът за вмъкване. Поставя put указателят в края. Допуска вмъкване на прозиволни места | |ios::nocreate | Отваря за вмъкване, само ако файлът с указаното име съществува.| ios::noreplace | Отваря за вмъкване само ако файлът с указаното име не съществува.| ### Пример: ```c++ #include #include using namespace std; int main() { ofstream f("test.txt", ios::app); if (!f.is_open()) { cout << "Error while opening file!" << endl; return -1; } f << 33; //appends at the end of the file f.close(); return 0; } ``` ## Флагове на състоянията на потока | Флаг:| Значение: | |--------|------------------------------------------------------------------------------| |bad() | Има загуба на информация. Някоя операция за четене и писане не е изпълнена. | |fail() |Последната входно/изходна операция е невалидна.| | eof() | Достигнат е края на файла. | |good() | Всички операции са изпълнени успешно.| |clear()| Изчиства състоянието на потока (Вече good() ще върне истина).| ## Двоични файлове. Изпоилзваме функциите: 1. .read(char* memoryBlock, size_t size); 2. .write(const char* memoryBlock, size_t size); Обърнете внимание, че функцията приема **char***. Ако искаме да запазим променлива от друг вид, ще трябва експлицитно да преобразуваме указателя към тип char* (без значение, че указателят не сочи към елементи от тип char) ### Пример за писане в двоичен файл: ```c++ #include #include using namespace std; int main() { ofstream f("file.dat"); int a = 155555; f.write((const char*)&a, sizeof(int)); return 0; } ``` ### Пример за четене от двоичен файл: ```c++ #include #include using namespace std; int main() { ifstream f("file.dat"); int a; f.read((char*)&a, sizeof(int)); cout << a << endl; return 0; } ``` ### Съдържанието на двоичния файл: ![enter image description here](https://i.ibb.co/G3R72qG/sudurjanie.png "Binary file example") Байтовете се записват в **обратна посока**. Най-старшият байт е последен. Следователно запаментеното число е **00025fa3**, което е точно шестанйсетичния запис на числото **155555**. ### Примери за запазване на стуктури във файл. - запазване на структура,която не използва динамична памет - **simple_struct_binary.cpp** и **student_staticName_to_binary.cpp** - запазване на структура,която използва динамична памет - **students_dynamicName_to_binary.cpp** ### Пример за запазване на масив от стуктури във файл. - запазване и четене на масив от обекти,които не използва динамична памет - **array_of_students_write.cpp**, **array_of_students_read.cpp** ```c++ #include #include using namespace std; struct Test { char ch; int a; }; int main() { ofstream f("output.dat", ios::binary); if(!f.is_open()) { cout << "Error!" << endl; return -1; } Test arr[3] = {{'a', 400},{'b', 500},{'c', 600}}; f.write((const char*)&arr, sizeof(arr)); f.close(); return 0; } ``` След изпълнението на програмата, файлът (**output.dat**) изглежда така: ![Image of the binary file after running the code](https://i.ibb.co/0JthLd6/3-A978-D14-7-C7-A-4-ABD-8-B0-C-DA27-F6-E9-CD0-A.png "Binary file") ## Позициониране във файл - tellg() - Връща позицития на текущия символ в **потока за четене** - tellp() - Връща позицития на текущия символ в **потока за писане** - seekg(offset, direction) - Премества get-указателят на позцития на **потока за четене**. - seekg(streampos idx) - Премества get-указателят на позция idx на **потока за четене**. - seekp(offset, direction) - Премества put-указателят на позцития на **потока за писане**. - seekp(streampos idx) - Премества put-указателят на позция idx на **потока за писане**. -**offset** : целочислена стойност. Отместването от direction. -**direction** : Може да заема следите стойностти: 1. ios::beg - началото на файла. 2. ios::cur - текущата позиция във файла. 3. ios::end - края на файла. ### Задачи **Задача 1:** Да се напишат програми, които запазват/четат масиви от обекти, които не използват динамична памет. **Задача 2:** Да се напише функция, която намира големината на файл. **Задача 3:** Да се напише функция, която заменя всяко срещане на символ във файл с друг символ. Съдържанието на файла не трябва да се зарежда в паметта. **Задача 4**: Да се реализира програма за изглед и модификация на двоични файлове (hex viewer). При стартиране на програмата трябва да се въведе път до двоичен файл и да зареди съдържанието на файла в паметта. След като файлът се е заредил в паметта, трябва да поддържате следните операции: - Преглед на файла (view) отпечатва байтовете на файла (в шестнайсетична бройна система). След това да се отпечатат интерпретацията на байтовете като символи. Ако байтът отговаря на малка/голяма латинска буква, то да се отпечата символа. В противен случай да се отпечатва точка. - Промяна на байт по индекс (change ). - Премахване на последния байт (remove) - Добавяне на байт в края (add ). - Запазване на промените в същия файл (save) - Запазване на промените в друг файл (save as ) Нека имаме двоичен файл, myData.dat, който се е получил след изпълнението на следния c++ код: ```c++ int x = 25409; ofstream file(“myData.dat”, ios::binary); file.write( (const char*)&x, sizeof(x)); ``` Да се реализира прост интерфейс, с който да се извикват написаните фунцкии през конзолата. **Важно** Не се ползволява използването на stl, както и std::hex. **Пример** (входът от потребителя започва с '>'): ``` Enter a file path: > myData.dat File loaded successfully! Size: 4 bytes > view 41 63 00 00 A c . . >change 1 65 Operation successfully executed! > view 41 65 00 00 A e . . > remove > view 41 65 00 A e . >save File successfully saved ``` **Забележка:** Не се изисква поддържането на конзолен интерфейс. Можете само да реализирате функциите и да ги изпробвате в main функцията.