Библиотека BOOST C++ Filesystem: хидер <boost/filesystem/path.hpp>

Вступление

Класс path

Концептуальная модель путей

Грамматика для платформонезависимого представления путей в виде строк

Каноническая форма

Нормализованная форма

Краткое описание хидера boost/filesystem/path.hpp

Специфичное для ОС представление путей

Примеры представления

Предупреждение для POSIX и UNIX программистов

Хорошая программистская практика: относительные пути

Функции-члены класса

Функции не-члены класса

Механизм проверки имен (name_check) по умолчанию

Опасности

Объяснение

Примеры разбора путей на элементы

 

Вступление

Функции библиотеки Boost.Filesystem работают с объектами класса path, который описан в хидере. Этот хидер также содержит объявления функций не-членов класса, выполняющих проверку ошибок.

Список операций с файлами и каталогами приведен в документации к boost/filesystem/operations.hpp.

Потоковые операции ввода/вывода описаны в документации к boost/filesystem/fstream.hpp.

Общие спецификации библиотеки Boost.Filesystem ко всем функциям (членам класса и нет), объявленным в указанном хидере.

Руководство по портабельности содержит обсуждение вопросов с именованием файлов, которые играют важную роль, если необходима платформонезависимость кода.

Класс path

Класс path позволяет представлять пути в C++ программах независимо от платформы, используя обобщенную портабельную грамматику для представления путей в виде строк. Класс path связан с лексическими и синтаксическими аспектами путей. Путь не обязан существовать в файловой системе ОС, и даже может содержать имена, которые некорректны для используемой ОС.

Объяснение: Если бы функции библиотеки Boost.Filesystem работали с объектами std::strings или с C-строками, тогда эти функции давали бы только иллюзию портабельности, так как вызовы функции сами по себе были бы портабельны, но строки, которыми они оперировали - нет.

Концептуальная модель путей

Объект класса path может быть концептуализирован как содержащий последовательность строк. Каждая строка является элементом пути. Каждый элемент представляет имя каталога, или, в случае строки, представляющей самый дальний от корня в иерархии каталогов элемент, имя каталоги или файла. Имена ".." и "." зарезервированы для представления концепций родительского-каталога и текущего-каталога.

Концептуальное представление пути не зависит он какого-либо конкретного представления пути в виде одной строки.

Не обязательно, чтобы реализация класса path на самом деле была массивом строк, но концепция содержимого как последовательности строк позволяет размышлять о путях платформонезависимо.

Чтобы программы могли портабельно работать с путями в виде строк, класс path определяет грамматику портабельного обобщенного формата путей в виде строк, и имеет конструктор и операторы соединения, получающие аргументы как строки. Так как пользовательский ввод или сторонняя библиотека функций могут работать с путями в специфичном для ОС формате, то объявлен дополнительный конструктор, которому задается формат путей для текущей ОС.

Для получения содержимого объекта класса path в виде строки портабельного формата, или строки для пути к каталогу или файлу в формате ОС, объявлены специальные функции. Дополнительные функции доступа извлекают определенные части путей.

Грамматика для платформонезависимого представления путей в виде строк

Эта грамматика представлена в виде расширенных BNF, с терминальными символами в кавычках:

path ::= [root] [relative-path]  // an empty path is valid
root ::= [root-name] [root-directory]
root-directory ::= "/"
relative-path ::= path-element { "/" path-element } ["/"]
path-element ::= name | parent-directory | directory-placeholder
name ::= char { char }
directory-placeholder ::= "."
parent-directory ::= ".." 

Грамматика элемента root-name определяется реализацией. Элемент root-name не должен задаваться при обобщенном вводе. Этот элемент может быть частью строки, возвращаемой функциями-членами класса path, и может присутствовать в аргументе src конструктора path, если действует ОС-специфичная политика проверки.

char не может быть символом дроби ('/') или '\0'.

Хотя root-name определяется реализацией, желательно, чтобы элемент root-name имел грамматику, которая отличается от грамматики других элементов, и следовал соглашениям ОС.

