Quel est le moyen le plus simple d’obtenir le nom de fichier à partir d’un chemin?
string 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 simplement faire quelque chose comme ça
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;
Une solution possible:
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);
}
La solution la plus simple consiste à utiliser quelque chose comme boost::filesystem
. Si Pour une raison quelconque, ce n'est pas une option ...
Cela correctement nécessitera un code dépendant du système: sousWindows, '\\'
ou '/'
peut être un séparateur de chemin; sous Unix, seul '/'
fonctionne, et sous d’autres systèmes, qui sait. La solution évidente Serait quelque chose comme:
std::string
basename( std::string const& pathname )
{
return std::string(
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: Il m'a manqué le fait qu'il voulait aussi supprimer l'extension . Pour cela, c'est toujours pareil:
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 );
}
Le code est un peu plus complexe, car dans ce cas, la base de L’itérateur inversé est du mauvais côté de l’endroit où nous voulons couper . (Rappelez-vous que la base d’un itérateur inversé est derrière le caractère l'indicateur pointe vers.) 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 Je dirais que vous devriez renvoyer le nom de fichier complet, ce qui nécessiterait un peu peu de code supplémentaire pour saisir le cas spécial.) }
La tâche est assez simple car le nom du fichier de base est simplement la partie de la chaîne commençant par le dernier délimiteur pour les dossiers:
std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
Si l'extension doit également être supprimée, la seule chose à faire est de trouver le dernier .
et d'emporter une substr
à ce stade.
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
Peut-être faudrait-il vérifier si les fichiers ne contiennent que des extensions (c.-à-d. .bashrc
...)
Si vous divisez cela en plusieurs fonctions, vous êtes en mesure de réutiliser les tâches simples:
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;
}
Le code est modélisé pour pouvoir être utilisé avec différentes instances de std::basic_string
(c'est-à-dire std::string
& std::wstring
...)
L'inconvénient de la modélisation est la nécessité de spécifier le paramètre de modèle si un const char *
est transmis aux fonctions.
Donc vous pouvez soit:
std::string
au lieu de modéliser le codestd::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\\") + 1);
}
std::string
(en tant qu'intermédiaires qui seront probablement en ligne/optimisés)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;
Impressions
MyFile
Vous pouvez également utiliser les API de chemin d'accès au 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'analyse de chemin d'accès et prennent en compte les chemins UNC, les barres obliques et autres éléments étranges.
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
L'inconvénient est que vous devez créer un lien vers shlwapi.lib, mais je ne sais pas vraiment pourquoi c'est un inconvénient.
Si vous pouvez utiliser boost,
#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();
C'est tout.
Je vous recommande d'utiliser boost library. Boost vous offre beaucoup de commodités lorsque vous travaillez avec C++. Il supporte presque toutes les plateformes . Si vous utilisez Ubuntu, vous pouvez installer la bibliothèque boost avec une seule ligne Sudo apt-get install libboost-all-dev
(réf. Comment installer boost sur Ubuntu? )
Une fonction:
#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");
}
La manière la 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 <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::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';
}
sortie:
MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
Réf: cppreference
À partir de documents C++ - 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;
}
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 en ligne anonyme.
std::string basename(const std::string& pathname)
{
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; }).base(),
pathname.end()};
}
Cela ne supprime toutefois pas l'extension du fichier.
La bibliothèque boost
filesystem
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 <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
int main () {
std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}
Sortie:
"bar.txt"
Cela fonctionne aussi pour les objets std::string
.
c'est la seule chose qui a finalement fonctionné pour moi:
#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);
à 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 devoir deviner combien de caractères [?] il me faut. Certaines personnes ont probablement besoin de ce contrôle, je suppose.
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);
Je ne crois pas que des inclus étaient nécessaires pour _splitpath. Aucune bibliothèque externe (comme boost) n’était nécessaire pour l’une ou l’autre de ces solutions.
Je le ferais par ...
Effectuez une recherche en arrière à partir de la fin de la chaîne jusqu'à la première barre oblique inversée/inversée.
Recherchez ensuite à nouveau à partir de 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 du fichier.
Simples ...
Cela devrait fonctionner aussi:
// 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("."));
}
Si vous pouvez l'utiliser, Qt fournira QString (avec split, trim etc.), QFile, QPath, QFileInfo etc. pour manipuler des fichiers, des noms de fichiers et des répertoires. Et bien sûr, c'est aussi un dépassement de temps.
Ne pas utiliser _splitpath()
et _wsplitpath()
. Ils ne sont pas en sécurité et ils sont obsolètes!
Utilisez plutôt leurs versions sûres, à savoir _splitpath_s()
et _wsplitpath_s()
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;
}
La variable m_szFileName contient le nom de fichier.
Pendant longtemps, je recherchais 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);
}
Voici des exemples de résultats:
[]
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: ''
J'espère que cela vous aide aussi :)
shlwapi.lib/dll
utilise la ruche de registre HKCU
en interne.
Il est préférable de ne pas créer de lien vers shlwapi.lib
si vous créez une bibliothèque ou si le produit ne dispose pas d'une interface utilisateur. Si vous écrivez une bibliothèque, votre code peut être utilisé dans n'importe quel projet, y compris ceux qui ne possèdent pas d'interface utilisateur.
Si vous écrivez du code qui s'exécute lorsqu'un utilisateur n'est pas connecté (par exemple, service [ou autre] configuré pour démarrer au démarrage ou au démarrage), il n'y a pas de variable HKCU
. Enfin, les shlwapi sont des fonctions d'établissement; et par conséquent en haut de la liste pour décourager les versions ultérieures de Windows.
Vous pouvez utiliser le système de fichiers std :: pour le faire très bien:
#include <filesystem>
namespace fs = std::experimental::filesystem;
fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
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);
}