¿Cómo puedo extraer el nombre del archivo y la extensión de una ruta en C++?

8 minutos de lectura

avatar de usuario
octaviano

Tengo una lista de archivos almacenados en un .log en esta sintaxis:

c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg

Quiero extraer el nombre y la extensión de estos archivos. ¿Puedes dar un ejemplo de una manera simple de hacer esto?

Para extraer un nombre de archivo sin extensión, use boost::filesystem::path::provenir en lugar de feo std::string::find_last_of(“.”)

boost::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext
std::cout << "filename only          : " << p.stem() << std::endl;     // file

  • Acordado. Responde a la pregunta de la manera más sucinta.

    – Andy Reino Unido

    28 mayo 2014 a las 12:43

  • En realidad, p.filename() es del tipo ruta, y estará entre comillas cuando se convierta implícitamente, por lo que obtendrá: nombre de archivo y extensión: “file.ext” Es posible que desee p.filename().string() en su lugar.

    –James Hirschorn

    18 de febrero de 2016 a las 19:54


  • Con C++14/C++17 puedes usar std::experimental::filesystem resp. std::filesystem. Vea la publicación de Yuchen Zhong a continuación.

    – Roi Dantón

    12 de abril de 2017 a las 14:15


  • El autor de la pregunta quería una forma sencilla. Agregar impulso a un proyecto solo para esta funcionalidad no es una forma simple. std::filesystem es la forma más sencilla.

    – KKlouzal

    30 de noviembre de 2019 a las 13:26

  • C++17 incluyó en la biblioteca estándar. Use un compilador nuevo… o importe boost.

    –Nicolay Merkin

    2 de diciembre de 2019 a las 16:29


avatar de usuario
Yuchen

Para C++17:

#include <filesystem>

std::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext"
std::cout << "filename only: " << p.stem() << std::endl;              // "file"

Referencia sobre el sistema de archivos: http://en.cppreference.com/w/cpp/filesystem


Como lo sugiere @RoiDantopara el formato de salida, std::out puede rodear la salida con citas, por ejemplo:

filename and extension: "file.ext"

Puedes convertir std::filesystem::path a std::string por p.filename().string() si eso es lo que necesitas, por ejemplo:

filename and extension: file.ext

  • Hola @RoiDanton, ¡gracias por la edición! Acabo de comprobar de nuevo el código de ejemplo en el enlace de referencia, no parece que sea necesario convertir el tipo de retorno de std::filesystem::path a std::string para poder usar std::cout. en.cppreference.com/w/cpp/filesystem/path/filename Pero si piensas lo contrario, no dudes en comentar o editar la publicación nuevamente.

    – Yuchen

    12 de abril de 2017 a las 14:39

  • Eso es cierto, std::cout puede confiar en la conversión implícita. Sin embargo, dado que los comentarios posteriores std::cout decir archivo.ext y archivo, ya sea .string() debe agregarse a los comentarios o deben ser “archivo.ext” y “archivo”. Con Visual C++, de hecho, no hay diferencia (incluso sin string() la salida está sin comillas), pero con gcc 6.1 la salida está entre comillas si .string() se omite. Ver coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3

    – Roi Dantón

    12 de abril de 2017 a las 14:56


  • @RoiDanton, oye, esa es una idea interesante. Actualizaré la publicación nuevamente. ¡Gracias por compartir esto!

    – Yuchen

    12 de abril de 2017 a las 15:21

Si desea una forma segura (es decir, portátil entre plataformas y sin suposiciones en el camino), le recomiendo usar boost::filesystem.

Se vería de alguna manera así:

boost::filesystem::path my_path( filename );

Luego puede extraer varios datos de esta ruta. Aquí está la documentación del objeto de ruta.


Por cierto: también recuerda que para usar una ruta como

c:\foto\foto2003\shadow.gif

tienes que escapar de la \ en un literal de cadena:

const char* filename = "c:\\foto\\foto2003\\shadow.gif";

O usar / en cambio:

const char* filename = "c:/foto/foto2003/shadow.gif";

Esto solo se aplica a la especificación de cadenas literales en "" comillas, el problema no existe cuando carga rutas desde un archivo.

  • +1 Definitivamente el camino a seguir. El ejemplo en el sitio principal da una forma de buscar un directorio: Use el método path.extension() para buscar registros (vea boost.org/doc/libs/1_36_0/libs/filesystem/doc/index.htm)

    – Tomás

    13 de diciembre de 2010 a las 16:18


  • De hecho, en la mayoría de los casos, este es el camino a seguir, sin embargo, implica agregar en algunos casos una dependencia no deseada en una biblioteca externa. Si desea trabajar solo con lo que proporciona el estándar C ++, le sugiero que consulte la expresión regular de C ++, donde puede definir una expresión regular para hacer lo que quiera (muchos ejemplos en Internet). La ventaja: sin gastos generales debido a algunas dependencias adicionales. Sin embargo, esto también deja una pregunta abierta: ¿se requiere multiplataforma? Boost se encarga del estilo de la ruta sin importar si está usando Windows o Linux. Usando expresiones regulares, tienes que hacerlo por tu cuenta.

    – rbaleksandar

    11 de julio de 2014 a las 20:13


