Выполнение операций с файлами на C++

C++C++Beginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом лабораторном занятии вы научитесь выполнять различные операции с файлами на языке C++. Лабораторная работа охватывает открытие файлов для чтения и записи, запись текстовых данных в файлы, чтение данных из текстовых файлов, проверку статуса открытия файла, обработку ошибок чтения/записи файлов, работу с бинарными файлами, позиционирование файлового указателя и закрытие файлов для освобождения ресурсов. Вы получите практический опыт использования фундаментальных классов потоков файлов ofstream и ifstream из стандартной библиотеки C++ для взаимодействия с файлами. К концу этого лабораторного занятия вы будете иметь твердое понимание работы с файлами в программировании на C++.

Открытие файлов с использованием ofstream и ifstream

На этом этапе вы научитесь открывать файлы на языке C++ с помощью двух фундаментальных классов потоков файлов: ofstream для записи в файлы и ifstream для чтения из файлов. Эти классы являются частью стандартной библиотеки C++ и предоставляют основные возможности работы с файлами.

Сначала перейдите в каталог проекта в терминале WebIDE:

cd ~/project

Создайте новый файл на языке C++ с именем file_operations.cpp:

touch file_operations.cpp

Добавьте следующий код в файл file_operations.cpp:

#include <iostream>
#include <fstream>  // Подключение заголовочного файла потоков файлов

int main() {
    // Открытие файла для записи с использованием ofstream
    std::ofstream outputFile("example.txt");

    // Проверка, успешно ли открыт файл
    if (outputFile.is_open()) {
        std::cout << "Файл успешно открыт для записи!" << std::endl;
        outputFile.close();  // Закрытие файла
    } else {
        std::cout << "Не удалось открыть файл для записи." << std::endl;
    }

    // Открытие файла для чтения с использованием ifstream
    std::ifstream inputFile("example.txt");

    // Проверка, успешно ли открыт файл
    if (inputFile.is_open()) {
        std::cout << "Файл успешно открыт для чтения!" << std::endl;
        inputFile.close();  // Закрытие файла
    } else {
        std::cout << "Не удалось открыть файл для чтения." << std::endl;
    }

    return 0;
}

Разберем основные концепции:

  1. #include <fstream>: Подключает библиотеку потоков файлов
  2. std::ofstream: Выходной поток файла для записи в файлы
  3. std::ifstream: Входной поток файла для чтения из файлов
  4. is_open(): Проверяет, успешно ли открыт файл
  5. close(): Закрывает файл после выполнения операций

Скомпилируйте программу:

g++ file_operations.cpp -o file_operations

Запустите исполняемый файл:

./file_operations

Пример вывода:

Файл успешно открыт для записи!
Файл успешно открыт для чтения!

Некоторые важные моменты о потоках файлов:

  • Всегда проверяйте, успешно ли открыт файл, прежде чем выполнять операции с ним
  • Не забудьте закрыть файлы после использования
  • ofstream используется для записи в файлы
  • ifstream используется для чтения из файлов
  • Оба класса являются частью заголовочного файла <fstream>

Вы можете представить потоки файлов как открытие и закрытие дверей в файлы. ofstream позволяет вам записывать данные в комнату (файл), а ifstream позволяет вам читать то, что находится внутри.

Запись текстовых данных в файлы

На этом этапе вы научитесь записывать текстовые данные в файлы с использованием класса ofstream на языке C++. Продолжая работу с предыдущим этапом, вы рассмотрите различные способы записи строк, чисел и нескольких строк в файлы.

Создайте новый файл на языке C++ с именем write_files.cpp:

touch ~/project/write_files.cpp

Добавьте следующий код в файл write_files.cpp:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    // Открытие файла для записи
    std::ofstream outputFile("student_data.txt");

    // Проверка, успешно ли открыт файл
    if (outputFile.is_open()) {
        // Запись одной строки
        outputFile << "John Doe" << std::endl;

        // Запись нескольких частей данных
        std::string name = "Alice Smith";
        int age = 22;
        double gpa = 3.75;
        outputFile << name << ", " << age << " years old, GPA: " << gpa << std::endl;

        // Запись нескольких строк
        outputFile << "Computer Science Student" << std::endl;
        outputFile << "University of Programming" << std::endl;

        // Закрытие файла
        outputFile.close();
        std::cout << "Данные успешно записаны в файл!" << std::endl;
    } else {
        std::cout << "Не удалось открыть файл для записи." << std::endl;
    }

    return 0;
}

Скомпилируйте программу:

