¿Cuál es la forma más sencilla de obtener el nombre de archivo de una ruta?
string filename = "C:\\MyDirectory\\MyFile.bat"
En este ejemplo, debería obtener "MyFile". sin extensión.
c++
visual-c++
nidhal
fuente
fuente
Respuestas:
_splitpath debería hacer lo que necesita. Por supuesto, puede hacerlo manualmente, pero también
_splitpath
maneja todos los casos especiales.EDITAR:
Como mencionó BillHoag, se recomienda usar la versión más segura de
_splitpath
llamada _splitpath_s cuando esté disponible.O si quieres algo portátil, puedes hacer algo como esto
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;
fuente
_splitpath
ninguna de las inclusiones en mi máquina.<stdlib.h>
. En cuanto a la portabilidad, ¿quizás pueda enumerar algunos ejemplos de "soluciones portátiles perfectamente buenas"?<stdlib.h>
. Y la solución portátil más obvia esboost::filesystem
._splitpath
enstdlib.h
tu copia de VS? Entonces es posible que desee realizar una instalación de reparación de VS.Una posible solución:
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); }
fuente
La tarea es bastante simple ya que el nombre del archivo base es solo la parte de la cadena que comienza en el último delimitador de las carpetas:
std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
Si la extensión también debe eliminarse, lo único que debe hacer es encontrar la última
.
y llevarsubstr
a este puntostd::string::size_type const p(base_filename.find_last_of('.')); std::string file_without_extension = base_filename.substr(0, p);
Quizás debería haber una verificación para hacer frente a los archivos que constan únicamente de extensiones (es decir
.bashrc
...)Si divide esto en funciones separadas, puede reutilizar las tareas individuales:
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; }
El código está diseñado para poder usarlo con diferentes
std::basic_string
instancias (es decir,std::string
&std::wstring
...)La desventaja de la plantilla es el requisito de especificar el parámetro de plantilla si
const char *
se pasa a las funciones.Entonces podrías:
A) Use solo en
std::string
lugar de crear una plantilla del códigostd::string base_name(std::string const & path) { return path.substr(path.find_last_of("/\\") + 1); }
B) Proporcione la función de envoltura usando
std::string
(como intermedios que probablemente estarán en línea / optimizados)inline std::string string_base_name(std::string const & path) { return base_name(path); }
C) Especifique el parámetro de plantilla al llamar con
const char *
.std::string base = base_name<std::string>("some/path/file.ext");
Resultado
std::string filepath = "C:\\MyDirectory\\MyFile.bat"; std::cout << remove_extension(base_name(filepath)) << std::endl;
Huellas dactilares
fuente
base_name
La solución más simple es usar algo como
boost::filesystem
. Si por alguna razón esta no es una opción ...Hacer esto correctamente requerirá algún código dependiente del sistema: en Windows,
'\\'
o'/'
puede ser un separador de ruta; bajo Unix, solo'/'
funciona, y bajo otros sistemas, quién sabe. La solución obvia sería algo como:std::string basename( std::string const& pathname ) { return std::string( std::find_if( pathname.rbegin(), pathname.rend(), MatchPathSeparator() ).base(), pathname.end() ); }
,
MatchPathSeparator
definiéndose en un encabezado dependiente del sistema como:struct MatchPathSeparator { bool operator()( char ch ) const { return ch == '/'; } };
para Unix, o:
struct MatchPathSeparator { bool operator()( char ch ) const { return ch == '\\' || ch == '/'; } };
para Windows (o algo aún diferente para algún otro sistema desconocido).
EDITAR: Me perdí el hecho de que él también quería suprimir la extensión. Por eso, más de lo mismo:
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 ); }
El código es un poco más complejo, porque en este caso, la base del iterador inverso está en el lado equivocado de donde queremos cortar. (Recuerde que la base de un iterador inverso está detrás del carácter al que apunta el iterador). E incluso esto es un poco dudoso: no me gusta el hecho de que pueda devolver una cadena vacía, por ejemplo. (Si el único
'.'
es el primer carácter del nombre de archivo, yo diría que debería devolver el nombre de archivo completo. Esto requeriría un poco de código adicional para detectar el caso especial.)}fuente
string::find_last_of
lugar de manipular iteradores inversos?string
, por lo que debe aprenderlos de todos modos. Y habiéndolos aprendido, no hay razón para molestarse en aprender toda la interfaz hinchadastd::string
.También puede utilizar las API de ruta de shell PathFindFileName, PathRemoveExtension. Probablemente sea peor que _splitpath para este problema en particular, pero esas API son muy útiles para todo tipo de trabajos de análisis de rutas y tienen en cuenta las rutas UNC, barras diagonales y otras cosas raras.
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
El inconveniente es que tienes que vincular a shlwapi.lib, pero no estoy muy seguro de por qué eso es un inconveniente.
fuente
Si puedes usar 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();
Esto es todo.
Te recomiendo que uses la biblioteca boost. Boost le brinda muchas comodidades cuando trabaja con C ++. Es compatible con casi todas las plataformas. Si usa Ubuntu, puede instalar la biblioteca boost en una sola línea
sudo apt-get install libboost-all-dev
(ref. ¿Cómo instalar boost en Ubuntu? )fuente
La forma más sencilla en C ++ 17 es:
utilice
#include <filesystem>
yfilename()
para el nombre de archivo con extensión ystem()
sin extensión.#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'; }
salida:
MyFile.bat MyFile "bar.txt" ".bar" "." "." ".." "." ".." "/"
Referencia: cppreference
fuente
Función:
#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); }
Pruebas:
#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"); }
fuente
Desde C ++ Docs - 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; }
Salidas:
fuente
find_last_of
regresastring::npos
si no se encontró nada.string::npos
no es necesario realizar la comprobación debido a la forma en questring::substr
se implementan. a)string::npos
se pasa como "longitud" =>substr
tiene un comportamiento documentado de leer todo hasta el final. b)substr
se le da "string::npos + 1
" y no tiene longitud:string::npos
se documenta que tiene un valor de-1
, por lo que se evalúa como0
=> inicio de la cadena y el valor predeterminado de las longitudessubstr
esnpos
=> también funciona en "solo nombre de archivo" cplusplus.com/reference / string / string / substr cplusplus.com/reference/string/string/nposVariante de C ++ 11 (inspirada en la versión de James Kanze) con inicialización uniforme y lambda en línea anónimo.
std::string basename(const std::string& pathname) { return {std::find_if(pathname.rbegin(), pathname.rend(), [](char c) { return c == '/'; }).base(), pathname.end()}; }
Sin embargo, no elimina la extensión del archivo.
fuente
return c == '/' || c == '\\';
que funcione en Windowsif (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());
La
boost
filesystem
biblioteca también está disponible comoexperimental/filesystem
biblioteca y se fusionó con ISO C ++ para C ++ 17. Puedes usarlo así:#include <iostream> #include <experimental/filesystem> namespace fs = std::experimental::filesystem; int main () { std::cout << fs::path("/foo/bar.txt").filename() << '\n' }
Salida:
"bar.txt"
También funciona para
std::string
objetos.fuente
esto es lo único que finalmente funcionó para mí:
#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);
más o menos lo que sugirió Skrymsli pero no funciona con wchar_t *, VS Enterprise 2015
_splitpath también funcionó, pero no me gusta tener que adivinar cuántos caracteres char [?] voy a necesitar; algunas personas probablemente necesitan este control, supongo.
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);
No creo que se necesitara ninguna inclusión para _splitpath. No se necesitaron bibliotecas externas (como boost) para ninguna de estas soluciones.
fuente
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); }
fuente
Lo haría por ...
Busque hacia atrás desde el final de la cadena hasta que encuentre la primera barra invertida / barra inclinada hacia adelante.
Luego busque hacia atrás nuevamente desde el final de la cadena hasta que encuentre el primer punto (.)
Luego tiene el inicio y el final del nombre del archivo.
Simples ...
fuente
'\\'
como separador de ruta también lo usa'/'
, por lo que debe hacer coincidir cualquiera de los dos). Y no estoy seguro de lo que estaría esperando.my.source.cpp
se compila enmy.source.obj
, por ejemplo (con la extensión.cpp
reemplazada por.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; }
La variable m_szFileName contiene el nombre de archivo.
fuente
boost::filesystem::path( path ).filename()
.No use
_splitpath()
y_wsplitpath()
. ¡No son seguros y están obsoletos!En su lugar, utilice sus versiones seguras, a saber
_splitpath_s()
y_wsplitpath_s()
fuente
Esto también debería funcionar:
// 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 puede usarlo, Qt proporciona QString (con split, trim, etc.), QFile, QPath, QFileInfo, etc. para manipular archivos, nombres de archivos y directorios. Y, por supuesto, también es una plataforma cruzada.
fuente
getFilename
o algo así).Puede usar el sistema de archivos std :: para hacerlo bastante bien:
#include <filesystem> namespace fs = std::experimental::filesystem; fs::path myFilePath("C:\\MyDirectory\\MyFile.bat"); fs::path filename = myFilePath.stem();
fuente
Durante mucho tiempo estuve buscando una función capaz de descomponer correctamente la ruta del archivo. Para mí, este código funciona perfectamente tanto para Linux como para 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); }
Los resultados de ejemplo son:
[] 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: ''
Espero que esto también te ayude :)
fuente
shlwapi.lib/dll
utiliza elHKCU
subárbol del registro internamente.Es mejor no vincularlo
shlwapi.lib
si está creando una biblioteca o si el producto no tiene una interfaz de usuario. Si está escribiendo una biblioteca, su código se puede usar en cualquier proyecto, incluidos aquellos que no tienen IU.Si está escribiendo código que se ejecuta cuando un usuario no está conectado (por ejemplo, servicio [u otro] configurado para iniciarse en el inicio o inicio), entonces no hay
HKCU
. Por último, shlwapi son funciones de liquidación; y como resultado alto en la lista para desaprobar en versiones posteriores de Windows.fuente
Una solución de expresiones regulares lenta pero sencilla:
std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
fuente