Holen Sie sich einen Dateinamen von einem Pfad

82

Was ist der einfachste Weg, um den Dateinamen von einem Pfad zu erhalten?

string filename = "C:\\MyDirectory\\MyFile.bat"

In diesem Beispiel sollte ich "MyFile" erhalten. ohne Verlängerung.

Nidhal
quelle
1
Von hinten suchen, bis Sie eine Rücktaste drücken?
Kerrek SB
2
@ KerrekSB, du meinst Backslash ;)
Nim
Ich habe eine std :: string, die einen Pfad einer Datei "c: \\ MyDirectory \\ Myfile.pdf" enthält. Ich muss diese Datei in myfile_md.pdf umbenennen, damit ich den Dateinamen aus dem Pfad abrufen kann.
Nidhal
1
Wenn Sie viel mit Dateipfaden arbeiten müssen, sollten Sie Boost FileSystem verwenden. Boost.org/doc/libs/release/libs/filesystem/v3/doc/index.htm
edA-qa mort-ora-y
2
@Nim: Ja! Ich muss Abstand gehalten haben ...
Kerrek SB

Antworten:

29

_splitpath sollte das tun, was Sie brauchen. Sie können dies natürlich manuell tun, aber auch _splitpathalle Sonderfälle.

BEARBEITEN:

Wie BillHoag erwähnt wird empfohlen , die sicherere Version zu verwenden _splitpathgenannt _splitpath_s , wenn verfügbar.

Oder wenn Sie etwas Tragbares wollen, können Sie einfach so etwas tun

std::vector<std::string> splitpath(
  const std::string& str
  , const std::set<char> delimiters)
{
  std::vector<std::string> result;

  char const* pch = str.c_str();
  char const* start = pch;
  for(; *pch; ++pch)
  {
    if (delimiters.find(*pch) != delimiters.end())
    {
      if (start != pch)
      {
        std::string str(start, pch);
        result.push_back(str);
      }
      else
      {
        result.push_back("");
      }
      start = pch + 1;
    }
  }
  result.push_back(start);

  return result;
}

...
std::set<char> delims{'\\'};

std::vector<std::string> path = splitpath("C:\\MyDirectory\\MyFile.bat", delims);
cout << path.back() << endl;
AndersK
quelle
2
Es gibt keine _splitpathan der ein auf meiner Maschine enthält.
James Kanze
9
Ich habe Visual Studio und g ++ und Sun CC. Warum sollte ich etwas verwenden, das nicht dem Standard entspricht, wenn es perfekt tragbare Lösungen gibt?
James Kanze
2
@ James, die Seite, auf die verlinkt ist, sagt, dass es in ist <stdlib.h>. In Bezug auf die Portabilität können Sie vielleicht einige Beispiele für „perfekt gute tragbare Lösungen“ auflisten?
Synetech
2
@Synetech Die mit verlinkte Seite beschreibt eine Microsoft-Erweiterung, nicht <stdlib.h>. Und die naheliegende tragbare Lösung ist boost::filesystem.
James Kanze
3
@ James, hast du nicht _splitpathin der stdlib.hKopie von VS? Dann möchten Sie möglicherweise eine Reparaturinstallation von VS durchführen.
Synetech
62

Eine mögliche Lösung:

string filename = "C:\\MyDirectory\\MyFile.bat";

// Remove directory if present.
// Do this before extension removal incase directory has a period character.
const size_t last_slash_idx = filename.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
    filename.erase(0, last_slash_idx + 1);
}

// Remove extension if present.
const size_t period_idx = filename.rfind('.');
if (std::string::npos != period_idx)
{
    filename.erase(period_idx);
}
hmjd
quelle
das einfachste ist immer das beste!
Jean-François Fabre
60

Die Aufgabe ist ziemlich einfach, da der Basisdateiname nur der Teil der Zeichenfolge ist, der am letzten Begrenzer für Ordner beginnt:

std::string base_filename = path.substr(path.find_last_of("/\\") + 1)