Опциональный конечный "/" в относительном-пути допускается для удобства записи. Он не имеет значения и просто отбрасывается.

Будет или нет строка обобщенного формата на самом деле портбельной для конкретной ОС, зависит от используемых имен - см. руководство по портабельности.

Каноническая форма

Операции, модифицирующие объекты класса path, оставляют объекты path в канонической форме.

Пустой путь представляется в канонической форме.

Не-пустой путь преобразуется в каноническую форму при первом преобразовании в концептальную_модель:

Нормализованная форма

Нормализованная форма это та же каноническая форма, за исключением того, что рекурсивно удалены добавочные элементы name, parent-directory.

Таким образом, не-пустой путь в нормальной форме либо не имеет элементов directory-placeholders, либо состоит из единственного элемента directory-placeholder. Если он имеет элементы parent-directory, они должны быть перед всеми элементами name.

Краткое описание хидера boost/filesystem/path.hpp

namespace boost
{
  namespace filesystem
  {
    class path
    {
    public:
      typedef bool (*name_check)( const std::string & name );

      // compiler generates copy constructor,
      // copy assignment, and destructor

      // constructors:
      path();
      path( const std::string & src );
      path( const char * src );
      path( const std::string & src, name_check checker );
      path( const char * src, name_check checker );

      // append operations:
      path & operator /= ( const path & rhs );
      path   operator /  ( const path & rhs ) const;

      // conversion functions:
      const std::string & string() const;
      std::string native_file_string() const;
      std::string native_directory_string() const;
      
      // modification functions:
      path &      normalize();

      // decomposition functions:
      path        root_path() const;
      std::string root_name() const;
      std::string root_directory() const;
      path        relative_path() const;
      std::string leaf() const;
      path        branch_path() const;
      
      // query functions: 
      bool empty() const;
      bool is_complete() const;
      bool has_root_path() const;
      bool has_root_name() const;
      bool has_root_directory() const;
      bool has_relative_path() const;
      bool has_leaf() const;
      bool has_branch_path() const;
      
      // iteration:
      typedef implementation-defined iterator;
      iterator begin() const;
      iterator end() const;

      // default name_check mechanism:
      static bool default_name_check_writable(); 
      static name_check default_name_check( name_check new_check );
      static name_check default_name_check();

    private:
      std::vector<std::string> m_name;  // for exposition only
    };

    path operator / ( const char * lhs, const path & rhs );
    path operator / ( const std::string & lhs, const path & rhs );

    // name_check functions
    bool portable_posix_name( const std::string & name );
    bool windows_name( const std::string & name );
    bool portable_name( const std::string & name );
    bool portable_directory_name( const std::string & name );
    bool portable_file_name( const std::string & name );
    bool no_check( const std::string & name );
    bool native( const std::string & name );
  }
}

Ради демонстрации функции-члены класса path описаны как будто класс содержит приватный элемент std::vector<std::string> m_name. Действительная реализация может быть другой.

operator / (член класса или свободная функция) может сгенерировать исключение filesystem_error, если синтаксис пути не соответствует грамматике.

Замечание: Не существует гарантий, что объект path представляет путь, который будет корректным для текущей ОС. Путь может быть некорректным для ОС по причине наличия некорректных имен (слишком длинные, недопустимые символы, и так далее), или потому что это незавершенный путь. Некорректный путь будет нормально обнаружен при первом использовании, например в операциях или fstream библиотека Boost.Filesystem.

Предупреждение о портабельности: не существует гарантий, что объект path представляет путь, который будет портабелен для другой ОС. Путь может быть непортабельным по причине наличия слишком длинных имен или недопустимых символов. Механизм проверки имен по умолчанию предназначен для детектирования не-портабельных имен, либо функция проверки может быть задана в конструкторе path. Библиотека Boost.Filesystem содержит несколько функций для проверки имен, либо пользователи могут написать свои собственные.

Специфичное для ОС представление путей

