libzipでファイル一覧を取得すると、元のディレクトリ構造が取得できるが、ツリーになっていない。これをツリーに変換する。
#include <iostream> #include <vector> #include <string> #include <sstream> #include <filesystem> // SetConsoleOutputCP のために必要 #include <windows.h> // libzip #include <zip.h> #pragma comment(lib,"zip.lib")
enum class FileType { Ignore, Directory, File };
struct DirTree { std::string name; FileType type; std::vector<DirTree> children; DirTree(const std::string& name, const FileType type) : name(name), type(type){} };
// 既に登録済みかどうかをチェックし、既にあるノードならイテレータを返す std::vector<DirTree>::iterator getNodeIfAlreadyRegistered(DirTree& root, const std::string& name) { return std::find_if( root.children.begin(), root.children.end(), [&](const DirTree& child) { return child.name == name; } ); }
void AddPath(DirTree& root, const std::string& path) { namespace fs = std::filesystem; std::istringstream ss(path); std::string segment; DirTree* current = &root; // 次の'/'までのパスを取得 while (std::getline(ss, segment, '/')) { // 過去にツリーに登録されたノードかどうかをチェック auto currentnode = getNodeIfAlreadyRegistered(*current, segment); // すでに登録済みなら次回以降のノードはそのノードより下に追加 if (currentnode != current->children.end()) { current = &(*currentnode); } else { // 最後の要素以外はディレクトリであることは自明なので、ディレクトリとして登録 current->children.push_back({ segment, FileType::Directory }); current = ¤t->children.back(); } } // 最後の要素はファイルの可能性があるので、pathに'/'があるかどうかで判別 bool isDir = path.back() == '/'; current->type = isDir ? FileType::Directory : FileType::File; }
// ファイルリストをツリー構造に変換 DirTree BuildDirTree(const std::vector<std::string>& files) { DirTree root( "root", FileType::Ignore); for (const auto& file : files) { AddPath(root, file); } return root; }
// ファイルツリーを表示 void printTree(const DirTree& tree, int depth = 0) { auto to_string = [](const FileType type) { switch (type) { case FileType::Ignore: return "*"; case FileType::Directory: return "[DIR]"; case FileType::File: return "[FILE]"; } return "Unknown"; }; std::string indent(depth * 2, ' '); std::cout << indent << to_string(tree.type) << tree.name << "\n"; for (const auto& child : tree.children) { printTree(child, depth + 1); } }
std::vector<std::string> FilesInZip(const char* pathname) { std::vector<std::string> files; // エラーコードを格納する変数 int error = 0; // zipファイルを読み取り専用でオープン zip_t* zipArchive = zip_open(pathname, ZIP_RDONLY, &error); if (zipArchive == nullptr) { return std::vector<std::string>(); } // zipファイル内のエントリ数を取得 zip_int64_t numEntries = zip_get_num_entries(zipArchive, 0); // 各エントリの名前を取得して表示 for (zip_uint64_t i = 0; i < static_cast<zip_uint64_t>(numEntries); ++i) { const char* entryName = zip_get_name(zipArchive, i, 0); files.emplace_back(entryName); // zipファイル内のファイル名を格納 } // zipファイルをクローズ zip_close(zipArchive); return files; }
int main() { std::vector<std::string> files = FilesInZip((const char*)u8R"(C:\test\ConsoleApplication31.zip)"); // windowsのコンソールでUTF-8を表示するために必要 SetConsoleOutputCP(CP_UTF8); // ファイルのリストをツリー構造に変換 DirTree root = BuildDirTree(files); printTree(root); for(auto& f : files) { std::cout << f << std::endl; } }
まずfilesをそのまま表示すると以下のようになる。
これをツリーにして、printTreeで出力すると以下になる。
*root [FILE]ConsoleApplication31.sln [DIR].vs [DIR]ConsoleApplication31 [DIR]copilot-chat [DIR]ff91ad84 [DIR]sessions [DIR]FileContentIndex [FILE]b5c23407-6b8a-446b-82cf-97b7abb15edd.vsidx [DIR]v17 [FILE].suo [FILE]Browse.VC.db [FILE]DocumentLayout.json