it-swarm.com.de

Wie erhalte ich das Datei-Trennzeichen in C/C++:/oder \?

Ich möchte eine Funktion schreiben:

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

gibt das Dateiseparator des Systems in Standard C/C++/C++ 11 zurück? (Ich meine Slash oder Backslash je nach System). Gibt es einen Weg, dies zu erreichen?

19
Vincent

Ich bin mir nicht sicher, wie ich das machen soll, indem ich ifdefs überprüfe

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

oder (wie von PaperBirdMaster vorgeschlagen)

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

Diese Frage weist auf ein viel schlimmeres Problem hin.

Wenn Sie sich einfach für UNIX vs. Winodws interessieren und nur für Verzeichnisse und Dateien sorgen, wird das, was Sie bereits gesehen haben, (meistens) funktionieren, aber das allgemeinere Problem, einen Pfadnamen in seine Komponenten zu spleißen, ist ein viel hässlicheres Problem. Abhängig von der Plattform kann ein Pfad ein oder mehrere der folgenden Elemente enthalten:

  • Datenträgerkennung
  • Liste der Verzeichnisse
  • Dateiname
  • Substream innerhalb der Datei
  • Versionsnummer

Zwar gibt es Drittanbieter-Bibliotheken (wie verschiedene CPAN-Perl-Module, Boost und andere), und jedes Betriebssystem enthält Systemfunktionen dafür, jedoch ist in C nichts eingebaut, und der C++ - Standard hat nur diese Funktionalität erhalten (durch Einbinden von) das Boost-Modul) im Jahr 2017.