Несколько членов класса path возвращают представление m_name в формате, специфичном для ОС. Этот формат определяется реализацией. Если элемент  m_name содержит символы, которые недопустимы по правилам ОС, и есть однозначное преобразование между недопустимыми и допустимыми символами, то реализация должна выполнить преобразование символов. К примеру, если ОС не допускает символов в нижнем регистре в именах файлов и каталогов, эти символы заменяются на символы в верхнем регистре, если не возникает неоднозначности. Такая трансляция не применяется к форматам представления обобщенных путей.

Примеры представления

Главное правило - использовать string(), когда требуется обобщенное строковое представление пути, и использовать либо native_directory_string(), либо  native_file_string(), когда строковое представление форматируется для конкретной ОС.

Разница между представлениями, возвращаемыми методами string(), native_directory_string(), и native_file_string() иллюстрируется следующими примерами:

path my_path( "foo/bar/data.txt" );
std::cout
  << "; "string------------------: " << my_path.string() << '\n'
  << "native_directory_string-: " << my_path.native_directory_string() <<  << "native_file_string------: " << my_path.native_file_string() << '\n';

На POSIX системах результат будет:

string------------------: foo/bar/data.txt
native_directory_string-: foo/bar/data.txt
native_file_string------: foo/bar/data.txt

В Windows результат будет:

string------------------: foo/bar/data.txt
native_directory_string-: foo\bar\data.txt
native_file_string------: foo\bar\data.txt

В классической Mac OS результат будет:

string------------------: foo/bar/data.txt
native_directory_string-: foo:bar:data.txt
native_file_string------: foo:bar:data.txt

Для гипотетической ОС, использующей формат представления путей OpenVMS, результат был бы следующий:

string------------------: foo/bar/data.txt
native_directory_string-: [foo.bar.data.txt]
native_file_string------: [foo.bar]data.txt

Обратите внимание, что так как OpenVMS использует точку и как символ-разделитель каталогов, и как разделитель между именем файла и расширением, то native_directory_string() в данном примере возвращает бесполезный результат. Для этой ОС программист должен использовать этот путь только как путь к файлу. (В рекомендациях по платформонезависимости есть совет не использовать точки в именах каталогов).

Предупреждение для POSIX и UNIX программистов

POSIX и другие UNIX-подобные ОС имеют единый корень файловой системы, тогда как большинство других ОС имеют несколько корней. Много-корневые ОС требуют имя корня, к примеру диск, устройство, том или имя сетевого ресурса, чтобы путь был разрешен как действительный путь к файлу или каталогу. Из-за этого функции root() и root_directory() возвращают идентичные результаты в UNIX и других ОС с одним корнем, но разные результаты для много-корневых ОС. Поэтому использование неправильной функции неочевидно в UNIX-подобных системах, но приведет к непортабельному коду, который не будет работать на много-корневых системах. UNIX программистам следует быть осторожными в выборе между root() и root_directory(). В случае сомнений следует использовать root().

Аналогичное предупреждение применимо к has_root() и has_root_directory().

Хорошая программистская практика: относительные пути

Прописывать в коде программы законченные пути является плохой практикой. Такие программы склонны к неустойчивой работе, так как они начинают сбоить если дерево каталогов реорганизуется, или программа переносится на другую машину или ОС.

Наиболее надежный способ получать законченные пути - прописывать в программе относительные пути. Когда требуется законченный путь, его можно получить несколькими способами:

Функции-члены класса

конструкторы

path();

Эффекты: создает по объект класса path со значением по умолчанию.

Послеусловия: path().empty()

path( const std::string & src, name_check checker );
path( const char * src, name_check checker );
path( const std::string & src );
path( const char * src );

Для форм с одним аргументом, default_name_check() используется как механизм проверки.

Предусловие: src != 0.

Эффекты: выберите грамматику:

Разбирает аргумент src на последовательность имен, согласно грамматике, затем, для каждого имени в src, делается  m_name.push_back( name ).

Исключения: Для каждого имени в src, исключение генерируется если checker( name ) возвращает false.

Послеусловия: m_name в канонической форме. Только для формы с одним аргументом проверяется  !default_name_check_writable().

