Skip to content
meta-legend edited this page Jul 17, 2026 · 2 revisions

File

ML::File is a set of cross-platform file and folder utilities built on std::filesystem. Errors are reported to std::cerr; query methods return sensible defaults on failure. Every method takes plain std::string paths.

Create and write

void createFolder(std::string name);                    // single level
void createFolders(std::string path);                   // recursive (mkdir -p)
void createFile(std::string name, std::string content); // create or overwrite
void appendFile(std::string name, std::string content); // append to the end
void writeBytes(std::string name, const std::vector<unsigned char>& data); // binary

Atomic writes

void writeFileAtomic(std::string name, std::string content);
void writeBytesAtomic(std::string name, const std::vector<unsigned char>& data);

Both methods stage the data in a temporary file next to the target, then rename over it. If the process crashes partway through, the existing target file is untouched. The temp is created in the same directory as the target so the rename stays a real atomic rename instead of a cross-filesystem copy. Reach for these when a partially written file would corrupt something the user or another process reads later, like a save file or a config file.

Read

std::string readFile(std::string name);                 // text, line-based
std::string readAll(std::string name);                  // exact bytes (binary-safe)
std::vector<unsigned char> readBytes(std::string name); // raw bytes

readAll keeps the file's exact bytes with no whitespace mangling, so it is the right choice for JSON, binary, or anything where exact content matters.

Line-based text

std::vector<std::string> readLines(std::string name);
void writeLines(std::string name, const std::vector<std::string>& lines);

readLines splits on \n and drops the empty string that a trailing newline would produce, so a file that ends with \n does not add a phantom empty line at the end. A stray \r from CRLF line endings is stripped from each line, so a file authored on Windows reads back the same as one authored on Linux.

writeLines joins with \n and adds a final newline so the file is well-formed for POSIX tools. Round-tripping a file that contains no \r or \n inside its lines is lossless.

Delete, copy, move

void deleteFile(std::string name);
void deleteFolder(std::string name);             // recursive
void copyFile(std::string src, std::string dst); // overwrites dst
void moveFile(std::string src, std::string dst); // rename or move

Listing

std::vector<std::string> listFiles(std::string folder);          // names, one level deep
std::vector<std::string> listFilesRecursive(std::string folder); // all files, recursive

listFiles returns the names directly inside a folder. listFilesRecursive walks the whole tree and returns every file as a path relative to the folder, using forward slashes (for example images/birds/downflap.png). It lists files only, not directories, and returns an empty list if the folder is missing.

Both methods have overloads that filter by a shell-style glob pattern:

std::vector<std::string> listFiles(std::string folder, std::string pattern);
std::vector<std::string> listFilesRecursive(std::string folder, std::string pattern);

The pattern matches file basenames, not full paths. * matches any run of characters, ? matches exactly one, and [abc] matches any character in the set (use [!abc] to negate). Everything else is literal, including ., so *.png matches every PNG in the folder and file.name.txt matches only that exact filename.

Queries and metadata

bool exists(std::string name);
bool isFile(std::string name);
bool isDirectory(std::string name);
int  fileCharacterCount(std::string name);
long fileSize(std::string name);          // bytes, -1 if missing
long long lastModified(std::string name); // unix seconds, -1 if missing

Temp file and directory

std::string makeTempFile(std::string prefix = "fileml");
std::string makeTempDir(std::string prefix = "fileml");

Both create a fresh entry under the system temp directory (whatever std::filesystem::temp_directory_path() returns on the platform) and hand back the full path. The prefix is prepended to a random suffix so the name is easy to spot while staying unique. The caller owns cleanup; nothing is auto-deleted.

Example

File f;

f.createFolders("data/cache");
f.createFile("data/note.txt", "hello");
f.appendFile("data/note.txt", " world");

std::cout << f.readAll("data/note.txt");   // "hello world"
std::cout << f.fileSize("data/note.txt");  // 11

for (const auto& name : f.listFiles("data"))
    std::cout << name << "\n";

// every file under a tree, as relative paths
for (const auto& path : f.listFilesRecursive("data"))
    std::cout << path << "\n";

// find just the PNGs, at any depth
for (const auto& png : f.listFilesRecursive("data", "*.png"))
    std::cout << png << "\n";

// binary read and write
auto bytes = f.readBytes("image.png");
f.writeBytes("copy.png", bytes);

// atomic write for something worth protecting
f.writeFileAtomic("save.json", R"({"score":1234})");

File ML

Related: Network ML

Clone this wiki locally