C++17 std::filesystem 완전 가이드
C++17의 std::filesystem(헤더 <filesystem>, 네임스페이스 std::fs)은 파일시스템 경로 조작, 파일/디렉터리 생성·삭제·복사, 파일 정보 조회를 표준화된 API로 제공합니다. Windows, Linux, macOS를 동일한 코드로 처리할 수 있습니다.
1. std::filesystem::path
섹션 제목: “1. std::filesystem::path”1.1 경로 생성과 조작
섹션 제목: “1.1 경로 생성과 조작”#include <filesystem>namespace fs = std::filesystem;
fs::path p1 = "C:/Users/user/Documents/file.txt";fs::path p2 = fs::current_path(); // 현재 작업 디렉터리
// 경로 조합fs::path dir = "src";fs::path file = "main.cpp";fs::path full = dir / file; // "src/main.cpp"
// 경로 구성 요소fs::path p = "/home/user/docs/file.txt";std::cout << p.root_path() << "\n"; // "/"std::cout << p.parent_path() << "\n"; // "/home/user/docs"std::cout << p.filename() << "\n"; // "file.txt"std::cout << p.stem() << "\n"; // "file"std::cout << p.extension() << "\n"; // ".txt"std::cout << p.relative_path() << "\n"; // "home/user/docs/file.txt"1.2 경로 변환
섹션 제목: “1.2 경로 변환”fs::path p = "src/main.cpp";
// 절대 경로 변환fs::path abs = fs::absolute(p); // /cwd/src/main.cpp
// 정규화 (., .., 심볼릭 링크 해소)fs::path canonical = fs::canonical(p); // 실제 존재해야 함
// 상대 경로 계산fs::path base = "/home/user";fs::path target = "/home/user/docs/file.txt";fs::path rel = fs::relative(target, base); // "docs/file.txt"2. 파일/디렉터리 작업
섹션 제목: “2. 파일/디렉터리 작업”2.1 생성
섹션 제목: “2.1 생성”// 디렉터리 생성fs::create_directory("output"); // 단일 디렉터리fs::create_directories("a/b/c/d"); // 중간 경로 포함
// 파일 생성 (표준 파일 스트림 사용)std::ofstream file("output/data.txt");file << "Hello, filesystem!\n";2.2 복사
섹션 제목: “2.2 복사”// 파일 복사fs::copy_file("src.txt", "dst.txt");fs::copy_file("src.txt", "dst.txt", fs::copy_options::overwrite_existing); // 덮어쓰기
// 디렉터리 재귀 복사fs::copy("src_dir", "dst_dir", fs::copy_options::recursive);2.3 이동/이름 변경
섹션 제목: “2.3 이동/이름 변경”fs::rename("old_name.txt", "new_name.txt");fs::rename("dir/file.txt", "other_dir/file.txt"); // 이동2.4 삭제
섹션 제목: “2.4 삭제”fs::remove("file.txt"); // 단일 파일fs::remove_all("directory"); // 디렉터리 재귀 삭제// 반환값: 삭제된 항목 수std::uintmax_t count = fs::remove_all("temp");3. 파일 정보 조회
섹션 제목: “3. 파일 정보 조회”fs::path p = "example.txt";
// 존재 여부bool exists = fs::exists(p);bool isFile = fs::is_regular_file(p);bool isDir = fs::is_directory(p);bool isSymlink = fs::is_symlink(p);
// 파일 크기std::uintmax_t size = fs::file_size(p);
// 최종 수정 시간auto time = fs::last_write_time(p);auto sctp = std::chrono::file_clock::to_sys(time); // C++20
// 권한fs::perms perms = fs::status(p).permissions();bool readable = (perms & fs::perms::owner_read) != fs::perms::none;4. 디렉터리 반복자
섹션 제목: “4. 디렉터리 반복자”4.1 단순 반복
섹션 제목: “4.1 단순 반복”fs::path dir = "src";
for (const fs::directory_entry& entry : fs::directory_iterator(dir)) { std::cout << entry.path() << "\n"; std::cout << " 타입: " << (entry.is_regular_file() ? "파일" : "디렉터리") << "\n"; if (entry.is_regular_file()) { std::cout << " 크기: " << entry.file_size() << " bytes\n"; }}4.2 재귀 반복
섹션 제목: “4.2 재귀 반복”for (const auto& entry : fs::recursive_directory_iterator("project")) { if (entry.is_regular_file() && entry.path().extension() == ".cpp") { std::cout << entry.path() << "\n"; }}4.3 정렬된 반복
섹션 제목: “4.3 정렬된 반복”// directory_iterator 자체는 정렬 미보장std::vector<fs::path> entries;for (const auto& e : fs::directory_iterator("src")) { entries.push_back(e.path());}std::sort(entries.begin(), entries.end());
for (const auto& p : entries) { std::cout << p.filename() << "\n";}5. 에러 처리
섹션 제목: “5. 에러 처리”5.1 예외 방식
섹션 제목: “5.1 예외 방식”try { fs::copy_file("nonexistent.txt", "dst.txt");} catch (const fs::filesystem_error& e) { std::cerr << "오류: " << e.what() << "\n"; std::cerr << "경로1: " << e.path1() << "\n"; std::cerr << "경로2: " << e.path2() << "\n";}5.2 에러 코드 방식 (예외 없음)
섹션 제목: “5.2 에러 코드 방식 (예외 없음)”std::error_code ec;fs::copy_file("nonexistent.txt", "dst.txt", ec);if (ec) { std::cerr << "오류: " << ec.message() << "\n"; // 예외 발생 없음}
// 거의 모든 함수에 ec 오버로드 존재if (!fs::exists("path", ec) && !ec) { // 존재하지 않음 (에러 아님)}6. 실전 활용 패턴
섹션 제목: “6. 실전 활용 패턴”6.1 파일 목록 수집
섹션 제목: “6.1 파일 목록 수집”std::vector<fs::path> findFiles(const fs::path& root, const std::string& ext) { std::vector<fs::path> results; std::error_code ec;
for (const auto& entry : fs::recursive_directory_iterator(root, ec)) { if (ec) break; if (entry.is_regular_file() && entry.path().extension() == ext) { results.push_back(entry.path()); } }
std::sort(results.begin(), results.end()); return results;}
auto cppFiles = findFiles("project/src", ".cpp");6.2 안전한 파일 교체
섹션 제목: “6.2 안전한 파일 교체”void safeReplace(const fs::path& target, const fs::path& newFile) { fs::path backup = target; backup += ".bak";
std::error_code ec; // 1. 기존 파일 백업 if (fs::exists(target)) { fs::rename(target, backup, ec); if (ec) throw std::runtime_error("백업 실패: " + ec.message()); }
// 2. 새 파일 이동 fs::rename(newFile, target, ec); if (ec) { // 복원 if (fs::exists(backup)) fs::rename(backup, target); throw std::runtime_error("교체 실패: " + ec.message()); }
// 3. 백업 삭제 fs::remove(backup, ec);}6.3 임시 디렉터리 RAII
섹션 제목: “6.3 임시 디렉터리 RAII”class TempDir { fs::path path_;public: TempDir() : path_(fs::temp_directory_path() / "myapp_XXXXXX") { fs::create_directories(path_); } ~TempDir() { std::error_code ec; fs::remove_all(path_, ec); } const fs::path& path() const { return path_; }};
{ TempDir tmp; auto file = tmp.path() / "work.dat"; // 작업 수행} // 소멸자에서 자동 삭제7. 크로스 플랫폼 주의사항
섹션 제목: “7. 크로스 플랫폼 주의사항”- Windows는
\, Linux/macOS는/—path::operator/로 구성하면 플랫폼 무관 - 대소문자 구분: Windows는 구분 없음, Linux는 구분 있음
path::string()은 플랫폼 기본 문자 형식,path::generic_string()은 항상/구분자
| 작업 | 함수 |
|---|---|
| 경로 조합 | path / other |
| 존재 확인 | fs::exists() |
| 디렉터리 생성 | fs::create_directories() |
| 파일 복사 | fs::copy_file() |
| 재귀 삭제 | fs::remove_all() |
| 반복 순회 | recursive_directory_iterator |
| 에러 처리 | 예외 또는 std::error_code |