Объяснение: Конструкторы с одним аргументом не объявлены как explicit, чтобы разрешить преобразование строк в пути.

operator /=

path & operator/=( const path & rhs );

Эффекты: Если любое из следующих условий удовлетворено, тогда делается m_name.push_back("/").

 Затем добавляет rhs.m_name к m_name.

(Сноска: таким образом для Windows, (path("//share") /= "foo").string() равно "//share/foo")

Возвращает: *this

Послеусловия: m_name в канонической форме.

Объяснение: Не будет ошибкой, если rhs включает root-directory, потому что m_name может быть относительным или пустым путем, и таким образом можно для rhs задавать root-directory.  К примеру, в Windows следующий код должен выполниться успешно:

path p( "c:", native );ative );
p /= "/foo";
assert( p.string() == "c:/foo" );

operator /

const path operator/ ( const path & rhs ) const;

Возвращает: path( *this ) /= rhs

Объяснение: Operator / создан для того, чтобы вместе с operator /= давать возможность удобно создавать пути с произвольным числом элементов. К примеру, initial_path() / "src" / test_name. Operator+ и operator+= рассматривались как альтернативы, но их слишком просто перепутать с такими же операторами в std::string. Первоначально какое-то время использовались  operator<< и operator=<<, пока однажды Dave Abrahams не указал, ччто / и /= соответствуют обобщенному синтаксису путей.

Замечание: см. также функции не-члены класса operator/.

normalize

path & normalize();

Послеусловия: m_name в нормализованной форме.

Возвращает: *this

string

const std::string & string() const;

Возвращает: содержимое m_name, отформатированное согласно правилам обобщенной грамматике строкового представления путей.

Замечание: возвращаемая строка должна быть однозначна в соответствии с грамматикой. Это означает, что для ОС с именами корней (root-names), неотличимыми от имен относительных путей, имен с "/", или ОС, допускающих "." или ".." как имена каталогов и файлов, спецсимволы (escapes) или другие механизмы должны быть введены в грамматику для предотвращения я неоднозначностей. Это пока не сделано, так как ни одна из реализаций еще не сталкивалась с ОС, имеющей такие проблемы.

См. далее пример представления.

native_file_string

std::string native_file_string() const;

Возвращает: содержимое m_name, форматированное для специфичного для ОС представления пути.

См. далее - пример представления.

Объяснение названия: название намеренно уродливо, чтобы предупредить пользователя, что данная функция возвращает непортабельные результаты.

native_directory_string

const std::string native_directory_string() const;

Возвращает: содержимое m_name, форматированное для специфичного для ОС представления пути.

См. далее - пример представления.

Объяснение названия: название намеренно уродливо, чтобы предупредить пользователя, что данная функция возвращает непортабельные результаты.

root_path

path root_path() const;

Возвращает: root_name() / root_directory()

Портабельно возращает копию полный путь к корню для пути, если таковой есть. См. примеры разбора путей.

std::string root_name() const;

Возвращает: Если !m_name.empty() && m_name[0] является именем-корня, то возвращает m_name[0], иначе возвращает пустую строку.

Портабельно возвращает копию корневого-имени для пути, если таковое есть. См. примеры разбора путей.

root_directory

std::string root_directory() const;

Возвращает: Если путь содержит корневой-каталог, то возвращает string("/"), иначе string().

Портабельно возвращает копию корневого-каталога для пути, если таковой есть. Возможны только варианты "/" или "". См. примеры разбора путей.

relative_pathive_path

path relative_path() const;

Возвращает: новый путь, содержащий только относительный путь в исходном пути.

Портабельно возвращает часть исходного пути - относительный путь, если такой есть. См. примеры разбора путей.

leaf

std::string leaf() const;

Возвращает: empty() ? std::string() : m_name.back()

Обычно используется для получения имени файла или каталога без пути из объекта, получаемого от directory_iterator. См. примеры разбора путей.

branch_path

path branch_path() const;