Wenn die Erweiterung ebenfalls entfernt werden soll, müssen Sie nur die letzte finden .und substrbis zu diesem Punkt gehen

std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);

Vielleicht sollte es eine Überprüfung geben, um mit Dateien fertig zu werden, die nur aus Erweiterungen bestehen (dh .bashrc...)

Wenn Sie dies in separate Funktionen aufteilen, können Sie die einzelnen Aufgaben flexibel wiederverwenden:

template<class T>
T base_name(T const & path, T const & delims = "/\\")
{
  return path.substr(path.find_last_of(delims) + 1);
}
template<class T>
T remove_extension(T const & filename)
{
  typename T::size_type const p(filename.find_last_of('.'));
  return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
}

Der Code ist so konzipiert, dass er mit verschiedenen std::basic_stringInstanzen verwendet werden kann (z. B. std::string& std::wstring...).

Der Nachteil der Vorlage ist die Anforderung, den Vorlagenparameter anzugeben, wenn a const char *an die Funktionen übergeben wird.

Sie könnten also entweder:

A) Verwenden Sie nur, std::stringanstatt den Code als Vorlage zu verwenden

std::string base_name(std::string const & path)
{
  return path.substr(path.find_last_of("/\\") + 1);
}

B) Bereitstellung einer Umhüllungsfunktion mit std::string(als Zwischenprodukte, die wahrscheinlich wegliniert / optimiert werden)

inline std::string string_base_name(std::string const & path)
{
  return base_name(path);
}

C) Geben Sie den Vorlagenparameter an, wenn Sie mit aufrufen const char *.

std::string base = base_name<std::string>("some/path/file.ext");

Ergebnis

std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;

Druckt

MyFile
Pixelchemist
quelle
In diesem Anwendungsfall ist alles in Ordnung (und die ursprüngliche Frage wird beantwortet), aber Ihr Erweiterungsentferner ist nicht perfekt - es wird fehlschlagen, wenn wir dort so etwas wie "/home/user/my.dir/myfile"
avtomaton
@avtomaton Die Funktion zum Entfernen von Erweiterungen sollte für einen Dateinamen und nicht für einen Pfad verwendet werden. ( base_name
Bewerben Sie sich
Ich verstehe es (deshalb habe ich geschrieben, dass die ursprüngliche Frage beantwortet wurde und in diesem Anwendungsfall alles in Ordnung ist). Ich wollte nur auf jemanden hinweisen, der versucht, diese Schnipsel zu verwenden.
Avtomaton
Sehr schöne Erklärung. Es verbessert das strukturelle Verständnis des Problems. Danke
hell_ical_vortex
38

Die einfachste Lösung ist, so etwas wie zu verwenden boost::filesystem. Wenn dies aus irgendeinem Grund keine Option ist ...

Um dies richtig zu machen, ist systemabhängiger Code erforderlich: Unter Windows entweder '\\'oder '/'als Pfadtrennzeichen; '/'funktioniert unter Unix nur und unter anderen Systemen, wer weiß. Die offensichtliche Lösung wäre etwa:

std::string
basename( std::string const& pathname )
{
    return std::string( 
        std::find_if( pathname.rbegin(), pathname.rend(),
                      MatchPathSeparator() ).base(),
        pathname.end() );
}

, MatchPathSeparatorwobei in einem systemabhängigen Header Folgendes definiert ist:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '/';
    }
};

für Unix oder:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '\\' || ch == '/';
    }
};

für Windows (oder etwas anderes für ein anderes unbekanntes System).

EDIT: Ich habe die Tatsache übersehen, dass er auch die Erweiterung unterdrücken wollte. Dafür mehr vom Gleichen:

std::string
removeExtension( std::string const& filename )
{
    std::string::const_reverse_iterator
                        pivot
            = std::find( filename.rbegin(), filename.rend(), '.' );
    return pivot == filename.rend()
        ? filename
        : std::string( filename.begin(), pivot.base() - 1 );
}