g++ write_files.cpp -o write_files

Запустите исполняемый файл:

./write_files

Пример вывода:

Данные успешно записаны в файл!

Проверьте содержимое файла:

cat student_data.txt

Пример содержимого файла:

John Doe
Alice Smith, 22 years old, GPA: 3.75
Computer Science Student
University of Programming

Основные моменты при записи в файлы:

  • Используйте оператор << для записи данных в файлы
  • std::endl добавляет новую строку
  • Можно записывать строки, числа и переменные
  • Всегда проверяйте, открыт ли файл, прежде чем записывать в него
  • Закрывайте файл после выполнения операций записи

Вы можете представить запись в файлы как запись в тетрадь. ofstream - это ваш ручка, а файл - страница, на которой вы записываете информацию.

Чтение данных из текстовых файлов

На этом этапе вы научитесь считывать данные из текстовых файлов с использованием класса ifstream на языке C++. Продолжая работу с предыдущими шагами, вы рассмотрите различные методы для чтения строк, слов и всего файла целиком.

Создайте новый файл на языке C++ с именем read_files.cpp:

touch ~/project/read_files.cpp

Добавьте следующий код в файл read_files.cpp:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    // Открытие файла, созданного на предыдущем этапе
    std::ifstream inputFile("student_data.txt");

    // Проверка, успешно ли открыт файл
    if (inputFile.is_open()) {
        // Чтение целой строки
        std::string line;
        std::cout << "Чтение целых строк:" << std::endl;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }

        // Сброс файлового указателя в начало
        inputFile.clear();
        inputFile.seekg(0, std::ios::beg);

        // Чтение отдельных слов
        std::cout << "\nЧтение отдельных слов:" << std::endl;
        std::string word;
        while (inputFile >> word) {
            std::cout << word << " ";
        }
        std::cout << std::endl;

        // Закрытие файла
        inputFile.close();
    } else {
        std::cout << "Не удалось открыть файл для чтения." << std::endl;
    }

    return 0;
}

Скомпилируйте программу:

g++ read_files.cpp -o read_files

Запустите исполняемый файл:

./read_files

Пример вывода:

Чтение целых строк:
John Doe
Alice Smith, 22 years old, GPA: 3.75
Computer Science Student
University of Programming

Чтение отдельных слов:
John Doe Alice Smith, 22 years old, GPA: 3.75 Computer Science Student University of Programming

Основные моменты при чтении файлов:

  • Используйте std::getline() для чтения целых строк
  • Используйте оператор >> для чтения отдельных слов
  • clear() и seekg() сбрасывают файловый указатель
  • Всегда проверяйте, открыт ли файл, прежде чем читать из него
  • Закрывайте файл после выполнения операций чтения

Вы можете представить чтение файлов как извлечение информации из книги. ifstream - это ваш закладка, которая помогает вам перемещаться по тексту.

Проверка статуса открытия файла

На этом этапе вы научитесь проверять статус операций с файлами на языке C++ с использованием различных методов для проверки, успешно ли файлы открыты и готовы к чтению или записи.

Создайте новый файл на языке C++ с именем file_status.cpp:

touch ~/project/file_status.cpp

Добавьте следующий код в файл file_status.cpp:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    // Попытка открыть существующий файл для чтения
    std::ifstream inputFile("student_data.txt");

    // Метод 1: Использование is_open() для проверки статуса файла
    if (inputFile.is_open()) {
        std::cout << "Файл успешно открыт для чтения." << std::endl;

        // Выполнение операций чтения
        std::string line;
        while (std::getline(inputFile, line)) {
            std::cout << "Прочитанная строка: " << line << std::endl;
        }

        inputFile.close();
    } else {
        std::cout << "Не удалось открыть файл для чтения." << std::endl;
    }

    // Метод 2: Использование good() для проверки состояния файла
    std::ofstream outputFile("test_status.txt");

    if (outputFile.good()) {
        std::cout << "Файл успешно открыт для записи." << std::endl;
        outputFile << "Testing file status" << std::endl;

        // Дополнительные проверки статуса
        if (outputFile) {
            std::cout << "Файл находится в хорошем состоянии для записи." << std::endl;
        }

        outputFile.close();
    } else {
        std::cout << "Не удалось открыть файл для записи." << std::endl;
    }

    // Метод 3: Несколько методов проверки статуса
    std::ifstream checkFile("non_existent_file.txt");

    std::cout << "Проверки статуса файла:" << std::endl;
    std::cout << "is_open(): " << (checkFile.is_open()? "True" : "False") << std::endl;
    std::cout << "good(): " << (checkFile.good()? "True" : "False") << std::endl;
    std::cout << "fail(): " << (checkFile.fail()? "True" : "False") << std::endl;

    return 0;
}

