Quelle est la façon la plus simple d'obtenir le nom du fichier à partir d'un chemin ?
string filename = "C:\\MyDirectory\\MyFile.bat"
Dans cet exemple, je devrais obtenir "MonFichier". sans extension.
Quelle est la façon la plus simple d'obtenir le nom du fichier à partir d'un chemin ?
string filename = "C:\\MyDirectory\\MyFile.bat"
Dans cet exemple, je devrais obtenir "MonFichier". sans extension.
La tâche est assez simple puisque le nom de fichier de base est juste la partie de la chaîne qui commence au dernier délimiteur pour les dossiers :
std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
Si l'extension doit être supprimée également, la seule chose à faire est de trouver la dernière .
et prendre un 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 qu'il devrait y avoir une vérification pour faire face aux fichiers constitués uniquement d'extensions (c'est à dire .bashrc
...)
Si vous divisez cette tâche en plusieurs fonctions distinctes, vous pourrez réutiliser les tâches individuelles :
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 l'utiliser avec différents types d'applications. std::basic_string
(c'est-à-dire std::string
& std::wstring
...)
L'inconvénient du modèle est la nécessité de spécifier le paramètre du 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 inlined / optimisé loin)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;
Imprimés
MyFile
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 méthode la plus simple en C++17 est :
utiliser le #include <filesystem>
y filename()
pour un nom de fichier avec extension et stem()
sans extension.
#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';
}
sortie :
MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
Référence : Référence cpp
La solution la plus simple est d'utiliser quelque chose comme boost::filesystem
. Si pour une raison quelconque, ce n'est pas une option...
Pour effectuer cette opération correctement, il faudra utiliser un code dépendant du système : sous Windows, soit '\\'
o '/'
peut être un séparateur de chemin ; sous Unix, seulement '/'
fonctionne, et sous d'autres systèmes, qui sait. La solution évidente solution 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() );
}
avec MatchPathSeparator
étant défini dans un en-tête dépendant du système soit comme :
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). inconnu).
EDIT : J'ai oublié qu'il voulait aussi supprimer l'extension. Pour cela, plus de la même chose :
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 inverse est sur la mauvaise position. l'itérateur inverse est du mauvais côté de l'endroit où nous voulons couper. (Rappelez-vous que la base d'un itérateur inversé est un derrière le caractère vers lequel l'itérateur pointe). Et même cela est un peu douteux : je n'aime pas le fait que t 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 soutiens que que vous devriez retourner le nom de fichier complet. Cela nécessiterait un peu un peu de code supplémentaire pour attraper le cas spécial). }
_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.
EDITAR:
Comme BillHoag l'a mentionné, il est recommandé d'utiliser la version la plus sûre de _splitpath
appelé _splitpath_s lorsqu'il est disponible.
Ou si vous voulez quelque chose de portable, vous pouvez simplement faire quelque chose comme ceci
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;
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
2 votes
Rechercher à partir de l'arrière jusqu'à ce que vous frappiez un retour arrière ?
2 votes
@KerrekSB, vous voulez dire barre oblique inversée ;)
0 votes
J'ai un std::string qui contient le chemin d'un fichier "c : \\MyDirectory\\Myfile.pdf "Je dois renommer ce fichier en monfichier_md.pdf et je dois donc obtenir le nom du fichier à partir du chemin.
1 votes
Si vous devez faire beaucoup de travail avec les chemins de fichiers, pensez à utiliser Boost FileSystem. boost.org/doc/libs/release/libs/filesystem/v3/doc/index.htm
2 votes
@Nim : Oui ! J'ai dû perdre le fil...