Der Code ist etwas komplexer, da sich in diesem Fall die Basis des Reverse-Iterators auf der falschen Seite befindet, an der wir schneiden möchten. (Denken Sie daran, dass die Basis eines umgekehrten Iterators eine hinter dem Zeichen ist, auf das der Iterator zeigt.) Und selbst das ist ein wenig zweifelhaft: Ich mag die Tatsache nicht, dass er beispielsweise eine leere Zeichenfolge zurückgeben kann. (Wenn das einzige '.'das erste Zeichen des Dateinamens ist, würde ich argumentieren, dass Sie den vollständigen Dateinamen zurückgeben sollten. Dies würde ein wenig zusätzlichen Code erfordern, um den Sonderfall zu erfassen.)}

James Kanze
quelle
9
Wie wäre es mit der Verwendung string::find_last_ofanstatt der Manipulation von Reverse Iteratoren?
Luc Touraille
@ LucTouraille Warum zwei Arten lernen, Dinge zu tun, wenn man es tut? Sie würden die Reverse-Iteratoren für jeden Container außer benötigen string, also müssen Sie sie trotzdem lernen. Und nachdem Sie sie gelernt haben, gibt es keinen Grund, sich die Mühe zu machen, die gesamte aufgeblähte Oberfläche zu lernen std::string.
James Kanze
Hinweis: Der Header <Dateisystem> wird mit Visual Studio 2015 und höher geliefert , sodass Sie keine Abhängigkeit von Boost hinzufügen müssen, um ihn zu verwenden.
Unsichtbar
15

Sie können auch die Shell-Pfad-APIs PathFindFileName, PathRemoveExtension verwenden. Wahrscheinlich schlechter als _splitpath für dieses spezielle Problem, aber diese APIs sind sehr nützlich für alle Arten von Pfadanalysejobs und berücksichtigen UNC-Pfade, Schrägstriche und andere seltsame Dinge.

wstring filename = L"C:\\MyDirectory\\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart); 

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx

Der Nachteil ist, dass Sie auf shlwapi.lib verlinken müssen, aber ich bin mir nicht sicher, warum das ein Nachteil ist.

Skrymsli
quelle
Meine bevorzugte Lösung, um einen Dateinamen von einem Pfad zu erhalten.
Andreas
15

Wenn Sie Boost verwenden können,

#include <boost/filesystem.hpp>
path p("C:\\MyDirectory\\MyFile.bat");
string basename = p.filename().string();
//or 
//string basename = path("C:\\MyDirectory\\MyFile.bat").filename().string();

Das ist alles.

Ich empfehle Ihnen, die Boost-Bibliothek zu verwenden. Boost bietet Ihnen viele Annehmlichkeiten, wenn Sie mit C ++ arbeiten. Es unterstützt fast alle Plattformen. Wenn Sie Ubuntu verwenden, können Sie die Boost-Bibliothek nur in einer Zeile installieren sudo apt-get install libboost-all-dev(siehe Wie installiere ich Boost unter Ubuntu? )

plhn
quelle
14

Der einfachste Weg in C ++ 17 ist:

Verwenden Sie das #include <filesystem>und filename()für den Dateinamen mit und stem()ohne Erweiterung.

    #include <iostream>
    #include <filesystem>
    namespace fs = std::filesystem;

    int main()
    {
        string filename = "C:\\MyDirectory\\MyFile.bat";

    std::cout << fs::path(filename).filename() << '\n'
        << fs::path(filename).stem() << '\n'
        << fs::path("/foo/bar.txt").filename() << '\n'
        << fs::path("/foo/bar.txt").stem() << '\n'
        << fs::path("/foo/.bar").filename() << '\n'
        << fs::path("/foo/bar/").filename() << '\n'
        << fs::path("/foo/.").filename() << '\n'
        << fs::path("/foo/..").filename() << '\n'
        << fs::path(".").filename() << '\n'
        << fs::path("..").filename() << '\n'
        << fs::path("/").filename() << '\n';
    }

Ausgabe:

MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"

Referenz: cppreference

eliasetm
quelle
es ist nicht mehr in "experimentell"
Vit
13

