Obtenir un nom de fichier à partir d’un chemin

Quelle est la manière la plus simple d’obtenir le nom de fichier d’un chemin?

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

Dans cet exemple, je devrais obtenir “MyFile”. sans extension.

_splitpath devrait faire ce dont vous avez besoin. Vous pouvez bien sûr le faire manuellement, mais _splitpath gère également tous les cas particuliers.

MODIFIER:

Comme BillHoag l’a mentionné, il est recommandé d’utiliser la version plus sûre de _splitpath appelée _splitpath_s lorsqu’elle est disponible.

Ou si vous voulez quelque chose de portable, vous pouvez faire quelque chose comme ça

 std::vector splitpath( const std::ssortingng& str , const std::set delimiters) { std::vector result; char const* pch = str.c_str(); char const* start = pch; for(; *pch; ++pch) { if (delimiters.find(*pch) != delimiters.end()) { if (start != pch) { std::ssortingng str(start, pch); result.push_back(str); } else { result.push_back(""); } start = pch + 1; } } result.push_back(start); return result; } ... std::set delims{'\\'}; std::vector path = splitpath("C:\\MyDirectory\\MyFile.bat", delims); cout << path.back() << endl; 

La solution la plus simple consiste à utiliser quelque chose comme boost::filesystem . Si pour une raison quelconque, ce n’est pas une option …

Cela nécessite un code dépendant du système: sous Windows, '\\' ou '/' peut être un séparateur de chemin; sous Unix, seulement '/' fonctionne, et sous d’autres systèmes, qui sait. La solution évidente serait quelque chose comme:

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

, MatchPathSeparator étant défini dans un en-tête dépendant du système soit:

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

pour Unix ou:

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

pour Windows (ou quelque chose de différent pour un autre système inconnu).

EDIT: J’ai manqué le fait qu’il voulait aussi supprimer l’extension. Pour cela, plus de la même chose:

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

Le code est un peu plus complexe, car dans ce cas, la base de l’iterator inversé est du mauvais côté de l’endroit où l’on veut couper. (Rappelez-vous que la base d’un iterator inversé est derrière le caractère sur lequel l’iterator pointe.) Et même ceci est un peu douteux: je n’aime pas le fait qu’il puisse retourner une chaîne vide, par exemple. (Si le seul '.' Est le premier caractère du nom de fichier, je pense que vous devriez retourner le nom de fichier complet. Cela nécessiterait un peu de code supplémentaire pour attraper le cas particulier.)}

Une solution possible:

 ssortingng 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::ssortingng::npos != last_slash_idx) { filename.erase(0, last_slash_idx + 1); } // Remove extension if present. const size_t period_idx = filename.rfind('.'); if (std::ssortingng::npos != period_idx) { filename.erase(period_idx); } 

La tâche est assez simple car le nom de fichier de base n’est que la partie de la chaîne commençant au dernier séparateur pour les dossiers:

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

Si l’extension doit être supprimée, la seule chose à faire est de trouver la dernière . et prendre un substr à ce point

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

Peut-être devrait-il y avoir une vérification pour faire face à des fichiers uniquement constitués d’extensions (ex: .bashrc …)

Si vous divisez cela en fonctions séparées, vous êtes capable de réutiliser les tâches individuelles:

 template T base_name(T const & path, T const & delims = "/\\") { return path.substr(path.find_last_of(delims) + 1); } template 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; } 

Le code est conçu pour pouvoir être utilisé avec différentes instances de std::basic_ssortingng (par exemple std::ssortingng & std::wssortingng …)

L’inconvénient de la méthode est la nécessité de spécifier le paramètre template si un caractère const char * est transmis aux fonctions.

Donc, vous pouvez soit:

A) Utilisez uniquement std::ssortingng au lieu de modéliser le code

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

B) Fournir une fonction d’ std::ssortingng utilisant std::ssortingng (en tant qu’intermédiaires susceptibles d’être intégrés / optimisés)

 inline std::ssortingng ssortingng_base_name(std::ssortingng const & path) { return base_name(path); } 

C) Spécifiez le paramètre de modèle lors de l’appel avec const char * .

 std::ssortingng base = base_name("some/path/file.ext"); 

Résultat

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

Des tirages

 MyFile 

Vous pouvez également utiliser les API de chemin de shell PathFindFileName, PathRemoveExtension. Probablement pire que _splitpath pour ce problème particulier, mais ces API sont très utiles pour toutes sortes de travaux d’parsing de chemin et prennent en compte les chemins UNC, les barres obliques et autres trucs étranges.

 wssortingng 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

L’inconvénient est que vous devez créer un lien vers shlwapi.lib, mais je ne suis pas sûr de savoir pourquoi c’est un inconvénient.

