В этом руководстве разрабатывается небольшая программа командной строки для перечисления информации о файлах и каталогах - по сути, это упрощенная версия команд POSIX<ls>или Windows<dir>. Мы начнем с самой простой версии и перейдем к более сложной функциональности. По пути мы рассмотрим темы, которые вам нужно знать, чтобы понять Boost.Filesystem.
Исходный код для каждой из учебных программ доступен, и вам рекомендуется компилировать, тестировать и экспериментировать с ним. Чтобы сохранить пространство, мы не всегда показываем здесь код, но предоставленный источник готов к сборке.
Установите дистрибутив Boost, если вы еще этого не сделали. См.Начало работы.
Это руководство предполагает, что вы собираетесь собирать и тестировать примеры с использованием предоставленных сценариев. Очень рекомендуется.
Если вы планируете собирать и тестировать примеры, но не используете сценарии, убедитесь, что ваша настройка сборки знает, где найти или построить библиотечные двоичные файлы Boost.
Запустите интерпретатор командной строки и введите следующие команды:
Ubuntu Linux
<
$ cdboost-root/libs/filesystem/example/test
$ ./setup.sh
Copying example programs...
$ ./build.sh
Compiling example programs...
$ ./tut1
Usage: tut1 path
>
Microsoft Windows
<
>cdboost-root\libs\filesystem\example\test
>setup
Copying example programs...
>build
Compiling example programs...
>tut1
Usage: tut1 path
>
Если<tut1>команда выводит "<Usage: tut1 path>", все хорошо. Набор учебных примеров программ был скопирован<setup>до<boost-root></libs/filesystem/example/test>и затем построен. Вам рекомендуется модифицировать и экспериментировать с ними по мере продвижения учебника. Призывайте<build>сценарий снова, чтобы восстановить, или призывайте<b2>напрямую.
Если что-то не сработало, вот несколько советов по устранению неполадок:
Если исполняемый файл программыb2не найден, проверьте переменную окружающей среды или посмотритеНачало работы.
Посмотрите наb2.log, чтобы попытаться обнаружить признаки проблемы.
Функция Boost.Filesystem<file_size>возвращает<uintmax_t>, содержащую размер файла, названного аргументом. Декларация выглядит так:
uintmax_t file_size(const path& p);
На данный момент все, что вам нужно знать, это то, что<class path>имеет конструкторы, которые принимают<const char *>и другие типы струн. (Если вы не можете дождаться, чтобы узнать больше, перейдите кклассный путьраздел учебника.)
Пожалуйста, потратьте минуту, чтобы опробовать<tut1>в вашей системе, используя файл, который, как известно, существует, например<tut1.cpp>. Вот как выглядят результаты на двух разных операционных системах:
Пока что так хорошо. Размеры Linux и Windows отличаются, потому что в тестах Linux использовались окончания строк<"\n">, а в тестах Windows использовались окончания строк<"\r\n">. Размеры отчетов могут отличаться от приведенных выше, если были внесены изменения<tut1.cpp>.
Теперь попробуйте еще раз, но дайте путь, которого не существует:
Ubuntu Linux
$ ./tut1 foo
terminate called after throwing an instance of 'boost::filesystem::filesystem_error'
what(): boost::filesystem::file_size: No such file or directory: "foo"
Aborted (core dumped)
Microsoft Windows
>tut1 foo
Исключение бросается; точная форма ответа зависит от опций системы Windows.
Что происходит? В текущем каталоге нет файла с именем<foo>, поэтому по умолчанию выбрасывается исключение. См.Отчет об ошибках, чтобы узнать об отчетности об ошибках с помощью кодов ошибок, а не исключений.
Попробуйте вот это:
Ubuntu Linux
<
$ ./tut1 .
terminate called after throwing an instance of 'boost::filesystem::filesystem_error'
what(): boost::filesystem::file_size: Operation not permitted: "."
Aborted (core dumped)
>
Microsoft Windows
>tut1 .
Исключение бросается; точная форма ответа зависит от опций системы Windows.
Текущий каталог существует, но<file_size()>работает с обычными файлами, а не каталогами, поэтому снова бросается исключение.
Повышаю. Файловая система включает в себя функции запроса состояния, такие как<exists>,<is_directory>и<is_regular_file>. Они возвращаются<bool>и вернутся<true>, если будет выполнено условие, описанное их именем. В противном случае они возвращаются<false>, в том числе, когда какой-либо элемент аргумента пути не может быть найден.
tut2.cppиспользует несколько функций запроса состояния, чтобы справиться с несуществующими файлами и с различными типами файлов:
<
#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut2 path\n";
return 1;
}
path p(argv[1]); // avoid repeated path construction below
if (exists(p)) // does path p actually exist?
{
if (is_regular_file(p)) // is path p a regular file?
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p)) // is path p a directory?
cout << p << " is a directory\n";
else
cout << p << " exists, but is not a regular file or directory\n";
}
else
cout << p << " does not exist\n";
return 0;
}
>#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut2 path\n";
return 1;
}
path p(argv[1]); // avoid repeated path construction below
if (exists(p)) // does path p actually exist?
{
if (is_regular_file(p)) // is path p a regular file?
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p)) // is path p a directory?
cout << p << " is a directory\n";
else
cout << p << " exists, but is not a regular file or directory\n";
}
else
cout << p << " does not exist\n";
return 0;
}
[ORIG_END] -->
Попробуйте вот это:
Ubuntu Linux
$ ./tut2 tut2.cpp
"tut2.cpp" size is 997
$ ./tut2 foo
"foo" does not exist
$ ./tut2 .
"." is a directory
Microsoft Windows
>tut2 tut2.cpp
tut2.cpp size is 1039
>tut2 foo
"foo" does not exist
>tut2 .
"." is a directory
Несмотря на то, что tut2 работает нормально в этих тестах, выход менее чем удовлетворителен для каталога. Обычно мы хотели бы видеть список содержимого каталога. В<tut3.cpp>мы увидим, как повторять каталоги.
Но сначала давайте попробуем еще один тест:
Ubuntu Linux
$ ls /home/jane/foo
ls: cannot access /home/jane/foo: No such file or directory
$ ./tut2 /home/jane/foo
terminate called after throwing an instance of 'boost::
filesystem::filesystem_error>'
what(): boost::filesystem::status: Permission denied:
"/home/jane/foo"
Aborted
Microsoft Windows
>dir e:\
The device is not ready.
>tut2 e:\
An exception is thrown;
the exact form of the response depends on
Windows system options.
В системе Linux тест запускался с аккаунта, у которого не было разрешения на доступ</home/jane/foo>. В системе Windows<
e:>был компакт-диск, который не был готов. Конечным пользователям не нужно интерпретировать отчеты о загадочных исключениях, поэтому, когда мы перейдем к<tut3.cpp>, мы также увеличим надежность кода.
Класс Boost.Filesystem<
directory_iterator>- это именно то, что нам нужно здесь. Это соответствует общей схеме стандартной библиотеки<istream_iterator>. Построенный с пути, он повторяется над содержимым каталога. По умолчанию построенный<directory_iterator>действует как конечный итератор.
Тип значения<directory_iterator>равен<directory_entry>. Объект<
directory_entry>содержит<path>и<file_status>информацию. Объект<
directory_entry>может быть использован непосредственно, но также может быть передан<path>аргументам в вызовах функций.
Другая необходимость заключается в повышении надежности перед лицом многих видов ошибок, которые могут повлиять на работу файловой системы. Мы могли бы сделать это на уровне каждого вызова на повышение. Функция файловой системы (см.Отчетность об ошибках), но для простотыtut3.cppиспользует общий блок проб/лов.
<
#include <iostream>
#include <boost/filesystem.hpp>
using std::cout;
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut3 path\n";
return 1;
}
path p (argv[1]);
try
{
if (exists(p))
{
if (is_regular_file(p))
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p))
{
cout << p << " is a directory containing:\n";
for (directory_entry& x : directory_iterator(p))
cout << " " << x.path() << '\n';
}
else
cout << p << " exists, but is not a regular file or directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
>#include <iostream>
#include <boost/filesystem.hpp>
using std::cout;
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut3 path\n";
return 1;
}
path p (argv[1]);
try
{
if (exists(p))
{
if (is_regular_file(p))
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p))
{
cout << p << " is a directory containing:\n";
for (directory_entry& x : directory_iterator(p))
cout << " " << x.path() << '\n';
}
else
cout << p << " exists, but is not a regular file or directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
[ORIG_END] -->
Дайте<tut3>попытку, передав ей путь к каталогу в качестве аргумента командной строки. Вот забег на кассе ветки разработки Boost Git, за которым следует повторение тестовых случаев, вызвавших исключения в Linux и Windows:
>tut3 e:\
boost::filesystem::status: The device is not ready: "e:\"
Неплохо, но мы можем сделать дальнейшие улучшения:
Список было бы намного легче читать, если бы отображалось только имя файла, а не полный путь.
Список Linux не сортируется. Это потому, что порядок итерации каталогов не определен. Заказ зависит от базовой операционной системы API и особенностей файловой системы. Поэтому мы должны сами сортировать результаты.
В следующих разделах показано, как эти изменения происходят, так что читайте дальше!
Для каталоговtut4.cppстроит<
std::vector>всех записей, а затем сортирует его перед написанием<
cout>.
<
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/filesystem.hpp>
using std::cout;
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut4 path\n";
return 1;
}
path p (argv[1]);
try
{
if (exists(p))
{
if (is_regular_file(p))
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p))
{
cout << p << " is a directory containing:\n";
std::vector<std::string> v;
for (auto&& x : directory_iterator(p))
v.push_back(x.path().filename().string());
std::sort(v.begin(), v.end());
for (auto&& x : v)
cout << " " << x << '\n';
}
else
cout << p << " exists, but is not a regular file or directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
>#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/filesystem.hpp>
using std::cout;
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: tut4 path\n";
return 1;
}
path p (argv[1]);
try
{
if (exists(p))
{
if (is_regular_file(p))
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p))
{
cout << p << " is a directory containing:\n";
std::vector<std::string> v;
for (auto&& x : directory_iterator(p))
v.push_back(x.path().filename().string());
std::sort(v.begin(), v.end());
for (auto&& x : v)
cout << " " << x << '\n';
}
else
cout << p << " exists, but is not a regular file or directory\n";
}
else
cout << p << " does not exist\n";
}
catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}
return 0;
}
[ORIG_END] -->
Ключевое различие между<tut3.cpp>и<tut4.cpp>заключается в том, что происходит в цикле итерации каталогов. Мы изменились:
$ ./tut4 ~/boost/develop
"/home/beman/boost/develop" is a directory containing:
.git
.gitattributes
.gitignore
.gitmodules
.travis.yml
INSTALL
Jamroot
LICENSE_1_0.txt
bin.v2
boost
boost-build.jam
boost.css
boost.png
boostcpp.jam
boostcpp.py
bootstrap.bat
bootstrap.sh
doc
index.htm
index.html
libs
more
project-config.jam
project-config.jam.1
project-config.jam.2
rst.css
stage
status
tools
>
Это завершает основную часть этого учебника. Если вы еще не проработалиКлассовая дорожкаразделов этого учебника, копайтесь в них сейчас. Раздел«Отчет об ошибках»также может представлять интерес, хотя его можно пропустить, если вы глубоко не обеспокоены проблемами обработки ошибок.
Традиционный C-интерфейсы проходят пути в виде аргументов<const char*>. Интерфейсы C++ могут добавлять<const std::string&>перегрузок, но добавление перегрузок становится несостоятельным, если необходимо поддерживать широкие символы, контейнеры и диапазоны итераторов.
Прохождение траекторий как<const path&>аргументов намного проще, но гораздо более гибко, потому что сам класс<path>гораздо более гибок:
Классpathподдерживает несколько типов символов и кодировок, включая Unicode, для облегчения интернационализации.
Классpathподдерживает несколько типов источников, таких как итераторы для нулевых завершенных последовательностей, диапазоны итераторов, контейнеры (включаяstd::basic_string) иdirectory_entry, поэтому функциям, принимающим пути, не нужно предоставлять несколько перегрузок.
Классpathподдерживает как нативные, так и общие форматы имени пути, поэтому программы могут быть переносимы между операционными системами, но использовать нативные форматы там, где это желательно.
Классpathпредоставляет полный набор итераторов, наблюдателей, функций композиции, разложения и запроса, что делает манипуляции с именем пути легкими, удобными, надежными и портативными.
Вот как работают (1) и (2). Конструкторы классовых путей, задания и приложения имеют шаблоны членов для источников. Например, вот конструкторы, которые берут источники:
Давайте посмотрим на небольшую программу, которая показывает, насколько комфортен класс<path>с узкими и широкими символами в строках C-стиля, строках C++ и с помощью итераторов C++:
Точный внешний вид улыбающегося лица будет зависеть от шрифта, размера шрифта и других настроек окна командной строки. Вышеуказанные тесты были запущены с Ubuntu 14.04 и Windows 7, US Edition. Если вы не получили вышеуказанных результатов, взгляните на каталог<boost-root/libs/filesystem/example/test>с помощью файлового браузера GUI вашей системы, такого как Linux Nautilus, Mac OS X Finder или Windows Explorer. Они, как правило, более удобны с международными наборами символов, чем переводчики командной строки.
Класс<path>заботится о любом типе символов или преобразованиях кодирования, требуемых конкретной операционной системой. Таким образом, как показывает<
tut5>, не проблема передать широкую строку символа на повышение. Операционная функция файловой системы, даже если базовая операционная система использует узкие символы, и наоборот. То же самое относится к функциям, которые используют аргументы<const path&>.
Класс<path>также обеспечивает синтаксис пути, переносимый через операционные системы, итераторы элементов и функции наблюдателя, композиции, разложения и запроса для управления элементами пути. Следующий раздел этого учебника посвящен синтаксису пути.
Класс<path>имеет дело с двумя различными форматами имени пути - общим форматом и родным форматом. Для POSIX-подобных файловых систем эти форматы одинаковы. Но для пользователей Windows и других не-POSIX файловых систем различие важно. Даже программисты, пишущие для POSIX-подобных систем, должны понимать разницу, если они хотят, чтобы их код был переносим на не-POSIX-системы.
Общий форматявляется знакомым форматом</my_directory/my_file.txt>, используемым POSIX-подобными операционными системами, такими как варианты Unix, Linux и Mac OS X. Windows также распознает общий формат, и он является основой для знакомого формата URL-адреса в Интернете. Сепаратор каталогов — это всегда один или несколько символов слэша.
Нативный форматявляется форматом, определенным конкретной операционной системой. Для Windows слэш или бэкслэш можно использовать в качестве символа разделителя каталогов, поэтому</my_directory\my_file.txt>будет работать нормально. Конечно, если вы напишете это в строке C++ буквально, она станет<"/my_directory\\my_file.txt">.
Если в системе Windows в имени пути появляется спецификатор диска или обратная слэш, он всегда рассматривается как родной формат.
Класс<path>имеет функции наблюдателя, которые позволяют получить представление строки объекта пути либо в нативном формате, либо в общем формате. Смотритеследующий раздел.
Различие между общим форматом и нативным форматом важно при общении с нативным API в стиле C и с пользователями. Оба, как правило, ожидают пути в родном формате и могут быть спутаны общим форматом. Общий формат отлично подходит для написания портативных программ, которые работают независимо от операционной системы.
Следующий раздел посвящен наблюдателям класса<path>, составу, разложению, запросу и итерации элементов пути.
Программа<path_info.cpp>полезна для изучения того, как работают итераторы классов<path>, наблюдатели, состав, разложение и функции запросов в вашей системе. Это одна из программ, построенных сценариями<build.sh>и<build.bat>:
Мы подробно рассмотрим приведенный выше код, чтобы лучше понять, что происходит.
A common need is to compose a path from its constituent
directories. Class path uses / and /= operators to
append elements. That's a reminder
that these operations append the operating system's preferred directory
separator if needed. The preferred
directory separator is a slash on POSIX-like systems, and a backslash on
Windows-like systems.
That's what this code does before displaying the resulting
path p using the class path stream inserter:
<
path p;
for (; argc > 1; --argc, ++argv)
p /= argv[1]; // compose path p from the command line arguments
cout << "\ncomposed path:\n";
cout << " operator<<()---------: " << p << "\n";
cout << " make_preferred()-----: " << p.make_preferred() << "\n";
> path p;
for (; argc > 1; --argc, ++argv)
p /= argv[1]; // compose path p from the command line arguments
cout << "\ncomposed path:\n";
cout << " operator<<()---------: " << p << "\n";
cout << " make_preferred()-----: " << p.make_preferred() << "\n";
[ORIG_END] -->
Одна абстракция для размышлений о пути — это последовательность элементов, где элементы являются именами каталогов и файлов. Чтобы поддержать эту абстракцию, класс<path>предоставляет итераторы, подобные STL, а также функции<begin()>и<end()>.
Вот код, который произвел список элементов в приведенном выше списке вывода:
<
cout << "\nelements:\n";
for (auto element : p)
cout << " " << element << '\n';
>
Давайте посмотрим на функции наблюдателя классового пути:
Нативные наблюдатели должны использоваться при взаимодействии с операционной системой или с пользователями.
Общий формат наблюдателей должен использоваться, когда результаты должны быть портативными и однородными независимо от операционной системы.
<path>объекты всегда держат имена путей в родном формате, но в противном случае оставляют их неизменными от своего источника. Функцияpreferred()преобразуется в предпочтительную форму, если нативный формат имеет несколько форм. Таким образом, в Windows он преобразует слэши в обратные.
Это довольно очевидно, но обратите внимание на разницу в результате<is_absolute()>между Linux и Windows. Поскольку не существует корневого имени (т.е. спецификатора диска или сетевого имени), одиночный слэш (или обратный слэш) является относительным путем в Windows, но абсолютным путем в POSIX-подобных операционных системах.
Единственное существенное различие между ними заключается в том, как они сообщают об ошибках.
Первая подпись будет содержать исключения для сообщения об ошибках. Исключение<filesystem_error>будет сделано из-за оперативной ошибки.<filesystem_error>происходит от<std::runtime_error>. Он имеет функцию участника для получения<error_code>, сообщенного источником ошибки. Он также имеет функции-члены для получения пути или путей, которые вызвали ошибку.
Мотивация для второй подписи:Выбрасывание исключений из ошибок было всей историей сообщений об ошибках для самых ранних версий Boost. Файловая система, и даже исключение ошибок, работает очень хорошо для многих приложений. Но отчеты пользователей просочились в то, что некоторый код стал настолько усеян попытками поймать блоки, чтобы быть нечитаемым и неподъемным. В некоторых приложениях ошибки ввода/вывода не являются исключительными, и это пример использования второй подписи.
Функции с аргументом<system::error_code&>устанавливают этот аргумент для сообщения о состоянии операционной ошибки, и поэтому не делайте исключений при возникновении ошибок, связанных с вводом/выводом. Полное объяснение см.Сообщение об ошибкев справочной документации.
Авторское право Beman Dawes 2010, 2015
Распространяется в соответствии с Лицензией на программное обеспечение Boost, версия 1.0. См.www.boost.org/LICENSE_1_0.txt
Пересмотрено26 июля 201526 July 2015[ORIG_END] -->
Статья Filesystem Tutorial раздела может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.