Funktion:

#include <string>

std::string
basename(const std::string &filename)
{
    if (filename.empty()) {
        return {};
    }

    auto len = filename.length();
    auto index = filename.find_last_of("/\\");

    if (index == std::string::npos) {
        return filename;
    }

    if (index + 1 >= len) {

        len--;
        index = filename.substr(0, len).find_last_of("/\\");

        if (len == 0) {
            return filename;
        }

        if (index == 0) {
            return filename.substr(1, len - 1);
        }

        if (index == std::string::npos) {
            return filename.substr(0, len);
        }

        return filename.substr(index + 1, len - index - 1);
    }

    return filename.substr(index + 1, len - index);
}

Tests:

#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>

TEST_CASE("basename")
{
    CHECK(basename("") == "");
    CHECK(basename("no_path") == "no_path");
    CHECK(basename("with.ext") == "with.ext");
    CHECK(basename("/no_filename/") == "no_filename");
    CHECK(basename("no_filename/") == "no_filename");
    CHECK(basename("/no/filename/") == "filename");
    CHECK(basename("/absolute/file.ext") == "file.ext");
    CHECK(basename("../relative/file.ext") == "file.ext");
    CHECK(basename("/") == "/");
    CHECK(basename("c:\\windows\\path.ext") == "path.ext");
    CHECK(basename("c:\\windows\\no_filename\\") == "no_filename");
}
Rian Quinn
quelle
8

Aus C ++ - Dokumenten - string :: find_last_of

#include <iostream>       // std::cout
#include <string>         // std::string

void SplitFilename (const std::string& str) {
  std::cout << "Splitting: " << str << '\n';
  unsigned found = str.find_last_of("/\\");
  std::cout << " path: " << str.substr(0,found) << '\n';
  std::cout << " file: " << str.substr(found+1) << '\n';
}

int main () {
  std::string str1 ("/usr/bin/man");
  std::string str2 ("c:\\windows\\winhelp.exe");

  SplitFilename (str1);
  SplitFilename (str2);

  return 0;
}

Ausgänge:

Splitting: /usr/bin/man
 path: /usr/bin
 file: man
Splitting: c:\windows\winhelp.exe
 path: c:\windows
 file: winhelp.exe
jave.web
quelle
Vergessen Sie nicht (und zu handhaben), dass find_last_ofzurückgegeben wird, string::nposwenn nichts gefunden wurde.
Congusbongus
@congusbongus Stimmt, aber es gibt keinen Sinn, den Dateipfad zu teilen, wenn es nur ein Dateiname (ohne Pfad) ist :)
jave.web
@ jave.web Es ist sinnvoll und MUSS die Rückgabe 'string :: npos' behandeln. Die Implementierung einer Funktion hierfür sollte in der Lage sein, verschiedene Eingaben zu verarbeiten, einschließlich "nur Dateiname". Andernfalls ist es nutzlos, wenn es in der tatsächlichen Implementierung fehlerhaft ist.
Winux
@winux Dies berücksichtigt bereits gültige Pfade ... Wenn Sie der Eingabe nicht vertrauen, sollten Sie natürlich zuerst den Pfad überprüfen .
jave.web
@winux Wie auch immer die Überprüfung string::nposmuss nicht getan werden , wegen der Art und Weise , wie diese und string::substrumgesetzt werden. a) string::npos wird als "Länge" übergeben => substrhat das Leseverhalten bis zum Ende dokumentiert. b) substrwird mit " string::npos + 1" angegeben und es wird keine Länge angegeben: Es string::nposwird dokumentiert, dass der Wert "= " lautet -1, sodass 0=> der Anfang der Zeichenfolge und der Standardwert für substrdie Länge für npos=> für "nur Dateiname" zu cplusplus.com/reference gilt / string / string / substr cplusplus.com/reference/string/string/npos
jave.web
5

C ++ 11-Variante (inspiriert von James Kanzes Version) mit einheitlicher Initialisierung und anonymem Inline-Lambda.

