Как сравнить (каталог) пути в C ++? - PullRequest
4 голосов
/ 14 декабря 2011

Я ищу способ проверить, совпадают ли 2 строки с точки зрения пути к файловой системе (каталог).Например, все строки из этого набора одинаковы с точки зрения пути к файловой системе: {/x, \x, //x, /x/}, но эти два - /x и /y - нет, даже если/y является символической ссылкой на /x.Программа, которую я собираюсь написать, должна работать как для Linux, так и для Windows, поэтому я ищу портативное решение.

РЕДАКТИРОВАТЬ:

Яиспользую только заголовочные файлы boost, так что решение с boost::filesystem не подходит для меня.Я знаю, что в Windows API есть UrlCompare, есть ли что-то подобное в Linux?

Ответы [ 4 ]

3 голосов
/ 14 декабря 2011

Любое решение не Boost будет включать системно-зависимый код (который скрыто в Boost, если вы используете Boost). И вам нужно будет точно определить что вы подразумеваете под «соответствием»: должны "./MyFile.xxx" и "MyFile.xxx" сравнить равных? А как насчет "aaa/.../MyFile.xxx" и "MyFile.xxx"?

То, как я бы справился, это определить класс с двумя членами данных, std::string с префиксом & ldquo; rdquo; (который всегда будет пуст в Unix) и std::vector<std::string> со всеми путями элементы. Этот класс будет увязан с необходимым сравнением функции, и будет использовать системно-зависимый код для реализации конструктор; сам конструктор будет в исходном файле, а исходный файл будет включать машинно-зависимый заголовок (обычно с помощью отдельный каталог для каждого варианта и выбор заголовка с помощью -I или /I, чтобы указать, какой каталог использовать). Такие вещи который может войти в заголовок:

inline bool
isPathSeparator( char ch )
{
    return ch == '/';
}

std::string
getHeader( std::string const& fullPathName )
{
    return "";
}

bool
charCompare( char lhs, char rhs )
{
    return lhs < rhs;
}

bool
charMatch( char lhs, char rhs )
{
    return lhs == rhs;
}

для Unix, с:

inline bool
isPathSeparator( char ch )
{
    return ch == '/' || ch == '\\';
}

std::string
getHeader( std::string const& fullPathName )
{
    return fullPathName.size() > 2 && fullPathName[1] == ':'
        ? fullPathName.substr( 0, 2 )
        : std::string();
}

bool
charCompare( char lhs, char rhs )
{
    return tolower( (unsigned char)lhs) < tolower( (unsigned char)rhs );
}

bool
charMatch( char lhs, char rhs )
{
    return tolower( (unsigned char)lhs ) == tolower( (unsigned char)rhs );
}

для Windows.

Затем конструктор будет использовать getHeader для инициализации заголовка, и перебирать input.begin() + header.size() и input.end(), ломая строка в элементы. Если вы столкнулись с элементом ".", игнорируйте его, и для одного из ".." используйте pop_back(), чтобы удалить верх элемент пути, при условии, что путь не пуст. После этого просто вопрос определения компараторов для использования charCompare и charMatch для char и std::lexicographical_compare или std::equal (после проверки того, что размеры равны) с компаратор для std::string (и, вероятно, для вашего нового учебный класс). Что-то вроде:

struct FileNameCompare
{
    bool operator()( char lhs, char rhs ) const
    {
        return charCompare( lhs, rhs );
    }
    bool operator()( std::string const& lhs, std::string const& rhs ) const
    {
        return std::lexicographical_compare(
            lhs.begin(), lhs.end(),
            rhs.begin(), rhs.end(),
            *this );
    }
    bool operator()( FileName const& lhs, FileName const& rhs ) const
    {
        return (*this)( lhs.prefix, rhs.prefix )
            || ( !(*this)( rhs.prefix, lhs.prefix )
                && std::lexicographical_compare(
                    lhs.elements.begin(), lhs.elements.end(),
                    rhs.elements.begin(), rhs.elements.end(),
                    *this ) );
    }
};

struct FileNameMatch
{
    bool operator()( char lhs, char rhs ) const
    {
        return charMatch( lhs, rhs );
    }
    bool operator()( std::string const& lhs, std::string const& rhs ) const
    {
        return lhs.size() == rhs.size()
            && std::equal( lhs.begin(), lhs.end(), rhs.begin(), *this );
    }
    bool operator()( FileName const& lhs, FileName const& rhs ) const
    {
        return (*this)( lhs.prefix, rhs.prefix )
            && lhs.elements.size() == rhs.elements.size()
            && std::equal( lhs.elements.begin(), lhs.elements.end(),
                           rhs.elements.begin(),
                           *this );
    }
};

должен сделать трюк. (Просто помните, что operator()( char, char ) const должно быть в исходном файле; вы не можете вставить их в заголовок, который не будет включать системно-зависимый заголовок, который определяет charCompare и charMatch.)

2 голосов
/ 14 декабря 2011

Используйте библиотеку boost :: filesystem - здесь есть функция сравнения путей.

EDIT: вы можете попробовать apr - да, это не C ++, однакобудет портативным.

1 голос
/ 16 июля 2012
std::string path1 = "c:\\folder\\";
std::string path2 = "c:\\folder\\folder\\..\\";

boost::filesystem::equivalent(boost::filesystem::path(path1), boost::filesystem::path(path2)

Код возвращает true, потому что папка на самом деле одинакова.

1 голос
/ 14 декабря 2011

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

EDIT

Это обсуждение на gmane.org касается минимальной версии boost::filesystem только для заголовков. В итоге: этого не существует. Таким образом, вы в конечном итоге создадите свою собственную библиотеку абстракций и предоставите ей кроссплатформенную функциональность.

Под Linux есть dirent.h, которая является библиотекой POSIX C. Для Windows вам придется использовать Win32 API. Тем не менее, существует также так называемая «Библиотека Directory-Stream», которая кажется кроссплатформенной, доступна здесь (сайт на немецком языке).

...