Einige Beispiele, mit denen sich eine solche Funktion befassen muss, sind:

  • UNIX- und UNIX-ähnliche Systeme verwenden eine Liste von Zeichenfolgen, die durch "/" - Zeichen getrennt sind, wobei ein führendes "/" einen absoluten Pfad (im Gegensatz zu einem relativen Pfad) angibt. In einigen Kontexten (wie NFS) kann es auch ein Hostnamen-Präfix geben (mit einem Trennzeichen ":").
  • Von DOS und DOS abgeleitete Betriebssysteme (Windows, OS/2 und andere) verwenden "\" als Verzeichnisseparator (wobei die APIs auch "/" akzeptieren), aber den Pfaden können auch Datenträgerinformationen vorangestellt werden. Dies kann ein Laufwerkbuchstabe ("C:") oder ein UNC-Freigabename ("\\ MYSERVER\SHARE \") sein. Es gibt zusätzliche Präfixe, um verschiedene Arten von Servern und Suffixe darzustellen, um nicht standardmäßige Streams in einer Datei darzustellen.
  • Macs (Classic Mac OS, Carbon und einige Cocoa-APIs) verwenden ":" als Verzeichnisseparator, wobei der erste Begriff ein Volume-Name und kein Verzeichnisname ist. Mac-Dateien können auch Sub-Streams ("Forks") enthalten, auf die über spezielle Namen mit speziellen APIs zugegriffen wird. Dies ist besonders wichtig für den resource Fork, der in der klassischen Mac-Software häufig verwendet wird.
  • Wenn Mac OS X die UNIX-APIs verwendet, macht dies im Allgemeinen das, was UNIX-ähnliche Systeme tun, sie können jedoch auch benannte Sub-Streams ("forks") darstellen, indem sie ein "." gefolgt von dem Gabelnamen bis zum Dateinamen.
  • Die neuesten Versionen von Cocoa (Mac OS X, iOS usw.) empfehlen die Verwendung einer URL-basierten API zur Darstellung von Dateien, da dieses Problem immer komplexer wird. Denken Sie an Dinge wie Cloud-basierte Dokumente und andere komplizierte vernetzte Dateisysteme.
  • VMS ist ziemlich kompliziert ( https://web.archive.org/web/20160324205714/http://www.djesys.com/vms/freevms/mentor/vms_path.html ), aber es enthält Komponenten, die repräsentieren ein Volume, Verzeichnispfad, Datei und Dateiversion.

Es gibt auch viele andere.

Es ist erwähnenswert, dass die C++ 17-Dateisystembibliothek nicht alle diese Möglichkeiten abdeckt. std::filesystem::path besteht aus einem optionalen root-name (einem Datenträgerbezeichner), einem optionalen root-verzeichnis (zur Identifizierung absoluter Pfade) und einer durch Verzeichnisseparatoren getrennten Folge von Dateinamen. Dies deckt alles ab, was wahrscheinlich auf UNIX-Plattformen und die Mehrzahl der Anwendungsfälle für andere Plattformen gilt, ist jedoch nicht umfassend. Zum Beispiel werden keine Sub-Streams unterstützt (abhängig vom Betriebssystem, um sie irgendwie einem Dateinamen zuzuordnen - dies wird von Mac OS X durchgeführt, nicht jedoch von klassischem MacOS). Es enthält auch keine Unterstützung für Dateiversionsnummern.

Siehe auch Wikipedia-Eintrag zu Path und C++ 17 std :: filesystem :: path class

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

Ich empfehle Ihnen, sich mit dem Verzeichnisseparator zu beschäftigen (extrahieren Sie den Basisnamen, brechen Sie einen Pfad in eine Liste von Verzeichnissen usw.) und schreiben Sie eine Funktion, um dies zu tun. Wenn Sie C++ 17 verwenden (und Sie sind sicher, dass Ihr Code nicht von einem C++ - Compiler vor 17 kompiliert wird), können Sie (wahrscheinlich) Standard-C++ - Bibliothekscode verwenden, um eine portable Implementierung dieser Funktion zu schreiben. Andernfalls muss diese Funktion plattformspezifische #ifdefs für jede unterstützte Plattform verwenden. Wenn keine der Bedingungen erfüllt ist, muss #error verwendet werden, um Bedingungen für unerwartete Plattformen hinzuzufügen.

Oder verwenden Sie eine Drittanbieter-Bibliothek (wie Boost), die Funktionen für all dies enthält, sofern dies akzeptabel ist.

10
David C.

das kann so etwas sein

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

Die akzeptierte Antwort funktioniert nicht unter Cygwin. Cygwin-kompilierte Programme, die unter Windows ausgeführt werden, können das Windows-Trennzeichen '\' verwenden, definieren jedoch nicht _WIN32 oder ähnliches. Eine modifizierte Lösung, die unter Cygwin funktioniert:

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

oder

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

Wenn Ihr Compiler bereits C++ 17-Funktionen bietet, können Sie std::experimental::filesystem::path::preferred_separator verwenden, der je nach Plattform entweder / oder \ zurückgeben sollte. 

Siehe this für weitere Informationen.

4
Overblade

Jetzt mit C++ 17 ist es möglich, std::filesystem::path::preferred_separator ( siehe ):

#include <filesystem>
#include <iostream>

int main() {
    std::cout << "This OS separator: " << std::filesystem::path::preferred_separator;
}
0
João Paulo

Ich bin überrascht, dass niemand folgendes angeboten hat. Dies baut ein wenig auf dem auf, was andere hier anbieten.

Obwohl in diesem Beispiel versucht wird, dynamisch den Namen der ausführbaren Datei zu ermitteln, die zur Verwendung ausgeführt wird, wäre es nicht zu schwer, den Sprung zu machen und diesen erneut anzuwenden, je nachdem, was Sie brauchen.

Windows verwendet einen Schrägstrich, um Argumente zu kennzeichnen. Sie können also zuerst im ersten Argument argv[0] nach dem Namen des ausgeführten Programms suchen.

Beachten Sie die folgenden Ergebnisse, wenn Sie den Pfadnamen vor dem letzten Schrägstrich entfernen und sepd als Dateinamen des Programms belassen.

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

Aber in Wirklichkeit ist das ziemlich schmutzig und mit der jüngsten Umstellung von Windows auf Bash (derzeit noch nicht implementiert) kann dies zu unerwarteten oder unerwarteten Ergebnissen führen.

Es ist auch nicht so vernünftig und fehleranfällig wie andere, insbesondere #ifdef _WIN32.

0
SYANiDE