std::string basename(const std::string& pathname)
{
    return {std::find_if(pathname.rbegin(), pathname.rend(),
                         [](char c) { return c == '/'; }).base(),
            pathname.end()};
}

Die Dateierweiterung wird jedoch nicht entfernt.

alveko
quelle
Kurz und bündig, obwohl es nur mit Nicht-Windows-Pfaden funktioniert.
Volomike
Sie können Lambda Return jederzeit ändern return c == '/' || c == '\\';, damit es unter Windows funktioniert
ziomq1991
Um Pfade wie "", "///" und "dir1 / dir2 /" zu verarbeiten, fügen Sie vor der obigen return-Anweisung den folgenden Code hinzu (vgl. POSIX basename ()): if (pathname.size() == 0) return "."; auto iter = pathname.rbegin(); auto rend = pathname.rend(); while (iter != rend && *iter == '/') ++iter; if (iter == rend) /* pathname has only path separators */ return "/"; pathname = std::string(pathname.begin(), iter.base());
Gidfiddle
5

Die boost filesystemBibliothek ist auch als experimental/filesystemBibliothek verfügbar und wurde in ISO C ++ für C ++ 17 zusammengeführt. Sie können es so verwenden:

#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

int main () {
    std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}

Ausgabe:

"bar.txt"

Es funktioniert auch für std::stringObjekte.

Adam Erickson
quelle
4

Dies ist das einzige, was tatsächlich für mich funktioniert hat:

#include "Shlwapi.h"

CString some_string = "c:\\path\\hello.txt";
LPCSTR file_path = some_string.GetString();
LPCSTR filepart_c = PathFindFileName(file_path);
LPSTR filepart = LPSTR(filepart_c);
PathRemoveExtension(filepart);

ziemlich genau das, was Skrymsli vorgeschlagen hat, aber nicht mit wchar_t *, VS Enterprise 2015, funktioniert

_splitpath hat auch funktioniert, aber ich muss nicht raten, wie viele Zeichen [?] ich benötigen werde. Einige Leute brauchen diese Kontrolle wahrscheinlich, denke ich.

CString c_model_name = "c:\\path\\hello.txt";
char drive[200];
char dir[200];
char name[200];
char ext[200];
_splitpath(c_model_name, drive, dir, name, ext);

Ich glaube nicht, dass für _splitpath irgendwelche Includes benötigt wurden. Für keine dieser Lösungen wurden externe Bibliotheken (wie Boost) benötigt.

Fraktal
quelle
4
std::string getfilename(std::string path)
{
    path = path.substr(path.find_last_of("/\\") + 1);
    size_t dot_i = path.find_last_of('.');
    return path.substr(0, dot_i);
}
Beyondo
quelle
3

Ich würde es tun, indem ...

Suchen Sie vom Ende der Zeichenfolge aus rückwärts, bis Sie den ersten Schrägstrich / Schrägstrich finden.

Suchen Sie dann vom Ende der Zeichenfolge aus wieder rückwärts, bis Sie den ersten Punkt (.) Finden.

Sie haben dann den Anfang und das Ende des Dateinamens.

Simples ...

TomP89
quelle
Was für kein System funktioniert, das ich kenne. (Das eine System, das '\\'als Pfadtrennzeichen akzeptiert, wird ebenfalls verwendet '/', daher müssen Sie auch übereinstimmen.) Und ich bin mir nicht sicher, worauf Sie sich freuen würden.
James Kanze
Okay, ändern Sie es so, dass es auch passt, kein Problem. Und freue mich auf den ersten Punkt (.)
TomP89
Sie müssen immer noch den letzten Punkt finden, nicht den ersten. (Reverse Iteratoren sind dein Freund!)
James Kanze
Ah ja, guter Punkt. Für eine file.ext.ext möchten Sie also file.ext extrahieren, nicht wahr? :)
TomP89
Vermutlich. Das ist auf jeden Fall die übliche Konvention: my.source.cppwird my.source.objzum Beispiel kompiliert (wobei die Erweiterung .cppdurch ersetzt wird .obj).
James Kanze
2
m_szFilePath.MakeLower();
CFileFind finder;
DWORD buffSize = MAX_PATH;
char longPath[MAX_PATH];
DWORD result = GetLongPathName(m_szFilePath, longPath, MAX_PATH );