Возвращает: m_name.size() <= 1 ? path("") : x, where x is a path constructed from all the elements of m_name except the last.

Обычно используется для получения родительского пути для переданного пользователем пути. См. примеры разбора путей.

empty

bool empty() const;

Возвращает: string().empty().

Функция path::empty() определяет, пустая ли строка пути. Чтобы определить, пустой ли файл или каталог, идентифицируемый путем, используйте функцию empty() в хидере operations.hpp.

Объяснение названия: Контейнеры стандартной библиотеки C++ используют имя empty для аналогичных целей.

is_complete

bool is_complete() const;

Возвращает: Для ОС с одним корнем файловой системы возвращает результат has_root_directory(). Для файловых систем с многими корнями возвращает has_root_directory() && has_root_name().

Объяснение названия: Другое имя - is_absolute() - вызывает сомнения и приводит к неправильному использованию, так как на ОС с многими корнями некоторые люди считают, что результат root_name() должен удовлетворять is_absolute(), тогда как некоторые - нет. См. частые вопросы.

Замечание: На большинстве ОС законченный (complete) путь всегда однозначно идентифицирует конкретный файл или каталог. На некоторых системах (classic Mac OS к примеру), даже законченный путь может быт неоднозначным в при некоторых условиях, так как эта ОС не требует однозначности.

has_root_path

bool has_root_path() const;

Возвращаетт: has_root_name() || has_root_directory()

has_root_name

bool has_root_name() const;

Возвращает: !root_name().empty()

has_root_directory

bool has_root_directory() const;

Возвращает: !root_directory().empty()

has_relative_path

bool has_relative_path() const;

Возвращает: !relative_path().empty()

has_leafhas_leaf

bool has_leaf() const;

Возвращает: !leaf().empty()

has_branch_path

bool has_branch_path() const;

Возвращает: !branch_path().empty()

iterator

typedef implementation-defined iterator;

Это константный итератор (const iterator), удовлетворяющий требованиям для двунаправленных итераторов стандартной библиотеки C++ (24.1). Итератор это класс  (поэтому инкремент и декремент - operator++ и -- вызывают появление временных объектов (temporaries) ). Значение, ссылка и тип указателя это соответственно  std::string, const std::string &, и const std::string *.

begin

iterator begin() const;

Возвращает: m_path.begin()

end

iterator end() const;

Возвращает: m_path.end()

default_name_check_writable

static bool default_name_check_writable();

Возвращает: true если ранее не была вызвана функция default_name_check.

default_name_check

static void default_name_check( name_check new_check );

Предусловие: new_check != 0

Послеусловия: default_name_check(new_check) && !default_name_check_writable()

Throws: if !default_name_check_writable()

static name_check default_name_check();

Возвращает: функцию default name_check.

Послеусловия: !default_name_check_writable()

Функции не-члены класса

Не-член класса operator /

path operator / ( const char * lhs, const path & rhs );

path operator / ( const std::string & lhs, const path & rhs );

Возвращает: path( lhs ) /= rhs

Механизм проверки имен (name_check) по умолчанию

Сложно или невозможно написать портабельную программу, в которой нетпроверок, что используемые имена файлов и каталогов портабельны. Без автоматической верификации имен проверка становится скучной, подверженой ошибкам и неуклюжей. Но единственная функция не может быть применима для всех программ, и даже в пределах одного приложения разные пути или части пути могут потребовать верификации разными функциями проверки имен. Иногда проверка вообще не нужна.

Такие требования удовлетворяются с помощью функции проверки имен по умолчанию, которая наиболее часто применяется программами, и конструкторами класса path, позволяющими задать другую функцию проверки для менее общих случаев. Функция проверки по умолчанию может быть установлена программой, что позволяет применять проверку по умолчанию для большинства случаев.

Опасности

