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.
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.
Antworten:
_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;
_splitpathan 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 ist boost::filesystem.
_splitpathin der stdlib.hKopie 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);
}
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:
std::stringanstatt den Code als Vorlage zu verwendenstd::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\\") + 1);
}
std::string(als Zwischenprodukte, die wahrscheinlich wegliniert / optimiert werden)inline std::string string_base_name(std::string const & path)
{
return base_name(path);
}
const char *.std::string base = base_name<std::string>("some/path/file.ext");
std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;
Druckt
MyFile
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() );
}
, 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.)}
string::find_last_ofanstatt 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 lernen std::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.
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? )
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
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");
}
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
find_last_ofzurückgegeben wird, string::nposwenn nichts gefunden wurde.
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
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.
return c == '/' || c == '\\';, damit es unter Windows funktioniert
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());
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.
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.
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 ...
'\\'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.cppwird my.source.objzum Beispiel kompiliert (wobei die Erweiterung .cppdurch 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.
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()
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.
getFilenameoder ä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();
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 :)
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.