avatar de usuario
Sylvain Defresne

Tendrá que leer sus nombres de archivo del archivo en std::string. Puede utilizar el operador de extracción de cadenas de std::ostream. Una vez que tenga su nombre de archivo en un std::stringpuedes usar el std::string::find_last_of método para encontrar el último separador.

Algo como esto:

std::ifstream input("file.log");
while (input)
{
    std::string path;
    input >> path;

    size_t sep = path.find_last_of("\\/");
    if (sep != std::string::npos)
        path = path.substr(sep + 1, path.size() - sep - 1);

    size_t dot = path.find_last_of(".");
    if (dot != std::string::npos)
    {
        std::string name = path.substr(0, dot);
        std::string ext  = path.substr(dot, path.size() - dot);
    }
    else
    {
        std::string name = path;
        std::string ext  = "";
    }
}

No es el código, pero aquí está la idea:

  1. Leer un std::string del flujo de entrada (std::ifstream), cada instancia leída será la ruta completa
  2. hacer un find_last_of en la cuerda para el \
  3. Extraiga una subcadena desde esta posición hasta el final, esto ahora le dará el nombre del archivo
  4. hacer un find_last_of por .y una subcadena a cada lado le dará nombre + extensión.

  • Y -1 por no ser portátil 🙂

    – Cos

    13 de diciembre de 2010 a las 16:12

  • ¿Por qué el voto negativo? Si hay algo mal con lo que dije, ¡avísame y lo arreglaré!

    – Nim

    13 de diciembre de 2010 a las 16:13

  • @Kos, bueno, ¡eso es duro! coincide con lo que quiere el OP, el archivo está basado en Windows y no había ningún requisito de portabilidad.

    – Nim

    13 de diciembre de 2010 a las 16:14

  • Como mínimo, una ruta válida de Windows puede tener los directorios separados por / también. Y ni siquiera sé si existen más advertencias en las especificaciones de la ruta, por lo que mi pensamiento es simple: si hay una buena biblioteca que hace lo que quiero, debería usarla, porque probablemente logrará mi objetivo mejor que yo. 😉

    – Cos

    13 de diciembre de 2010 a las 16:17

  • @Nim, pero ¿no hay un boost::insects::disperser<T> plantilla genérica para eso? 🙂

    – Cos

    13 de diciembre de 2010 a las 17:28

El siguiente truco para extraer el nombre del archivo de una ruta de archivo sin extensión en C++ (no se requieren bibliotecas externas):

#include <iostream>
#include <string>

using std::string;

string getFileName(const string& s) {
char sep = "https://stackoverflow.com/";
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
string filename = s.substr(i+1, s.length() - i);
size_t lastindex = filename.find_last_of("."); 
string rawname = filename.substr(0, lastindex); 
return(rawname);
}

return("");
}

int main(int argc, char** argv) {

string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}

  • Y -1 por no ser portátil 🙂

    – Cos

    13 de diciembre de 2010 a las 16:12

  • ¿Por qué el voto negativo? Si hay algo mal con lo que dije, ¡avísame y lo arreglaré!

    – Nim

    13 de diciembre de 2010 a las 16:13

  • @Kos, bueno, ¡eso es duro! coincide con lo que quiere el OP, el archivo está basado en Windows y no había ningún requisito de portabilidad.

    – Nim

    13 de diciembre de 2010 a las 16:14

  • Como mínimo, una ruta válida de Windows puede tener los directorios separados por / también. Y ni siquiera sé si existen más advertencias en las especificaciones de la ruta, por lo que mi pensamiento es simple: si hay una buena biblioteca que hace lo que quiero, debería usarla, porque probablemente logrará mi objetivo mejor que yo. 😉

    – Cos

    13 de diciembre de 2010 a las 16:17

  • @Nim, pero ¿no hay un boost::insects::disperser<T> plantilla genérica para eso? 🙂

    – Cos

    13 de diciembre de 2010 a las 17:28

avatar de usuario
svanschalkwyk

También uso este fragmento de código para determinar el carácter de barra inclinada apropiado:

boost::filesystem::path slash("https://stackoverflow.com/");
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();

y luego reemplace las barras con la barra inclinada preferida para el sistema operativo. Útil si uno está implementando constantemente entre Linux/Windows.

¿Ha sido útil esta solución?