[ 현재 본 강좌는 미완성입니다.
계속 만들어지는중입니다!! ]
<filesystem> 라이브러리는 경로, 일반 파일 및 디렉토리와 같은 파일 시스템 및 해당 구성 요소에 대한 작업을 수행하기 위한 기능을 제공합니다.
<filesystem> 라이브러리는 원래 boost.filesystem으로 개발되었으며, 최종적으로 C++17에서 ISO C++에 병합되었습니다.
2021-07-09기준 Windows10 Home 사용했으며, 컴파일러는 MSVC 사용했습니다.
c++버전은 20입니다.
<filesystem>에서는 기본적으로 일반 std레퍼런스와의 혼동을 막기위해서 추가로 "std::filesystem"이라는 이름공간을 만들어놨습니다.
우선 다음과 같이 생략가능 합니다.
#include <filesystem>
using namespace std::filesystem;
int main() {
}
또한, 다음과 같이 이름공간 변경이 가능합니다.
저는 이번글에서는 아래와 같이 짧게 사용할겁니다.
#include <filesystem>
namespace fs = std::filesystem;
int main() {
}
파일의 절대경로를 알아내기위한 방법으로 <filesystem>에서는 위 함수를 제공합니다.
사용법은 간단합니다.
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main() {
fs::path p = "./";
std::cerr << fs::absolute(p);
}
저는 분명 상대경로를 입력했지만, 위 함수를 거쳐오면서 절대경로로 바뀌었습니다!
std::filesystem::path는 <filesystem>에서 기본적으로 파일의 경로를 다루기위해 만들어진 클래스입니다.
우선 저는 exe파일이 있는 위치에다가 "basic"이라는 폴더를 하나 만들어놨고, 그안에다가는 "test.txt"라는 텍스트파일을 만들어놨습니다.
그러면 "test.txt"라는 텍스트파일을 가리키기위해서는 어떻게 해야할까요?
#include <filesystem> //파일시스템
#include <fstream> //파일입출력
#include <iostream> //표준입출력
namespace fs = std::filesystem;
int main() {
fs::path p = "./basic/test.txt"; //상대경로입력
std::ofstream(p).put('a'); //ofstream을 이용해서 path객체가 가지고있는 경로에 해당하는 파일에 'a'쓰기
std::cerr << p; //출력
}
다음과 같이 콘솔창에는 path객체가 가지고있는 실제경로와 그 경로에 알맞게
"test.txt"라는 텍스트파일에 정상적으로 'a'가 출력된것을 보실수있습니다.
<filesystem>에서 여러개또는 재귀적으로 존재하는 디렉터리들을 모두 순회하는 방법은 뭐가있을까요?
가장 편하고 대표적인 방법은 std::filesystem::directory_iterator를 사용하는것입니다.
해당 클래스는 디렉터리를 비재귀적으로 순회하면서, 생성자의 인자로서 전달받은 std::filesystem::path 객체에 포함된 경로에따라 그 하위디렉터리에 포함되는 모든 파일과 디렉터리들을 순회합니다.
우선 기본적인 사용법입니다.
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main() {
fs::path p = fs::current_path(); //std::filesystem::current_path();는 현재 실행되고있는 메인모듈의 파일경로를 반환합니다.
fs::directory_iterator iter(p);
for (auto const& i : iter)
std::cerr << i.path() << '\n'; //해당 디렉터리및파일의 경로출력
}
다음 코드 실행시, 사진처럼 하위 디렉터리에 포함된 모든 원소들이 출력될겁니다.
우선 코드 설명부터 하겠습니다.
저가 auto를 썼지만, i의 자료형은 정확히 "std::filesystem::directory_entry"입니다.
또한, "std::filesystem::directory_iterator"는 이름에서도 짐작이 가시다시피, 반복자로서 각 디렉터리및 파일들을 가리키는 주소를 증가시키면서 순회하기 때문에 다음과 같이도 사용이 가능합니다.
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main() {
fs::path p = fs::current_path();
fs::directory_iterator iter(p);
for (auto& i = iter; i != fs::end(iter); ++i) {
std::cerr << i->path() << '\n';
}
}
하지만, 여기서 나는 "./basic/"디렉터리 안에있는 "test.txt"라는 텍스트 파일까지 출력하고 싶으면은 어떻게 하면될까요?
이럴때는 "std::filesystem::recursive_directory_iterator"클래스를 사용하시면 됩니다.
다음과 같이 사용될 수 있습니다.
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main() {
fs::path p = fs::current_path();
fs::recursive_directory_iterator iter(p);
for (auto& i = iter; i != fs::end(iter); ++i) {
std::cerr << i->path() << '\n';
}
}
그러면, 밑에 사진처럼 재귀적으로 모든 디렉터리를 호출이 됩니다.
보시다시피, "./basic/"디렉터리내에 포함된 "test.txt"파일도 정상적으로 순회한것을 보실 수 있습니다.
이 "std::filesystem::recursive_directory_iterator"클래스에 관한 원리가 궁금하신분이 있을것같아서, 직접 구현해보았습니다.
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
void recursive(fs::path const& p) {
std::error_code ec; //예외사항을 잡아내기위한 클래스
fs::directory_iterator iter(p, ec);
//만약 값이 없다면, 함수종료
//참고로, std::error_code의 value함수는 예외사항이 발생하면 0이 아닌값을 반환합니다.
if (ec.value() != 0) return;
for (auto& i = iter; i != fs::end(iter); ++i) {
//만약 폴더인경우, 해당경로로 함수 재귀적 호출
if (i->is_directory()) recursive(i->path());
std::cerr << i->path() << '\n';
}
}
int main() {
fs::path p = fs::current_path();
recursive(p);
}
아마 위 코드와 같이, 재귀적인 형태로 만들어져있을겁니다.
.
.
.
[ 현재 본 강좌는 미완성입니다.
계속 만들어지는중입니다!! ]
마지막 파일을 알 수 있는 방법이 없을까요 ?
없으면 경로를 하나하나 섬세하게 vector에 담아 놓고 vecPath.back() 이렇게 하는거 말고는 방법이 없을까요 ?