Show Lecture.Filesystem as a slide show.
CS253 Filesystem
Major filesystem classes
#include <filesystem>
introduces a lot of new symbols, quarantined to
their own namespace std::filesystem
to avoid conflicts, so put
using namespace std::filesystem;
in your code.
<filesystem> defines several classes, including:
- The path class represents a single path in a filesystem,
whether relative (
foo
, ./bar
) or absolute (/etc/group
).
- It has several methods to extract parts of the path.
All return a path, except
.native()
, which returns
a string (or a wstring on Windows?).
- A path displays with
"
quotes"
, to protect funny characters.
path p("/usr/bin/cat");
cout << "path: " << p << '\n'
<< "native: " << p.native() << '\n'
<< "parent_path: " << p.parent_path() << '\n'
<< "filename: " << p.filename() << '\n'
<< "stem: " << p.stem() << '\n'
<< "extension: " << p.extension() << '\n';
path: "/usr/bin/cat"
native: /usr/bin/cat
parent_path: "/usr/bin"
filename: "cat"
stem: "cat"
extension: ""
Many filesystem functions take an optional error_code argument.
If that argument isn’t given, then the function throws a
filesystem_error object if things go bad, which has a nice
error message.
try {
cout << "/etc/group: " << file_size("/etc/group") << endl;
cout << "doh!: " << file_size("doh!") << endl;
}
catch (const filesystem_error &oops) {
cerr << oops.what() << '\n';
}
/etc/group: 1535
doh!: filesystem error: cannot get file size: No such file or directory [doh!]
If the optional error_code argument is given, nothing is thrown:
for (auto fn : {"/bin/cc", "/bin/cat"}) {
error_code ec;
string s = read_symlink(fn, ec);
if (ec)
cerr << fn << ": " << ec.message() << '\n';
else
cout << fn << " -> " << s << '\n';
}
/bin/cc -> gcc
/bin/cat: Invalid argument
An error_code in a boolean context is true iff
the last operation was bad.
Iteration
path home(getenv("HOME"));
path pub(home / "pub");
for (directory_entry d : directory_iterator(pub))
if (d.path().extension() == ".txt")
cout << d.path().filename() << '\n';
"hamlet.txt"
"common-words.txt"
"abcdefg.txt"
Recursive Iteration
path home(getenv("HOME"));
path pub(home / "pub");
for (auto &d : recursive_directory_iterator(pub))
if (d.path().stem() == "README")
cout << d << '\n';
"/s/bach/a/class/cs253/pub/Example/README.txt"
A path is printed with "quotes" so we can read & write paths containing whitespace.
$ ls -lhog /etc/group
-rw-r--r-- 1 1.5K Nov 30 2022 /etc/group
status() returns a file_status object:
file_status s = status("/etc/group");
file_type t = s.type();
perms p = s.permissions();
if ((p & perms::owner_read) != perms::none)
cout << "readable by owner\n";
if (t == file_type::regular)
cout << "a regular file\n";
readable by owner
a regular file
const path home(getenv("HOME"));
auto fname(home / "pub/dwarfs");
if (ifstream in(fname); in.good())
for (string s; getline(in, s); )
cout << s << '\n';
else
cerr << "can’t open " << fname << " for reading.\n";
Bashful
Doc
Dopey
Grumpy
Happy
Sleepy
Sneezy
- The fstream ctor takes a const char *, a string, or now, a path.
- Note the unnecessary
if (
declaration;
condition)
.
- Purists would insist on
home / "pub" / "dwarfs"
.
- True, but both
\
and /
work on Windows.
- The
HOME
environment variable already broke this on Windows,
which would like USERPROFILE
or HOMEDRIVE
+HOMEPATH
,
I think.
And …