Si vous pouvez utiliser boost,

 #include  path p("C:\\MyDirectory\\MyFile.bat"); ssortingng basename = p.filename().ssortingng(); //or //ssortingng basename = path("C:\\MyDirectory\\MyFile.bat").filename().ssortingng(); 

C’est tout.

Je vous recommande d’utiliser la librairie boost. Boost vous offre beaucoup de commodités lorsque vous travaillez avec C ++. Il supporte presque toutes les plates-formes. Si vous utilisez Ubuntu, vous ne pouvez installer la bibliothèque boost que par une seule ligne: sudo apt-get install libboost-all-dev (réf. Comment installer boost sur Ubuntu? )

Fonction:

 #include  std::ssortingng basename(const std::ssortingng &filename) { if (filename.empty()) { return {}; } auto len = filename.length(); auto index = filename.find_last_of("/\\"); if (index == std::ssortingng::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::ssortingng::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  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"); } 

De C ++ Docs – ssortingng :: find_last_of

 #include  // std::cout #include  // std::ssortingng void SplitFilename (const std::ssortingng& 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; } 

Les sorties:

 Splitting: /usr/bin/man path: /usr/bin file: man Splitting: c:\windows\winhelp.exe path: c:\windows file: winhelp.exe 

Variante C ++ 11 (inspirée de la version de James Kanze) avec initialisation uniforme et lambda inline anonyme.

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

Il ne supprime cependant pas l’extension de fichier.

c’est la seule chose qui a finalement fonctionné pour moi:

 #include "Shlwapi.h" CSsortingng some_ssortingng = "c:\\path\\hello.txt"; LPCSTR file_path = some_ssortingng.GetSsortingng(); LPCSTR filepart_c = PathFindFileName(file_path); LPSTR filepart = LPSTR(filepart_c); PathRemoveExtension(filepart); 

à peu près ce que Skrymsli a suggéré mais ne fonctionne pas avec wchar_t *, VS Enterprise 2015

_splitpath a également fonctionné, mais je n’aime pas avoir à deviner combien de caractères de char [?] je vais avoir besoin; certaines personnes ont probablement besoin de ce contrôle, je suppose.

 CSsortingng 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); 

Je ne crois pas que les inclusions étaient nécessaires pour _splitpath. Aucune bibliothèque externe (comme boost) n’était nécessaire pour l’une de ces solutions.

Je le ferais par …

Recherche en arrière à partir de la fin de la chaîne jusqu’à ce que vous trouviez la première barre oblique / barre oblique inverse.

Ensuite, recherchez à nouveau la fin de la chaîne jusqu’à ce que vous trouviez le premier point (.)

Vous avez alors le début et la fin du nom de fichier.

Simples …

Le moyen le plus simple dans cpp17 est:

utilisez le #include experimental / filesystem et le nom de fichier () pour le nom de fichier avec extension et stem () sans extension.

  #include  #include  namespace fs = std::experimental::filesystem; int main() { ssortingng 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'; } 

sortie:

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

Ref: cppreference

La bibliothèque du filesystem boost est également disponible en tant que bibliothèque experimental/filesystem et a été fusionnée dans ISO C ++ pour C ++ 17. Vous pouvez l’utiliser comme ceci:

 #include  #include  namespace fs = std::experimental::filesystem; int main () { std::cout << fs::path("/foo/bar.txt").filename() << '\n' } 

Sortie:

 "bar.txt" 

Cela fonctionne aussi pour std::ssortingng objects std::ssortingng .

 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 = CSsortingng(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(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; } 

La variable m_szFileName contient le nom de fichier.

_splitpath() pas _splitpath() et _wsplitpath() . Ils ne sont pas en sécurité et ils sont obsolètes!

Utilisez plutôt leurs versions sécurisées, à savoir _splitpath_s() et _wsplitpath_s()

Cela devrait fonctionner aussi:

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

Si vous pouvez l’utiliser, Qt fournira QSsortingng (avec split, sortingm etc), QFile, QPath, QFileInfo etc. pour manipuler des fichiers, des noms de fichiers et des répertoires. Et bien sûr, c’est aussi une plate-forme croisée.

Pendant longtemps, je cherchais une fonction capable de décomposer correctement le chemin du fichier. Pour moi, ce code fonctionne parfaitement pour Linux et 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); } 

Les exemples de résultats sont:

 [] fileDir: '' fileName: '' fileExt: '' [.htaccess] fileDir: '' fileName: '.htaccess' fileExt: '' [a.exe] fileDir: '' fileName: 'a' fileExt: '.exe' [a\bc] 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: '' 

J’espère que cela vous aide aussi 🙂