Obtener un nombre de directorio a partir de un nombre de archivo

85

Tengo un nombre de archivo (C: \ carpeta \ foo.txt) y necesito recuperar el nombre de la carpeta (C: \ carpeta) en C ++ no administrado. En C # haría algo como esto:

string folder = new FileInfo("C:\folder\foo.txt").DirectoryName;

¿Existe una función que se pueda usar en C ++ no administrado para extraer la ruta del nombre del archivo?

Jon Tackabury
fuente

Respuestas:

20

Hay una función estándar de Windows para esto, PathRemoveFileSpec . Si solo es compatible con Windows 8 y versiones posteriores, se recomienda encarecidamente utilizar PathCchRemoveFileSpec en su lugar. Entre otras mejoras, ya no se limita a MAX_PATH(260) caracteres.

Andreas Rejbrand
fuente
2
Tenga en cuenta que esta función ahora está obsoleta. La sugerencia de Microsoft es utilizar PathCchRemoveFileSpec en su lugar.
defecto el
1
@Default: PathCchRemoveFileSpec solo está disponible a partir de Windows 8. Dado que Windows Vista y 7 todavía son compatibles, también lo es PathRemoveFileSpec .
Inspectable
153

Usando Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
AraK
fuente
2
p.remove_filename()se modificará pen el lugar y puede implementarse de manera más eficiente quep = p.parent_path()
Peter Cordes
Si también puede trabajar con directorios, tenga en cuenta el hecho de que parent_path()de "C:\\folder"resultará en "C:".
Semjon Mössinger
mucho impulso se actualiza a std así que también intente esto .... incluir <filesystem> .... std :: experimental :: filesystem :: path p ("C: \\ folder \\ foo.txt");
S Meaden
72

Ejemplo de http://www.cplusplus.com/reference/string/string/find_last_of/

// string::find_last_of
#include <iostream>
#include <string>
using namespace std;

void SplitFilename (const string& str)
{
  size_t found;
  cout << "Splitting: " << str << endl;
  found=str.find_last_of("/\\");
  cout << " folder: " << str.substr(0,found) << endl;
  cout << " file: " << str.substr(found+1) << endl;
}

int main ()
{
  string str1 ("/usr/bin/man");
  string str2 ("c:\\windows\\winhelp.exe");

  SplitFilename (str1);
  SplitFilename (str2);

  return 0;
}
corsiKa
fuente
1
Esta es la mejor solución mínima aquí.
plasmacel
39

En C ++ 17 existe una clase que std::filesystem::pathusa el método parent_path.

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
    for(fs::path p : {"/var/tmp/example.txt", "/", "/var/tmp/."})
        std::cout << "The parent path of " << p
                  << " is " << p.parent_path() << '\n';
}

Salida posible:

The parent path of "/var/tmp/example.txt" is "/var/tmp"
The parent path of "/" is ""
The parent path of "/var/tmp/." is "/var/tmp"
Alessandro Jacopson
fuente
2
También existe un .remove_filename()método.
Qqwy
1
Gracias, @Qqwy, también permite usar la ruta del directorio con ese método para obtener resultados correctos y esperados a diferencia del enfoque de la respuesta
Herrgott
13

¿Por qué tiene que ser tan complicado?

#include <windows.h>

int main(int argc, char** argv)         // argv[0] = C:\dev\test.exe
{
    char *p = strrchr(argv[0], '\\');
    if(p) p[0] = 0;

    printf(argv[0]);                    // argv[0] = C:\dev
}
toster-cx
fuente
10
Esto no es portátil. El separador de ruta en Linux es '/'. std :: filesystem :: path es estándar y portátil.
Rémi
7
 auto p = boost::filesystem::path("test/folder/file.txt");
 std::cout << p.parent_path() << '\n';             // test/folder
 std::cout << p.parent_path().filename() << '\n';  // folder
 std::cout << p.filename() << '\n';                // file.txt

Es posible que deba p.parent_path().filename()obtener el nombre de la carpeta principal.

srbcheema1
fuente
5

Utilice boost :: filesystem. Se incorporará en el siguiente estándar de todos modos, por lo que es mejor que se acostumbre a él.

Edward extraño
fuente
1
¿De qué estándar estás hablando? Sé que se agregaron muchas cosas de boost a C ++ std lib, ¿también se agregará el sistema de archivos?
McLeary
7
"Se incorporará en el próximo estándar de todos modos" Y no lo es
Anton K
@AntonK tal vez C ++ 2017?
Alessandro Jacopson
6
@AlessandroJacopson Genial, parece incluido en C ++ 17 - en.cppreference.com/w/cpp/filesystem
Anton K
1

Estoy tan sorprendido de que nadie haya mencionado la forma estándar en Posix

Utilice basename / dirnameconstrucciones.

hombre nombre de base

Utkarsh Kumar
fuente
Las funciones POSIX no están exentas de inconvenientes. En particular, pueden modificar el búfer que pasa, (realmente quieren decir que la firma es basname(char * path)y no basename(const char * path)), y las implementaciones que no lo hacen parecen tener que usar un búfer estático que los hace inseguros para subprocesos (en principio, usted también podría devolver resultados asignados dinámicamente, pero eso lo hace dependiente de allocfunciones familiares, lo cual es incómodo en C ++).
dmckee --- ex-moderador gatito
-1

C ++ estándar no hará mucho por usted en este sentido, ya que los nombres de ruta son específicos de la plataforma. Puede analizar manualmente la cadena (como en la respuesta de glowcoder), usar las funciones del sistema operativo (por ejemplo, http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ), o probablemente el mejor enfoque, puede utilizar una biblioteca de sistema de archivos de terceros como boost :: filesystem.

Rueda dentada
fuente
El C ++ 1z del estándar está intentando adoptar la biblioteca del sistema de archivos boost a partir de ahora, haciendo que la compatibilidad con la plataforma sea un problema mucho menor. Todavía está en los encabezados experimentales de MSVC, al menos.
kayleeFrye_onDeck
-6

Simplemente use esto: ExtractFilePath (your_path_file_name)

fxPiotr
fuente
5
Creo que este es un método Delphi, no algo en C ++.
Ian Hunter