Как получить символ разделителя файлов в стандартном C/C++:/или \?

Я хотел бы написать функцию:

inline char separator()
{
    /* SOMETHING */
}

который возвращает разделитель файлов системы в стандартном C/C++/C++ 11? (Я имею в виду слэш или обратную косую черту в зависимости от системы). Есть ли способ достичь этого?

Ответ 1

Я не уверен, как это сделать, кроме проверки ifdefs

inline char separator()
{
#ifdef _WIN32
    return '\\';
#else
    return '/';
#endif
}

или (как предложено PaperBirdMaster)

const char kPathSeparator =
#ifdef _WIN32
                            '\\';
#else
                            '/';
#endif

Ответ 2

Этот вопрос действительно намекает на гораздо более неприятную проблему.

Если вы просто заботитесь о UNIX против Winodws и заботитесь только о каталогах и файлах, то то, что вы уже видели, будет (в основном) работать, но более общая проблема объединения имени пути в его компоненты является гораздо более сложной задачей. В зависимости от платформы путь может включать одно или несколько из:

  • Идентификатор тома
  • Список каталогов
  • Имя файла
  • Подпоток в файле
  • Номер версии

Хотя для этого есть сторонние библиотеки (например, различные модули CPAN Perl, Boost и другие), и каждая ОС включает в себя системные функции для этого, для этого нет ничего встроенного в C, и стандарт C++ только получил эту функциональность ( путем включения модуля Boost) в 2017 году.

Вот некоторые примеры того, с чем может столкнуться такая функция:

  • UNIX и UNIX-подобные системы используют список строк, разделенных символами "/", с начальным "/" для обозначения абсолютного пути (относительно относительного пути). В некоторых контекстах (например, NFS) может также присутствовать префикс имени хоста (с разделителем ":")
  • ОС DOS и производная от DOS (Windows, OS/2 и другие) используют "\" в качестве разделителя каталогов (с API, также принимающими "/"), но пути могут также начинаться с информации о томе. Это может быть буква диска ("C:") или имя общего ресурса UNC ("\\ MYSERVER\SHARE \"). Существуют дополнительные префиксы для представления различных типов серверов и суффиксы для представления потоков не по умолчанию в файле.
  • Mac (Classic Mac OS, Carbon и некоторые API-интерфейсы Cocoa) используют ":" в качестве разделителя каталогов, причем первым термином является имя тома, а не имя каталога. Файлы Mac также могут содержать подпотоки ("вилки"), доступ к которым осуществляется через одно и то же имя с помощью специальных API. Это особенно важно для ветки ресурсов, которая широко используется в классическом программном обеспечении Mac.
  • Mac OS X при использовании API-интерфейсов UNIX обычно делает то же, что и UNIX-подобные системы, но они также могут представлять именованные подпотоки ("вилки"), добавляя суффикс "." с последующим ответвлением к имени файла.
  • В последних версиях Cocoa (Mac OS X, iOS и т.д.) Рекомендуется использовать API-интерфейс на основе URL для представления файлов из-за постоянно растущей сложности этой проблемы. Подумайте о таких вещах, как облачные документы и другие сложные сетевые файловые системы.
  • VMS довольно сложная (https://web.archive.org/web/20160324205714/http://www.djesys.com/vms/freevms/mentor/vms_path.html), но в ней есть компоненты, представляющие том, каталог путь, файл и файл-ревизия.

Есть и много других.

Стоит отметить, что библиотека файловой системы C++ 17 не охватывает все эти возможности. std::filesystem::path состоит из необязательного корневого имени (идентификатора тома), необязательного корневого каталога (для определения абсолютных путей) и последовательности имен файлов, разделенных разделителями каталогов. Это охватывает все, что может быть допустимо на платформах UNIX и большинство сценариев использования для других платформ, но не является исчерпывающим. Например, он не поддерживает подпотоки (полагаясь на ОС, чтобы каким-то образом сопоставить их с именем файла - что делается в Mac OS X, но не в классической MacOS). Он также не включает поддержку номеров версий файлов.

См. Также запись в Википедии о пути и класс C++ 17 std :: filesystem :: path

http://en.cppreference.com/w/cpp/filesystem

Я рекомендую вам посмотреть, что вы хотите сделать с разделителем каталогов (извлечь базовое имя, разбить путь на список каталогов и т.д.) И написать функцию для этого. Если вы используете C++ 17 (и вы уверены, что ваш код не будет скомпилирован компилятором до [17] C++), то вы (вероятно) можете использовать стандартный библиотечный код C++ для написания переносимой реализации эта функция. Если нет, эта функция должна будет использовать специфичный для платформы #ifdef для каждой платформы, которую вы будете поддерживать, используя #error если не #error ни одно из условий, чтобы заставить вас добавлять условия для неожиданных платформ.

Или используйте стороннюю библиотеку (например, Boost), которая включает функции для всего этого, если это приемлемо.

Ответ 3

это может быть что-то вроде этого

#if defined(WIN32) || defined(_WIN32) 
#define PATH_SEPARATOR "\\" 
#else 
#define PATH_SEPARATOR "/" 
#endif 

Ответ 4

Принятый ответ не работает под Cygwin. Cygwin скомпилированные программы, запущенные в Windows, могут использовать разделитель "\" в стиле Windows, но он не определяет _WIN32 или подобных. Модифицированное решение, которое работает под Cygwin:

inline char separator()
{
#if defined _WIN32 || defined __CYGWIN__
    return '\\';
#else
    return '/';
#endif
}

или

const char kPathSeparator =
#if defined _WIN32 || defined __CYGWIN__
    '\\';
#else
    '/';
#endif

Ответ 5

Если ваш компилятор уже предлагает возможности С++ 17, то вы можете использовать std::experimental::filesystem::path::preferred_separator который должен возвращать / или \ зависимости от вашей платформы.

Смотрите это для получения дополнительной информации.

Ответ 6

Я удивлен, что никто не предложил следующее. Это немного улучшает то, что предлагают другие.

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

Windows использует косую черту для обозначения аргументов. Таким образом, вы можете проверить это первым в первом аргументе argv[0], который содержит имя запускаемой программы.

Обратите внимание на следующие результаты при удалении имени пути предыдущей последней косой черты, оставив sepd качестве имени файла программы.

#include <string.h>
#include <stdio.h>

int main(int argc, char *argv[]){
//int a = 1
//int this = (a == 1) ? 20 : 30;  //ternary operator
//is a==1 ? If yes then 'this' = 20, or else 'this' = 30
    char *sepd = (strrchr(argv[0], '\/') != NULL) ? 
        strrchr(argv[0], '\/') : 
        strrchr(argv[0], '\\');
    printf("%s\n\n", sepd);
    printf("usage: .%s <host> \n\n", sepd);
    while (getchar() != '\n');
}

Но во всей реальности это довольно грязно, и с недавним переходом Windows к Bash (еще не реализованным в это время) это может привести к неожиданным или непредвиденным результатам.

Это также не столь разумно и непроницаемо для ошибок, как и другие, в частности #ifdef _WIN32.

Ответ 7

Теперь с С++ 17 можно использовать std::filesystem::path::preferred_separator (см.):

#include <filesystem>
#include <iostream>

int main() {
    std::cout << "This OS separator: " << std::filesystem::path::preferred_separator;
}