C++ FileSystem

Seongcheol Jeon·2024년 11월 17일
0

CPP

목록 보기
18/47
post-thumbnail

파일 시스템이란?

파일 시스템이란 데이터를 저장하고 관리하는 체계를 의미한다. 이러한 파일 시스템은 운영체제에 따라 디렉토리, 파일 종류, 권한, 상태, 속석 등의 구조가 다르다. 따라서 운영체제마다 파일 시스템 프로그래밍에서 사용하는 헤더 파일과 사용 방법이 다르다.
C++ 표준 라이브러리에서는 이러한 어려움을 해결할 수 있도록 파일 시스템과 관련된 다양한 형식과 함수를 지원한다.

파일 시스템 라이브러리는 데이터의 입출력을 담당하는 fstream과는 다르다. fstream은 해당 파일의 내용을 읽어내는 역할을 하지만, 파일 이름이나 위치 같은 파일에 관한 정보(metadata)를 수정할 수는 없다. 반면에 파일 시스템 라이브러리는 파일에 관한 정보에 접근할 수 있지만, 파일 자체를 읽을 수는 없다.

파일 시스템 라이브러리

C++14까지 표준 라이브러리에는 파일 입출력을 위한 수단은 있었지만, 파일 시스템을 위한 수단은 없었다. C++17에서 드디어 C++도 파일 시스템 라이브러리를 지원하게 되었다.
C++ 파일 시스템 라이브러리의 모든 형식과 함수는 std::filesystem 네임스페이스에 속해 있으며, 이를 사용하려면 소스파일에 <filesystem> 헤더를 포함해야 한다.

C++ 파일 시스템 라이브러리는 파일과 디렉토리 조작에 유용한 기능을 제공한다. 주요 구성 요소와 함수를 정리하면 다음과 같다.

구분
이름
설명
클래스path파일과 디렉토리 경로를 나타내는 클래스. 경로 조작이나 분석 지원
directory_entry디렉토리 내의 항목을 나타내는 클래스. 경로나 속성 정보 제공
directory_iterator디렉토리 내의 모든 항목을 순회하기 위한 반복자 클래스
file_status파일이나 디렉토리의 상태 정보를 나타내는 열거형 클래스
file_time_type파일이나 시간 정보를 나타내는 file_time과 같은 형식
space_info디스크 공간에 대한 정보를 제공하는 클래스
열거형perms파일이나 디렉토리의 권한을 나타내는 열거형 상수 집합
file_type파일이나 디렉토리의 유형을 나타내는 열거형 상수 집합
함수exists주어진 경로에 파일이나 디렉토리가 존재하는지 확인
is_directory주어진 경로가 디렉토리인지 확인
is_regular_file주어진 경로가 일반 파일인지 확인
create_directory디렉토리 생성
create_directories경로에 지정된 디렉토리나 중간 디렉토리 생성
remove파일이나 디렉토리 삭제
rename파일이나 디렉토리의 이름 변경
copy파일이나 디렉토리 복사
copy_file파일 복사
copy_directory디렉토리 복사
copy_symlink심볼릭 링크 복사
file_size파일의 크기 반환
last_write_time파일이나 디렉토리의 마지막 수정 시간 반환
current_path현재 작업 디렉토리의 경로 반환
equivalent두 경로가 같은 파일이나 디렉토리를 가리키는지 확인
is_empty주어진 디렉토리가 비었는지 확인
remove_all디렉토리와 하위 항목 모두 삭제
resize_file파일 크기 변경
status파일이나 디렉토리의 상태 정보 반환
temp_directory_path임시 디렉토리의 경로 반환

이러한 기능들은 파일 시스템 조작을 더 편리하게 해주므로 필요할 때 찾아서 활용하면 더 빠르고 쉽게 프로그래밍할 수 있다.

경로를 나타내는 path 객체

파일 시스템 라이브러리에서 파일이나 디렉토리를 다루는 모든 함수는 path 객체를 매개변수로 받는다. 따라서 보통 다음의 순서로 작업한다.

  1. 원하는 경로에 있는 파일/디렉토리의 path 정의
  2. 해당 path로 파일/디렉토리 정보 수집

한 가지 중요한 점은 path 객체만으로는 실제 해당 경로에 파일이 존재하는지 알 수 없다. path 클래스는 그냥 경로를 나타낼 뿐 실제 파일을 지칭하지는 않기 때문이다.
만약, 해당 경로에 파일이 실제로 존재하는지 확인하려면 exists 함수를 사용해야 한다.

bool exists(const std::filesystem::path& p)

파일 시스템 활용

파일 시스템 라이브러리는 원래 boostfilesystemC++17 표준에 병합한 것이다. 아직은 표준보다 boost가 더 기능이 많고 광범위하게 사용되므로 std::filesystem으로 사용하는 것은 boost::filesystem으로 대체할 수 있다.

boost를 사용할 때는 헤더 파일만 포함해도 되는 것이 많지만, boost::filesystem은 사용하는 시스템에 맞게 컴파일된 라이브러리가 필요하다. std::filesystem도 언급한 바와 같이 C++17 이상의 컴파일러에서만 사용할 수 있다.

다음 예제 코드는 파일 시스템 라이브러리를 사용하여 파일과 디렉토리를 생성, 쓰기, 읽기, 삭제까지 다르는 예제이다.