Скомпилируйте программу:

g++ file_status.cpp -o file_status

Запустите исполняемый файл:

./file_status

Пример вывода:

Файл успешно открыт для чтения.
Прочитанная строка: John Doe
Прочитанная строка: Alice Smith, 22 years old, GPA: 3.75
Прочитанная строка: Computer Science Student
Прочитанная строка: University of Programming
Файл успешно открыт для записи.
Файл находится в хорошем состоянии для записи.
Проверки статуса файла:
is_open(): False
good(): False
fail(): True

Основные моменты при проверке статуса файла:

  • is_open(): Проверяет, успешно ли открыт файл
  • good(): Проверяет, не произошло ли ошибок
  • fail(): Проверяет, произошла ли ошибка
  • Всегда проверяйте статус файла перед выполнением операций
  • Различные методы предоставляют различные способы проверки состояния файла

Вы можете представить проверки статуса файла как технический осмотр перед поездкой. Эти методы помогают убедиться, что ваши операции с файлами безопасны и успешны.

Обработка ошибок чтения и записи файлов

На этом этапе вы научитесь обрабатывать потенциальные ошибки, которые могут возникнуть при операциях чтения и записи файлов на языке C++. Понимание обработки ошибок является важной частью создания надежных программ для работы с файлами.

Создайте новый файл на языке C++ с именем file_error_handling.cpp:

touch ~/project/file_error_handling.cpp

Добавьте следующий код в файл file_error_handling.cpp:

#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>

void writeToFile(const std::string& filename) {
    std::ofstream outputFile(filename);

    // Проверка, открыт ли файл
    if (!outputFile) {
        // Генерация исключения, если файл не может быть открыт
        throw std::runtime_error("Unable to open file for writing: " + filename);
    }

    try {
        // Попытка записи данных
        outputFile << "Hello, Error Handling!" << std::endl;
        outputFile << "This is a sample text." << std::endl;

        // Симуляция ошибки записи (не рекомендуется делать намеренно)
        if (outputFile.bad()) {
            throw std::runtime_error("Write operation failed");
        }
    }
    catch (const std::exception& e) {
        std::cerr << "Error during writing: " << e.what() << std::endl;
    }

    outputFile.close();
}

void readFromFile(const std::string& filename) {
    std::ifstream inputFile(filename);
    std::string line;

    // Проверка, открыт ли файл
    if (!inputFile) {
        // Генерация исключения, если файл не может быть открыт
        throw std::runtime_error("Unable to open file for reading: " + filename);
    }

    try {
        // Чтение и вывод содержимого файла
        std::cout << "File contents:" << std::endl;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }

        // Проверка на ошибки чтения
        if (inputFile.bad()) {
            throw std::runtime_error("Read operation encountered an error");
        }
    }
    catch (const std::exception& e) {
        std::cerr << "Error during reading: " << e.what() << std::endl;
    }

    inputFile.close();
}

