C++ 에서 파일 작업 수행하기

C++Beginner
지금 연습하기

소개

본 랩에서는 C++ 에서 다양한 파일 작업을 수행하는 방법을 배웁니다. 이 랩에서는 파일 열기 (읽기 및 쓰기), 텍스트 데이터 파일에 쓰기, 텍스트 파일에서 데이터 읽기, 파일 열림 상태 확인, 파일 읽기/쓰기 오류 처리, 바이너리 파일 작업, 파일 포인터 위치 지정, 리소스 해제를 위한 파일 닫기 등을 다룹니다. C++ 표준 라이브러리의 기본 파일 스트림 클래스인 ofstreamifstream을 사용하여 파일과 상호 작용하는 실질적인 경험을 쌓게 될 것입니다. 이 랩이 끝날 때쯤에는 C++ 프로그래밍에서 파일 처리에 대한 확고한 이해를 갖게 될 것입니다.

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 초급 레벨의 실험이며 완료율은 88%입니다.학습자들로부터 100%의 긍정적인 리뷰율을 받았습니다.

ofstream 및 ifstream 을 사용하여 파일 열기

이 단계에서는 파일을 쓰기 위한 ofstream과 파일을 읽기 위한 ifstream이라는 두 가지 기본 파일 스트림 클래스를 사용하여 C++ 에서 파일을 여는 방법을 배웁니다. 이 클래스들은 C++ 표준 라이브러리의 일부이며 필수적인 파일 처리 기능을 제공합니다.

먼저 WebIDE 터미널에서 프로젝트 디렉토리로 이동합니다:

cd ~/project

file_operations.cpp라는 새 C++ 파일을 생성합니다:

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 << "File opened for writing successfully!" << std::endl;
        outputFile.close();  // 파일 닫기
    } else {
        std::cout << "Unable to open file for writing." << std::endl;
    }

    // ifstream 을 사용하여 읽기를 위해 파일 열기
    std::ifstream inputFile("example.txt");

    // 파일이 성공적으로 열렸는지 확인
    if (inputFile.is_open()) {
        std::cout << "File opened for reading successfully!" << std::endl;
        inputFile.close();  // 파일 닫기
    } else {
        std::cout << "Unable to open file for reading." << std::endl;
    }

    return 0;
}

주요 개념을 분석해 보겠습니다:

  1. #include <fstream>: 파일 스트림 라이브러리를 포함합니다.
  2. std::ofstream: 파일을 쓰기 위한 출력 파일 스트림 (Output File Stream) 입니다.
  3. std::ifstream: 파일을 읽기 위한 입력 파일 스트림 (Input File Stream) 입니다.
  4. is_open(): 파일이 성공적으로 열렸는지 확인합니다.
  5. close(): 작업 후 파일을 닫습니다.

프로그램을 컴파일합니다:

g++ file_operations.cpp -o file_operations

실행 파일을 실행합니다:

./file_operations

예상 출력:

File opened for writing successfully!
File opened for reading successfully!

파일 스트림에 대한 몇 가지 중요한 사항:

  • 작업을 수행하기 전에 항상 파일이 성공적으로 열렸는지 확인하십시오.
  • 파일 사용을 마친 후에는 반드시 파일을 닫아야 합니다.
  • ofstream은 파일에 쓰기 위해 사용됩니다.
  • ifstream은 파일에서 읽기 위해 사용됩니다.
  • 두 클래스 모두 <fstream> 헤더의 일부입니다.

파일 스트림을 파일에 대한 문을 열고 닫는 것으로 생각할 수 있습니다. ofstream은 방에 쓸 수 있게 해주고, ifstream은 내부에 무엇이 있는지 읽을 수 있게 해줍니다.

파일에 텍스트 데이터 쓰기

이 단계에서는 C++ 의 ofstream 클래스를 사용하여 파일에 텍스트 데이터를 쓰는 방법을 배웁니다. 이전 단계를 기반으로 문자열, 숫자 및 여러 줄을 파일에 쓰는 다양한 방법을 탐구합니다.

