web-dev-qa-db-fra.com

Comment obtenir le symbole séparateur de fichiers en C/C++ standard:/ou \?

J'aimerais écrire une fonction:

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

qui renvoie le séparateur de fichiers du système en standard C/C++/C++ 11? (Je veux dire slash ou backslash selon le système). Y a-t-il un moyen d'y parvenir?

19
Vincent

Je ne sais pas comment le faire autrement qu'en vérifiant ifdefs

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

ou (comme suggéré par PaperBirdMaster)

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

Cette question fait vraiment allusion à un problème beaucoup plus méchant.

Si vous vous souciez simplement d'UNIX et de Winodws et que vous vous souciez uniquement des répertoires et des fichiers, alors ce que vous avez déjà vu fonctionnera (généralement), mais la question plus générique de la jonction d'un nom de chemin d'accès à ses composants est un problème beaucoup plus laid. Selon la plate-forme, un chemin peut inclure un ou plusieurs des éléments suivants:

  • Identifiant de volume
  • Liste de répertoires
  • Nom de fichier
  • Sous-flux dans le fichier
  • Numéro de version

Bien qu'il existe des bibliothèques tierces (comme divers modules CPAN Perl, Boost et autres) pour cela, et que chaque système d'exploitation en inclut des fonctions système, rien n'est intégré à C pour cela et le standard C++ n'a obtenu que cette fonctionnalité (en incorporant le module Boost) en 2017.

Voici quelques exemples de problèmes pouvant être traités par une telle fonction:

  • Les systèmes UNIX et de type UNIX utilisent une liste de chaînes séparées par des caractères "/", précédées de "/" pour indiquer un chemin absolu (par rapport à un chemin relatif). Dans certains contextes (comme NFS), il peut également y avoir un préfixe de nom d’hôte (avec un délimiteur ":")
  • Les systèmes d'exploitation dérivés de DOS (Windows, OS/2 et autres) utilisent "\" comme séparateur de répertoire (les API acceptant également "/"), mais les chemins peuvent également être préfixés avec les informations de volume. Il peut s'agir d'une lettre de lecteur ("C:") ou d'un nom de partage UNC ("\\ MYSERVER\SHARE \"). Il existe des préfixes supplémentaires pour représenter différents types de serveurs et des suffixes pour représenter des flux autres que ceux par défaut dans un fichier.
  • Les Mac (Classic Mac OS, Carbon et certaines API Cocoa) utilisent ":" comme séparateur de répertoire, le premier terme étant un nom de volume et non un nom de répertoire. Les fichiers Mac peuvent également contenir des sous-flux ("forks"), auxquels on accède sous le même nom à l'aide d'API spéciales. Ceci est particulièrement important pour le resource fork, qui est largement utilisé dans les logiciels Mac classiques.
  • Mac OS X, lors de l'utilisation des API UNIX, fait généralement ce que font les systèmes de type UNIX, mais ils peuvent également représenter des sous-flux nommés ("forks") en ajoutant un "." suivi du nom de fork pour le nom de fichier.
  • Les dernières versions de Cocoa (Mac OS X, iOS, etc.) recommandent l'utilisation d'une API basée sur une URL pour représenter les fichiers, en raison de la complexité croissante de ce problème. Pensez à des éléments tels que les documents en nuage et d'autres systèmes de fichiers complexes en réseau.
  • VMS est assez compliqué ( https://web.archive.org/web/20160324205714/http://www.djesys.com/vms/freevms/mentor/vms_path.html ), mais il a des composants qui représentent un volume, chemin de répertoire, fichier et révision de fichier.

Il y en a beaucoup d'autres aussi.

Il est à noter que la bibliothèque de systèmes de fichiers C++ 17 ne couvre pas toutes ces possibilités. Le std::filesystem::path consiste en un nom-racine facultatif (un identificateur de volume), un répertoire racine facultatif (pour identifier les chemins absolus) et une séquence de noms de fichiers séparés par des séparateurs de répertoires. Cela couvre tout ce qui est susceptible d'être valable sur les plateformes UNIX et la majorité des cas d'utilisation d'autres plateformes, mais n'est pas complet. Par exemple, il ne prend pas en charge les sous-flux (le système d'exploitation permet de les mapper sur un nom de fichier - ce qui est effectué par Mac OS X mais pas par MacOS classique). Il n’inclut pas non plus la prise en charge des numéros de version de fichier.

Voir aussi Entrée de Wikipedia sur Path et le C++ 17 std :: filesystem :: path class

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

Je vous recommande de regarder ce que vous voulez faire avec le séparateur de répertoire (extraire le nom de base, diviser un chemin d'accès en une liste de répertoires, etc.) et d'écrire une fonction pour le faire. Si vous utilisez C++ 17 (et que vous êtes certain que votre code ne sera pas compilé par un compilateur C++ antérieur à 17), vous pouvez (probablement) utiliser le code de bibliothèque C++ standard pour écrire une implémentation portable de cette fonction. Sinon, cette fonction devra utiliser des #ifdefs spécifiques à la plate-forme pour chaque plate-forme que vous prendrez en charge, en utilisant un #error si aucune des conditions n'est remplie, pour vous obliger à ajouter des conditions aux plates-formes inattendues.

Ou utilisez une bibliothèque tierce (comme Boost) qui inclut des fonctions pour tout cela, si cela est acceptable.

10
David C.

ça peut être quelque chose comme ça

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

La réponse acceptée ne fonctionne pas sous Cygwin. Les programmes compilés par Cygwin fonctionnant sous Windows peuvent utiliser le séparateur de style Windows '\', mais ne définissent pas _WIN32 ou autres. Une solution modifiée qui fonctionne sous Cygwin:

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

ou

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

Si votre compilateur offre déjà des fonctionnalités c ++ 17, vous pouvez utiliser std::experimental::filesystem::path::preferred_separator qui devrait renvoyer / ou \ en fonction de votre plateforme. 

Voir this pour plus d'informations.

4
Overblade

Je suis surpris que personne n'ait offert ce qui suit. Cela s'appuie un peu sur ce que les autres proposent ici.

Bien que, dans cet exemple, j'essaie de saisir dynamiquement le nom de l'exécutable en cours d'utilisation, il ne serait pas trop difficile de faire le saut et de réappliquer cette opération comme vous le souhaitez.

Windows utilise une barre oblique pour désigner les arguments. Vous pouvez donc vérifier cela en premier dans le premier argument argv[0], qui contient le nom du programme en cours d'exécution.

Notez les résultats suivants en supprimant le chemin précédent de la dernière barre oblique, en laissant sepd comme nom de fichier du programme.

#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');
}

Mais dans les faits, il s’agit là d’une situation assez sale et, avec la dernière initiative de Windows visant à inclure Bash (qui n’a pas encore été implémentée), cela peut produire des résultats inattendus ou imprévus.

Ce n’est pas non plus aussi sain d’esprit et insensible aux erreurs que ce que d’autres ont proposé, en particulier #ifdef _WIN32.

0
SYANiDE