Функция проверки имен по умолчанию устанавливается и возвращается статическими функциями-членами класса path, и поэтому похожа на глобальную переменную. Так как глобальные переменные считаются небезопасными [Wulf-Shaw-73], класс path позволяет только один раз установить функцию проверки имен по умолчанию, и только перед первым использованием. Это превращает опасную глобальную переменную в более безопасную глобалдьную константу. Но даже с таким защитным механизмом, возможность задать функцию проверки имен по умолчанию все же является очень мощным средством, и в некоторой степени опасной, так как может изменить поведение кода, спрятанного глубоко в недрах библиотеки или где-то еще. Поэтому изменение функции проверки имен по умолчанию следует делать только в том случае, если явное задание функции проверки как аргумента конструктора класса path не подходит.

Объяснениее

Для дополнительных пояснений также см.  частые вопросы.

Название функций: имена функций-членов класса path и имена свободных функций в хидере operations.hpp были выбраны отличающимися. Целью было избежать случаи, когда и foo.empty() и empty( foo ) корректны, но с совершенно разной семантикой. Однажды path::empty() была переименована в path::is_null(), но это вызвало множество опечаток, так как std::string::empty() часто используется неподалеку.

Функции разбора: без этих функций было бы невозможно выполнять портабельные манипуляции с путями. Немаловажное значение имеет и удобство.

Возвращение константных или не-константных объектов: в некоторых ранних версиях библиотеки, функции-члены возвращали объекты как константы (т.е. с квалификатором const). См. книгу Скотта Майерса Эффективный C++, пункт 21. Квалификаторы const были убраны по трем причинам. Во-первых, чтобы согласовать Boost.Filesystem с практикой стандартной библиотеки C++. Во-вторых, потому что неконстантные возвращаемые объекты позволяют применять иногда полезные выражения с возвращаемыми объектами. В третьих, потому что число исключаемых таким образом ошибок кодирования было исчезающе мало.

Чтобы исключить ошибки, было добавлено требование к path::iterator - он не должен быть не-классом.

Примеры разбора путей на элементы

В ряде случаев требуется извлечт определенные части из пути. Хотя любой разбор может быть осуществлен путем перебора всех элементов пути, удобнее использовать вспомогательные функции, которые проще, эффективнее и вызывают меньше ошибок.

Первый столбец в таблице содержит пример пути, форматированный функцией string(). Второй столбец показывает значение, получаемое при разыменовании итератора каждого элемента. Оставшиеся столбцы показывают результаты применения различных выражений.

p.string() элементы p.root_
path()
 
p.root_
name()
p.root_
directory()
p.relative_
path()
p.root_
directory()
/ p.relative_
path()
p.root_
name() /
p.relative_
path()
p.branch_
path()
p.leaf()
Все системы                  
/ / / "" / "" / "" "" /
foo foo "" "" "" foo foo foo "" foo
/foo /,foo / "" / foo /foo foo / foo
foo/bar foo,bar "" "" "" foo/bar foo/bar foo/bar foo bar
/foo/bar /,foo,bar />/ "" / foo/bar /foo/bar foo/bar /foo bar
. . "" "" "" . . . "" .
.. .. "" "" "" .. .. .. "" ..
../foo ..,foo "" "" "" ../foo ../foo ../foo ..
Windows                  
c: c: c: c: "" "" "" c: "" c:
c:/ c:,/ c:/ c: / "" / c: c: /
c:.. c:,..de>c:,.. c: c: "" .. c:.. c:.. c: ..
c:foo c:,foo c: c: "" foo foo c:foo c: foo
c:/foo c:,/,foo c:/ c: / foo /foo c:foo c:/
//shr //shr //shr //shr "" "" "" //shr "" //shr
//shr/ //shr,/ //shr/ //shr / "" / //shr //shr /
//shr/foo //shr,
/,foo
//shr/ //shr / foo /foo //shr/foo //shr/ foo
prn: prn: prn: prn: "" "" "" prn: "" prn:

Revised 04 июня, 2005

© Copyright Beman Dawes, 2002

Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at www.boost.org/LICENSE_1_0.txt_1_0.txt)

последняя правка: 04.05.2005

библиотека BOOST C++ http://www.boost.org
перевод Elijah Koziev www.solarix.ru

  © Mental Computing 2010