if( result == 0)
{
    m_bExists = FALSE;
    return;
}
m_szFilePath = CString(longPath);
m_szFilePath.Replace("/","\\");
m_szFilePath.Trim();
//check if it does not ends in \ => remove it
int length = m_szFilePath.GetLength();
if( length > 0 && m_szFilePath[length - 1] == '\\' )
{
    m_szFilePath.Truncate( length - 1 );
}
BOOL bWorking = finder.FindFile(this->m_szFilePath);
if(bWorking){
    bWorking = finder.FindNextFile();
    finder.GetCreationTime(this->m_CreationTime);
    m_szFilePath = finder.GetFilePath();
    m_szFileName = finder.GetFileName();

    this->m_szFileExtension = this->GetExtension( m_szFileName );

    m_szFileTitle = finder.GetFileTitle();
    m_szFileURL = finder.GetFileURL();
    finder.GetLastAccessTime(this->m_LastAccesTime);
    finder.GetLastWriteTime(this->m_LastWriteTime);
    m_ulFileSize = static_cast<unsigned long>(finder.GetLength());
    m_szRootDirectory = finder.GetRoot();
    m_bIsArchive = finder.IsArchived();
    m_bIsCompressed = finder.IsCompressed();
    m_bIsDirectory = finder.IsDirectory();
    m_bIsHidden = finder.IsHidden();
    m_bIsNormal = finder.IsNormal();
    m_bIsReadOnly = finder.IsReadOnly();
    m_bIsSystem = finder.IsSystem();
    m_bIsTemporary = finder.IsTemporary();
    m_bExists = TRUE;
    finder.Close();
}else{
    m_bExists = FALSE;
}

Die Variable m_szFileName enthält den Dateinamen.

Lucian
quelle
3
wow - das ist viel Code für "Dateiname abrufen" vom Pfad ... :)
Nim
4
@Nim Mein Eindruck auch. In meinem eigenen Code verwende ich einen Einzeiler : boost::filesystem::path( path ).filename().
James Kanze
Ich habe eine CFileInfo-Klasse, die diesen Code hat. Ich habe den Code hier nur abgeladen, weil er getestet wurde und ich nichts riskieren wollte ... Sie könnten in diesem Beispiel nur etwa 5 Codezeilen verwenden.
Lucian
2

Verwenden Sie nicht _splitpath()und _wsplitpath(). Sie sind nicht sicher und veraltet!

Verwenden Sie stattdessen ihre sicheren Versionen, nämlich _splitpath_s()und_wsplitpath_s()

hkBattousai
quelle
2

Das sollte auch funktionieren:

// strPath = "C:\\Dir\\File.bat" for example
std::string getFileName(const std::string& strPath)
{
    size_t iLastSeparator = 0;
    return strPath.substr((iLastSeparator = strPath.find_last_of("\\")) != std::string::npos ? iLastSeparator + 1 : 0, strPath.size() - strPath.find_last_of("."));
}

Wenn Sie es verwenden können, stellt Qt QString (mit Split, Trim usw.), QFile, QPath, QFileInfo usw. zur Verfügung, um Dateien, Dateinamen und Verzeichnisse zu bearbeiten. Und natürlich ist es auch Cross Plaftorm.

typedef
quelle
4
Verwenden Sie für die zukünftigen Leser Ihres Codes temporäre Variablen mit aussagekräftigen Namen, anstatt alles in eine einzige Codezeile zu packen (und kapseln Sie dies alles in eine Funktion getFilenameoder ähnliches).
Luc Touraille
bearbeitet. Es ging jedoch darum, es kurz zu machen, da bereits mehrere Arbeitsantworten gegeben wurden.
typedef
1
Ich denke es ist FALSCH. Sollten Sie nicht den letzten Teil ersetzen: "strPath.size () - strPath.find_last_of (". ")" Durch "strPath.find_last_of (". ") - iLastSeparator"
taktak004
@ taktak004 Sie haben Recht, es sollte "return strPath.substr ((iLastSeparator = strPath.find_last_of (" / "))! = std :: string :: npos? iLastSeparator + 1: 0, strPath.find_last_of (" "sein. ) - iLastSeparator); `
phenmod
2

