Как получить символ разделителя файлов в стандартном 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;
}