int main() {
    try {
        // Запись в файл
        writeToFile("error_handling_example.txt");

        // Чтение из файла
        readFromFile("error_handling_example.txt");

        // Попытка чтения из несуществующего файла
        readFromFile("non_existent_file.txt");
    }
    catch (const std::exception& e) {
        std::cerr << "Main error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

Скомпилируйте программу:

g++ file_error_handling.cpp -o file_error_handling

Запустите исполняемый файл:

./file_error_handling

Пример вывода:

File contents:
Hello, Error Handling!
This is a sample text.
Error during reading: Unable to open file for reading: non_existent_file.txt

Основные моменты при обработке ошибок файловых операций:

  • Используйте блоки try-catch для обработки потенциальных исключений
  • Проверяйте состояние потока файла до и после операций
  • Используйте std::runtime_error для генерации осмысленных сообщений об ошибках
  • Обрабатывайте различные типы ошибок, связанных с файлами
  • Предоставляйте информативные сообщения об ошибках

Вы можете представить обработку ошибок как страховочную сеть. Она ловит потенциальные проблемы и предотвращает неожиданный сбой вашей программы.

Работа с бинарными файлами с использованием fstream

На этом этапе вы научитесь работать с бинарными файлами с использованием класса fstream на языке C++. Бинарные файлы хранят данные в их исходном двоичном формате, что отличается от текстовых файлов и полезно для эффективного хранения структурированных данных.

Создайте новый файл на языке C++ с именем binary_files.cpp:

touch ~/project/binary_files.cpp

Добавьте следующий код в файл binary_files.cpp:

#include <iostream>
#include <fstream>
#include <string>

// Простая структура для демонстрации записи в бинарный файл
struct Student {
    int id;
    char name[50];
    double gpa;
};

void writeBinaryFile() {
    std::fstream binaryFile("students.bin", std::ios::out | std::ios::binary);

    if (!binaryFile) {
        std::cerr << "Ошибка при открытии файла для записи!" << std::endl;
        return;
    }

    // Создание нескольких записей о студентах
    Student students[3] = {
        {1, "John Doe", 3.5},
        {2, "Alice Smith", 3.8},
        {3, "Bob Johnson", 3.2}
    };

    // Запись всей структуры в бинарный файл
    binaryFile.write(reinterpret_cast<char*>(students), sizeof(students));

    binaryFile.close();
    std::cout << "Бинарный файл успешно записан!" << std::endl;
}

void readBinaryFile() {
    std::fstream binaryFile("students.bin", std::ios::in | std::ios::binary);

    if (!binaryFile) {
        std::cerr << "Ошибка при открытии файла для чтения!" << std::endl;
        return;
    }

    Student students[3];

    // Чтение всей структуры из бинарного файла
    binaryFile.read(reinterpret_cast<char*>(students), sizeof(students));

    std::cout << "Записи о студентах:" << std::endl;
    for (int i = 0; i < 3; ++i) {
        std::cout << "ID: " << students[i].id
                  << ", Имя: " << students[i].name
                  << ", GPA: " << students[i].gpa << std::endl;
    }

    binaryFile.close();
}

int main() {
    // Запись в бинарный файл
    writeBinaryFile();

    // Чтение из бинарного файла
    readBinaryFile();

    return 0;
}

Скомпилируйте программу:

g++ binary_files.cpp -o binary_files

Запустите исполняемый файл:

./binary_files

Пример вывода:

Бинарный файл успешно записан!
Записи о студентах:
ID: 1, Имя: John Doe, GPA: 3.5
ID: 2, Имя: Alice Smith, GPA: 3.8
ID: 3, Имя: Bob Johnson, GPA: 3.2

Основные моменты о бинарных файлах:

  • Используйте флаг std::ios::binary для бинарного режима
  • Методы write() и read() для работы с бинарными данными
  • reinterpret_cast используется для преобразования типов
  • Полезны для эффективного хранения структурированных данных
  • Сохраняют точное двоичное представление данных

Вы можете представить бинарные файлы как точный чертеж. Они хранят данные именно так, как они существуют в памяти, без какого-либо текстового преобразования.

Перемещение файлового указателя с использованием seekg/seekp

На этом этапе вы научитесь управлять файловыми указателями с помощью методов seekg() для чтения и seekp() для записи на языке C++. Эти методы позволяют перемещаться по файлу и изменять определенные позиции в нем.

Создайте новый файл на языке C++ с именем file_pointer.cpp:

touch ~/project/file_pointer.cpp

Добавьте следующий код в файл file_pointer.cpp:

#include <iostream>
#include <fstream>
#include <string>

void createSampleFile() {
    std::ofstream file("numbers.txt");
    for (int i = 1; i <= 10; ++i) {
        file << i << " ";
    }
    file.close();
}

void demonstrateSeekOperations() {
    // Открытие файла в режиме чтения и записи
    std::fstream file("numbers.txt", std::ios::in | std::ios::out);

    if (!file) {
        std::cerr << "Ошибка при открытии файла!" << std::endl;
        return;
    }

    // Чтение из разных позиций
    std::cout << "Операции чтения:" << std::endl;

    // Перемещение на 5-й байт (позицию символа)
    file.seekg(5);
    int value;
    file >> value;
    std::cout << "Значение на 5-м байте: " << value << std::endl;

    // Перемещение в начало файла
    file.seekg(0);
    file >> value;
    std::cout << "Первое значение: " << value << std::endl;

    // Запись в определенные позиции
    std::cout << "\nОперации записи:" << std::endl;

    // Перемещение в определенную позицию и запись
    file.seekp(0);
    file << "100 ";

    // Сброс файлового указателя и чтение для проверки
    file.seekg(0);
    file >> value;
    std::cout << "Измененное первое значение: " << value << std::endl;

    file.close();
}

int main() {
    // Создание примерного файла с числами
    createSampleFile();

    // Демонстрация операций перемещения указателя
    demonstrateSeekOperations();

    return 0;
}

Скомпилируйте программу:

g++ file_pointer.cpp -o file_pointer

Запустите исполняемый файл:

./file_pointer

Пример вывода:

Операции чтения:
Значение на 5-м байте: 4
Первое значение: 1

Операции записи:
Измененное первое значение: 10

Основные моменты о файловых указателях:

  • seekg(): Перемещает указатель чтения (get pointer)
  • seekp(): Перемещает указатель записи (put pointer)
  • Первый аргумент - это позиция в байтах
  • Полезно для произвольного доступа к файлам
  • Позволяет перемещаться в определенные места

Вы можете представить файловые указатели как курсор в текстовом редакторе. seekg() и seekp() помогают вам точно переместить этот курсор туда, куда вы хотите.

Закрытие файлов и освобождение ресурсов

На этом этапе вы узнаете о важности правильного закрытия файлов и управления ресурсами на языке C++ для предотвращения утечек памяти и обеспечения эффективной работы с файлами. Вы рассмотрите различные методы закрытия файлов и применение принципов RAII (Resource Acquisition Is Initialization, «Приобретение ресурса есть инициализация»).

Создайте новый файл на языке C++ с именем file_resources.cpp:

touch ~/project/file_resources.cpp

Добавьте следующий код в файл file_resources.cpp:

#include <iostream>
#include <fstream>
#include <string>
#include <memory>

// Ручное закрытие файла
void manualFileHandling() {
    std::ofstream outputFile("manual_file.txt");

    if (outputFile.is_open()) {
        outputFile << "Manually managed file resource" << std::endl;

        // Явно закрыть файл
        outputFile.close();

        std::cout << "Файл закрыт вручную." << std::endl;
    }
}

// Работа с файлами на основе RAII с использованием unique_ptr
void raii_fileHandling() {
    try {
        // Использование unique_ptr для управления ресурсом файла
        std::unique_ptr<std::ofstream> file(new std::ofstream("raii_file.txt"));

        if (file && file->is_open()) {
            *file << "RAII-managed file resource" << std::endl;
            std::cout << "Работа с файлом на основе RAII выполнена успешно." << std::endl;
        }
        // Файл автоматически закрывается, когда unique_ptr выходит из области видимости
    }
    catch (const std::exception& e) {
        std::cerr << "Ошибка при работе с файлом на основе RAII: " << e.what() << std::endl;
    }
}

// Работа с файлами на основе области видимости
void scopeBasedFileHandling() {
    {
        // Файл будет автоматически закрыт, когда выйдет из области видимости
        std::ofstream scopedFile("scoped_file.txt");

        if (scopedFile.is_open()) {
            scopedFile << "Scope-based file resource management" << std::endl;
            std::cout << "Работа с файлом на основе области видимости выполнена успешно." << std::endl;
        }
    } // Файл автоматически закрывается здесь
}

int main() {
    // Демонстрация различных методов управления ресурсами файлов
    manualFileHandling();
    raii_fileHandling();
    scopeBasedFileHandling();

    return 0;
}

Скомпилируйте программу:

g++ file_resources.cpp -o file_resources

Запустите исполняемый файл:

./file_resources

Пример вывода:

Файл закрыт вручную.
Работа с файлом на основе RAII выполнена успешно.
Работа с файлом на основе области видимости выполнена успешно.

Основные моменты при закрытии файлов и управлении ресурсами:

  • Всегда закрывайте файлы после использования
  • Используйте метод close() для ручного закрытия
  • Применяйте принципы RAII
  • Используйте управление ресурсами на основе области видимости
  • Предотвращайте утечки ресурсов
  • Обрабатывайте исключения при работе с файлами

Вы можете представить ресурсы файлов как взятие книги в библиотеке. Всегда возвращайте книгу (закрывайте файл), когда закончите, чтобы библиотека (системные ресурсы) оставалась в порядке.

Резюме

В этом практическом занятии вы научились открывать файлы на языке C++ с использованием классов ofstream и ifstream из стандартной библиотеки. Вы изучили процесс проверки статуса открытия файла, записи текстовых данных в файлы и чтения данных из текстовых файлов. Кроме того, вы поняли, как обрабатывать ошибки чтения и записи файлов, работать с бинарными файлами с помощью fstream, перемещать файловый указатель с использованием seekg/seekp и правильно закрывать файлы и освобождать ресурсы. Эти основные операции с файлами являются важными для управления хранением и извлечением данных в приложениях на C++.