Sie können das std :: -Dateisystem verwenden, um es ganz gut zu machen:

#include <filesystem>
namespace fs = std::experimental::filesystem;

fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
GuidedHacking
quelle
0

Lange suchte ich nach einer Funktion, die den Dateipfad richtig zerlegen kann. Für mich funktioniert dieser Code perfekt für Linux und Windows.

void decomposePath(const char *filePath, char *fileDir, char *fileName, char *fileExt)
{
    #if defined _WIN32
        const char *lastSeparator = strrchr(filePath, '\\');
    #else
        const char *lastSeparator = strrchr(filePath, '/');
    #endif

    const char *lastDot = strrchr(filePath, '.');
    const char *endOfPath = filePath + strlen(filePath);
    const char *startOfName = lastSeparator ? lastSeparator + 1 : filePath;
    const char *startOfExt = lastDot > startOfName ? lastDot : endOfPath;

    if(fileDir)
        _snprintf(fileDir, MAX_PATH, "%.*s", startOfName - filePath, filePath);

    if(fileName)
        _snprintf(fileName, MAX_PATH, "%.*s", startOfExt - startOfName, startOfName);

    if(fileExt)
        _snprintf(fileExt, MAX_PATH, "%s", startOfExt);
}

Beispielergebnisse sind:

[]
  fileDir:  ''
  fileName: ''
  fileExt:  ''

[.htaccess]
  fileDir:  ''
  fileName: '.htaccess'
  fileExt:  ''

[a.exe]
  fileDir:  ''
  fileName: 'a'
  fileExt:  '.exe'

[a\b.c]
  fileDir:  'a\'
  fileName: 'b'
  fileExt:  '.c'

[git-archive]
  fileDir:  ''
  fileName: 'git-archive'
  fileExt:  ''

[git-archive.exe]
  fileDir:  ''
  fileName: 'git-archive'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git-core\.htaccess]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: '.htaccess'
  fileExt:  ''

[D:\Git\mingw64\libexec\git-core\a.exe]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: 'a'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git-core\git-archive.exe]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: 'git-archive'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git.core\git-archive.exe]
  fileDir:  'D:\Git\mingw64\libexec\git.core\'
  fileName: 'git-archive'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git-core\git-archiveexe]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: 'git-archiveexe'
  fileExt:  ''

[D:\Git\mingw64\libexec\git.core\git-archiveexe]
  fileDir:  'D:\Git\mingw64\libexec\git.core\'
  fileName: 'git-archiveexe'
  fileExt:  ''

Ich hoffe das hilft dir auch :)

niemand besonderes
quelle
0

shlwapi.lib/dll verwendet die HKCU Registrierungsstruktur intern.

Es ist am besten, nicht zu verlinken shlwapi.lib herzustellen wenn Sie eine Bibliothek erstellen oder das Produkt keine Benutzeroberfläche hat. Wenn Sie eine Bibliothek schreiben, kann Ihr Code in jedem Projekt verwendet werden, einschließlich solcher, die keine Benutzeroberflächen haben.

Wenn Sie Code schreiben, der ausgeführt wird, wenn ein Benutzer nicht angemeldet ist (z. B. Dienst [oder anderer], der beim Booten oder Starten gestartet werden soll), gibt es keinen HKCU. Schließlich sind Shlwapi Siedlungsfunktionen; und als Ergebnis ganz oben auf der Liste, um in späteren Versionen von Windows veraltet zu sein.

Bach
quelle
0

Eine langsame, aber unkomplizierte Regex-Lösung:

    std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
Sava B.
quelle