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.
c++
visual-c++
Nidhal
quelle
quelle
Antworten:
_splitpath sollte das tun, was Sie brauchen. Sie können dies natürlich manuell tun, aber auch
_splitpath
alle Sonderfälle.BEARBEITEN:
Wie BillHoag erwähnt wird empfohlen , die sicherere Version zu verwenden
_splitpath
genannt _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;
quelle
_splitpath
an der ein auf meiner Maschine enthält.<stdlib.h>
. In Bezug auf die Portabilität können Sie vielleicht einige Beispiele für „perfekt gute tragbare Lösungen“ auflisten?<stdlib.h>
. Und die naheliegende tragbare Lösung istboost::filesystem
._splitpath
in derstdlib.h
Kopie von VS? Dann möchten Sie möglicherweise eine Reparaturinstallation von VS durchführen.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); }
quelle
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
.
undsubstr
bis zu diesem Punkt gehenstd::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_string
Instanzen 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::string
anstatt den Code als Vorlage zu verwendenstd::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
quelle
base_name
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() ); }
,
MatchPathSeparator
wobei 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.)}quelle
string::find_last_of
anstatt der Manipulation von Reverse Iteratoren?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 lernenstd::string
.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.
quelle
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? )quelle
Der einfachste Weg in C ++ 17 ist:
Verwenden Sie das
#include <filesystem>
undfilename()
für den Dateinamen mit undstem()
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
quelle
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"); }
quelle
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:
quelle
find_last_of
zurückgegeben wird,string::npos
wenn nichts gefunden wurde.string::npos
muss nicht getan werden , wegen der Art und Weise , wie diese undstring::substr
umgesetzt werden. a)string::npos
wird als "Länge" übergeben =>substr
hat das Leseverhalten bis zum Ende dokumentiert. b)substr
wird mit "string::npos + 1
" angegeben und es wird keine Länge angegeben: Esstring::npos
wird dokumentiert, dass der Wert "= " lautet-1
, sodass0
=> der Anfang der Zeichenfolge und der Standardwert fürsubstr
die Länge fürnpos
=> für "nur Dateiname" zu cplusplus.com/reference gilt / string / string / substr cplusplus.com/reference/string/string/nposC ++ 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.
quelle
return c == '/' || c == '\\';
, damit es unter Windows funktioniertif (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());
Die
boost
filesystem
Bibliothek ist auch alsexperimental/filesystem
Bibliothek 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::string
Objekte.quelle
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.
quelle
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); }
quelle
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 ...
quelle
'\\'
als Pfadtrennzeichen akzeptiert, wird ebenfalls verwendet'/'
, daher müssen Sie auch übereinstimmen.) Und ich bin mir nicht sicher, worauf Sie sich freuen würden.my.source.cpp
wirdmy.source.obj
zum Beispiel kompiliert (wobei die Erweiterung.cpp
durch ersetzt wird.obj
).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.
quelle
boost::filesystem::path( path ).filename()
.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()
quelle
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.
quelle
getFilename
oder ähnliches).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();
quelle
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 :)
quelle
shlwapi.lib/dll
verwendet dieHKCU
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.quelle
Eine langsame, aber unkomplizierte Regex-Lösung:
std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
quelle