#include <iostream>
#include <string>
#include <filesystem> // 파일 시스템 헤더 파일
#include <fstream> // 파일 입출력 헤더 파일


using namespace std;

namespace fs = std::filesystem;


int main()
{
    // 디렉토리 생성
    fs::create_directories("MyDirectory");

    // 파일 생성 및 쓰기
    ofstream outFile("MyDirectory/MyFile.txt");
    outFile << "Hello, World!" << endl;
    outFile.close();

    // 디렉토리 내의 파일 확인
    cout << "Files in MyDirectory: " << endl;
    for (const fs::directory_entry& entry : fs::directory_iterator("MyDirectory")) {
        if (entry.is_regular_file()) {
            cout << entry.path().filename() << endl;
        }
    }

    // 파일 읽기
    ifstream inFile("MyDirectory/MyFile.txt");
    string line;
    while (getline(inFile, line)) {
        cout << line << endl;
    }
    inFile.close();

    fs::remove_all("MyDirectory");

    return 0;
}

실행 결과

Files in MyDirectory:
"MyFile.txt"
Hello, World!

디렉토리 생성

create_directories 함수는 지정된 경로에 디렉토리를 생성한다. 필요하면 중간 단계의 디렉토리도 함께 생성한다. 예를 들어 path/to/MyDirectory에 해당하는 디렉토리를 생성하려고 할 때 pathto 디렉토리가 없으면 새로 만들고, 이미 있어도 오류가 발생하지 않으므로 안전하게 사용할 수 있다.

fs::create_directories("path/to/MyDirectory");

Linux 명령어 중에 mkdir -p와 같다.

파일 생성 및 쓰기

ostream을 사용해서 MyDirectory에 MyFile.txt라는 파일을 생성하고, 이 파일을 열어서 Hello, World! 라는 문자열을 쓰고 닫는다.

ofstream outFile("MyDirectory/MyFile.txt");
outFile << "Hello, World!" << endl;
outFile.close();

ofstream (파일 출력 스트림)을 사용하여 파일에 내용을 쓰고 난 후 close() 함수를 호출하는 이유는, 파일을 제대로 닫기 위해서이다.
파일을 열였을 때 메모리를 사용하게 되는데, 이 메모리를 정리하고 파일을 닫지 않으면 다른 부분에서 이 파일에 접근하지 못하거나 문제가 발생할 수 있다. 따라서 파일을 열었으면 작업 후에 항상 닫아줘야 한다.

C++에서는 ofstream의 소멸자가 파일을 자동으로 닫아 주긴 하지만, 명시적으로 close 함수를 호출하는 것이 코드를 해석하기 쉽게 하고, 파일을 여는 곳과 닫는 곳이 명확하게 구분되어 유지/보수 할 때도 좋다.

디렉토리 탐색

다음의 코드에서는 해당 디렉토리의 모든 파일 이름을 출력한다.

cout << "Files in MyDirectory: " << endl;
for (const fs:directory_entry& entry : fs::directory_iterator("MyDirectory")) {
	if (entry.is_regular_file()) {
    	cout << entry.path().filename() << endl;
    }
}

MyDirecotry 경로에 대한 디렉토리 반복자(directory_iterator)를 생성한다. 이 반복자는 해당 디렉토리의 파일과 디렉토리 정보를 제공한다. 그리고 범위 기반 for문으로 반복자가 가리키는 모든 원소를 대상으로 반복한다. 여기서 entry는 각 파일이나 디렉토리에 대한 참조를 나타낸다.

if 문에서는 현재 반복자가 가리키는 항목이 디렉토리가 아닌 파일인지 확인하고 현재 파일의 경로에서 파일 이름만 출력한다. entry.path 함수는 파일의 전체 경로를 나타내며, filename 함수는 파일 이름만 추출한다.

파일 읽기

다음 코드는 ifstream을 사용하여 파일을 열고 각 줄을 읽어 그 내용을 화면에 출력한다. 그리고 파일을 닫는다.

ifstream inFile("MyDirectory/MyFile.txt");
string line;
while (getline(inFile, line)) {
	cout << line << endl;
}
inFile.close();

MyDirectory/MyFile.txt 파일을 읽기 모드로 연다. 이때 ifstream은 파일을 읽기 위한 입력 스트림을 제공한다. 그리고 getline 함수로 파일에서 한 줄씩 읽어 line에 저장한 후 화면에 출력하는 동작을 파일의 끝에 도달할 때까지 반복한다.
getline 함수는 첫 번째 인자로 전달한 스트림에서 한 줄을 읽어 두 번째로 전달한 인자에 저장하는데, 스트림에서 더 이상 읽을 내용이 없으면 false를 반환한다.

마지막으로 close() 함수를 호출하여 파일을 닫는다. 파일을 닫음으로써 파일 자원을 해제하고 접근을 종료한다.

파일 삭제

다음 코드는 remove_all 함수를 사용하여 디렉토리를 삭제한다. 즉, MyDirectory와 그 안에 있는 파일, 하위 디렉토리까지 모두 삭제한다.

fs::remove_all("MyDirectory");

remove_all 함수는 지정한 디렉토리와 그 안에 포함된 모든 요소를 삭제한다. 만약 디렉토리 안에 하위 디렉토리나 파일이 있더라도 재귀하는 방법으로 모두 삭제한다.

remove_all 함수는 디렉토리와 파일을 완전히 삭제하므로 조심해서 사용해야 한다.

0개의 댓글