write_files.cpp라는 새 C++ 파일을 생성합니다:

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 << "Data written to file successfully!" << std::endl;
    } else {
        std::cout << "Unable to open file for writing." << std::endl;
    }

    return 0;
}

프로그램을 컴파일합니다:

g++ write_files.cpp -o write_files

실행 파일을 실행합니다:

./write_files

예상 출력:

Data written to file successfully!

파일 내용을 확인합니다:

cat student_data.txt

예상 파일 내용:

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

파일 쓰기에 대한 주요 사항:

  • 데이터 쓰기를 위해 << 연산자를 사용합니다.
  • std::endl은 새 줄을 추가합니다.
  • 문자열, 숫자 및 변수를 쓸 수 있습니다.
  • 쓰기 전에 파일이 열려 있는지 항상 확인하십시오.
  • 쓰기 작업 후 파일을 닫습니다.

파일에 쓰는 것을 공책에 쓰는 것에 비유할 수 있습니다. ofstream은 펜이고, 파일은 정보를 기록하는 페이지입니다.

텍스트 파일에서 데이터 읽기

이 단계에서는 C++ 의 ifstream 클래스를 사용하여 텍스트 파일에서 데이터를 읽는 방법을 배웁니다. 이전 단계를 기반으로 줄, 단어 및 전체 파일을 읽는 다양한 방법을 탐구합니다.

read_files.cpp라는 새 C++ 파일을 생성합니다:

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 << "Reading entire lines:" << std::endl;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }

        // 파일 포인터를 시작 위치로 재설정
        inputFile.clear();
        inputFile.seekg(0, std::ios::beg);

        // 개별 단어 읽기
        std::cout << "\nReading individual words:" << std::endl;
        std::string word;
        while (inputFile >> word) {
            std::cout << word << " ";
        }
        std::cout << std::endl;

        // 파일 닫기
        inputFile.close();
    } else {
        std::cout << "Unable to open file for reading." << std::endl;
    }

    return 0;
}

프로그램을 컴파일합니다:

g++ read_files.cpp -o read_files

실행 파일을 실행합니다:

./read_files

예상 출력:

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

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

파일 읽기에 대한 주요 사항:

  • 전체 줄을 읽으려면 std::getline()을 사용합니다.
  • 개별 단어를 읽으려면 >> 연산자를 사용합니다.
  • clear()seekg()는 파일 포인터를 재설정합니다.
  • 읽기 전에 파일이 열려 있는지 항상 확인하십시오.
  • 읽기 작업 후 파일을 닫습니다.

파일을 읽는 것을 책에서 정보를 추출하는 것에 비유할 수 있습니다. ifstream은 책갈피 역할을 하여 텍스트를 탐색하는 데 도움을 줍니다.

파일 열림 상태 확인

이 단계에서는 C++ 에서 다양한 방법을 사용하여 파일 작업의 상태를 확인하고 파일이 성공적으로 열렸는지, 읽기 또는 쓰기를 위해 준비되었는지 확인하는 방법을 배웁니다.

file_status.cpp라는 새 C++ 파일을 생성합니다:

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 << "File opened successfully for reading." << std::endl;

        // 읽기 작업 수행
        std::string line;
        while (std::getline(inputFile, line)) {
            std::cout << "Read line: " << line << std::endl;
        }

        inputFile.close();
    } else {
        std::cout << "Failed to open file for reading." << std::endl;
    }

    // 방법 2: good() 을 사용하여 파일 상태 확인
    std::ofstream outputFile("test_status.txt");

    if (outputFile.good()) {
        std::cout << "File opened successfully for writing." << std::endl;
        outputFile << "Testing file status" << std::endl;

        // 추가 상태 확인
        if (outputFile) {
            std::cout << "File is in a good state for writing." << std::endl;
        }

        outputFile.close();
    } else {
        std::cout << "Failed to open file for writing." << std::endl;
    }

    // 방법 3: 여러 상태 확인 메서드
    std::ifstream checkFile("non_existent_file.txt");

    std::cout << "File status checks:" << 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

예상 출력:

File opened successfully for reading.
Read line: John Doe
Read line: Alice Smith, 22 years old, GPA: 3.75
Read line: Computer Science Student
Read line: University of Programming
File opened successfully for writing.
File is in a good state for writing.
File status checks:
is_open(): False
good(): False
fail(): True

파일 상태 확인에 대한 주요 사항:

  • is_open(): 파일이 성공적으로 열렸는지 확인합니다.
  • good(): 오류가 발생하지 않았는지 확인합니다.
  • fail(): 오류가 발생했는지 확인합니다.
  • 작업을 수행하기 전에 항상 파일 상태를 확인해야 합니다.
  • 다양한 메서드는 파일 상태를 확인하는 여러 방법을 제공합니다.

파일 상태 확인을 여행 전 안전 점검과 같다고 생각할 수 있습니다. 이러한 메서드는 파일 작업이 안전하고 성공적인지 확인하는 데 도움이 됩니다.

파일 읽기/쓰기 오류 처리

이 단계에서는 C++ 에서 파일 읽기 및 쓰기 작업 중 발생할 수 있는 잠재적 오류를 처리하는 방법을 배웁니다. 오류 처리를 이해하는 것은 강력한 파일 조작 프로그램을 만드는 데 중요합니다.

file_error_handling.cpp라는 새 C++ 파일을 생성합니다:

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 을 사용하여 바이너리 파일 작업

이 단계에서는 C++ 에서 fstream 클래스를 사용하여 바이너리 파일 작업을 수행하는 방법을 배웁니다. 바이너리 파일은 텍스트 파일과 달리 데이터를 원시 바이너리 형식으로 저장하며, 구조화된 데이터를 효율적으로 저장하는 데 유용합니다.

binary_files.cpp라는 새 C++ 파일을 생성합니다:

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 << "Error opening file for writing!" << 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 << "Binary file written successfully!" << std::endl;
}

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

    if (!binaryFile) {
        std::cerr << "Error opening file for reading!" << std::endl;
        return;
    }

    Student students[3];

    // 전체 구조체를 바이너리 파일에서 읽기
    binaryFile.read(reinterpret_cast<char*>(students), sizeof(students));

    std::cout << "Student Records:" << std::endl;
    for (int i = 0; i < 3; ++i) {
        std::cout << "ID: " << students[i].id
                  << ", Name: " << 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

예상 출력:

Binary file written successfully!
Student Records:
ID: 1, Name: John Doe, GPA: 3.5
ID: 2, Name: Alice Smith, GPA: 3.8
ID: 3, Name: Bob Johnson, GPA: 3.2

바이너리 파일에 대한 주요 사항:

  • 바이너리 모드에는 std::ios::binary 플래그를 사용합니다.
  • 바이너리 데이터 처리를 위한 write()read() 메서드.
  • 유형 간 변환을 위해 reinterpret_cast가 사용됩니다.
  • 구조화된 데이터를 효율적으로 저장하는 데 유용합니다.
  • 데이터의 정확한 바이너리 표현을 보존합니다.

바이너리 파일을 정밀한 청사진이라고 생각할 수 있습니다. 텍스트 변환 없이 메모리에 존재하는 그대로 데이터를 저장합니다.

seekg/seekp 를 사용하여 파일 포인터 위치 지정

이 단계에서는 C++ 에서 읽기를 위한 seekg()와 쓰기를 위한 seekp()를 사용하여 파일 포인터를 조작하는 방법을 배웁니다. 이 메서드들을 사용하면 파일 내의 특정 위치로 이동하고 수정할 수 있습니다.

file_pointer.cpp라는 새 C++ 파일을 생성합니다:

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 << "Error opening file!" << std::endl;
        return;
    }

    // 다른 위치에서 읽기
    std::cout << "Reading operations:" << std::endl;

    // 5 번째 바이트 (문자 위치) 로 이동
    file.seekg(5);
    int value;
    file >> value;
    std::cout << "Value at 5th byte: " << value << std::endl;

    // 파일의 시작 부분으로 이동
    file.seekg(0);
    file >> value;
    std::cout << "First value: " << value << std::endl;

    // 특정 위치에 쓰기
    std::cout << "\nWriting operations:" << std::endl;

    // 특정 위치로 이동하여 쓰기
    file.seekp(0);
    file << "100 ";

    // 포인터를 재설정하고 확인을 위해 읽기
    file.seekg(0);
    file >> value;
    std::cout << "Modified first value: " << value << std::endl;

    file.close();
}

int main() {
    // 숫자가 포함된 샘플 파일 생성
    createSampleFile();

    // seek 작업 시연
    demonstrateSeekOperations();

    return 0;
}

프로그램을 컴파일합니다:

g++ file_pointer.cpp -o file_pointer

실행 파일을 실행합니다:

./file_pointer

예상 출력:

Reading operations:
Value at 5th byte: 4
First value: 1

Writing operations:
Modified first value: 100

파일 포인터에 대한 주요 사항:

  • seekg(): 읽기 포인터 (get pointer) 이동
  • seekp(): 쓰기 포인터 (put pointer) 이동
  • 첫 번째 인수는 바이트 위치입니다.
  • 파일 내에서 임의 접근 (random access) 에 유용합니다.
  • 특정 위치로 이동할 수 있습니다.

파일 포인터를 텍스트 편집기의 커서라고 생각할 수 있습니다. seekg()seekp()는 이 커서를 원하는 정확한 위치로 이동시키는 데 도움을 줍니다.

파일 닫기 및 리소스 해제

이 단계에서는 C++ 에서 파일 닫기의 중요성과 메모리 누수를 방지하고 효율적인 파일 처리를 보장하기 위한 리소스 관리에 대해 배웁니다. 파일 닫기의 다양한 방법과 RAII(Resource Acquisition Is Initialization, 자원 획득은 초기화) 원칙의 사용법을 탐구합니다.

file_resources.cpp라는 새 C++ 파일을 생성합니다:

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 << "File closed manually." << std::endl;
    }
}

// unique_ptr 를 사용한 RAII 기반 파일 처리
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 file handling successful." << std::endl;
        }
        // unique_ptr 가 범위를 벗어나면 파일이 자동으로 닫힙니다.
    }
    catch (const std::exception& e) {
        std::cerr << "Error in RAII file handling: " << 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 << "Scoped file handling successful." << std::endl;
        }
    } // 파일이 여기서 자동으로 닫힙니다.
}

int main() {
    // 다양한 파일 리소스 관리 기법 시연
    manualFileHandling();
    raii_fileHandling();
    scopeBasedFileHandling();

    return 0;
}

프로그램을 컴파일합니다:

g++ file_resources.cpp -o file_resources

실행 파일을 실행합니다:

./file_resources

예상 출력:

File closed manually.
RAII file handling successful.
Scoped file handling successful.

파일 닫기 및 리소스 관리에 대한 주요 사항:

  • 사용 후에는 항상 파일을 닫아야 합니다.
  • 수동 닫기를 위해 close() 메서드를 사용합니다.
  • RAII 원칙을 활용합니다.
  • 범위 기반 리소스 관리를 사용합니다.
  • 리소스 누수를 방지합니다.
  • 파일 작업 중 예외를 처리합니다.

파일 리소스를 도서관에서 책을 빌리는 것에 비유할 수 있습니다. 도서관 (시스템 리소스) 을 정리하려면 사용이 끝나면 항상 책 (파일) 을 반납해야 합니다.

요약

본 랩 (lab) 에서는 표준 라이브러리의 ofstreamifstream 클래스를 사용하여 C++ 에서 파일을 여는 방법을 배웠습니다. 파일 열림 상태 확인, 파일에 텍스트 데이터 쓰기, 텍스트 파일에서 데이터 읽기 과정을 탐구했습니다. 추가적으로, 파일 읽기/쓰기 오류 처리, fstream을 사용한 바이너리 파일 작업, seekg/seekp를 사용한 파일 포인터 위치 지정, 그리고 파일 닫기 및 리소스 해제 방법을 이해했습니다. 이러한 기본적인 파일 작업은 C++ 애플리케이션에서 데이터 저장 및 검색을 관리하는 데 필수적입니다.