Obter um nome de diretório a partir de um nome de arquivo

85

Eu tenho um nome de arquivo (C: \ pasta \ foo.txt) e preciso recuperar o nome da pasta (C: \ pasta) em C ++ não gerenciado. Em C #, eu faria algo assim:

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

Existe uma função que pode ser usada em C ++ não gerenciado para extrair o caminho do nome do arquivo?

Jon Tackabury
fonte

Respostas:

20

Existe uma função padrão do Windows para isso, PathRemoveFileSpec . Se você só oferece suporte ao Windows 8 e posterior, é altamente recomendável usar PathCchRemoveFileSpec . Entre outras melhorias, ele não está mais limitado a MAX_PATH(260) caracteres.

Andreas Rejbrand
fonte
2
Observe que esta função agora está obsoleta. A sugestão da Microsoft é usar PathCchRemoveFileSpec .
Padrão
1
@Default: PathCchRemoveFileSpec está disponível apenas a partir do Windows 8. Como o Windows Vista e o 7 ainda são compatíveis, PathRemoveFileSpec também o é .
Inesperável
154

Usando Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
AraK
fonte
2
p.remove_filename()modificará pno local e pode ser implementado com mais eficiência do quep = p.parent_path()
Peter Cordes
Se você também lida com diretórios, saiba que parent_path()de "C:\\folder"resultará em "C:".
Semjon Mössinger
muitos boost são atualizados para std, então tente também ... inclua <filesystem> .... std :: experimental :: filesystem :: path p ("C: \\ folder \\ foo.txt");
S Meaden
72

Exemplo 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
fonte
1
Esta é a melhor solução mínima aqui.
plasmacel
42

Em C ++ 17 existe uma classe std::filesystem::pathusando o 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';
}

Resultado possível:

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
fonte
3
Também existe um .remove_filename()método.
Qqwy
1
Obrigado, @Qqwy, também permite usar o caminho do diretório com esse método para obter resultados corretos e esperados, ao contrário da abordagem da resposta
Herrgott,
13

Por que tem que ser tão 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
fonte
10
Isso não é portátil. O separador de caminho no Linux é '/'. std :: filesystem :: path é padrão e 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

Você pode precisar p.parent_path().filename()obter o nome da pasta pai.

srbcheema1
fonte
5

Use boost :: filesystem. Ele será incorporado ao próximo padrão de qualquer maneira, então você também pode se acostumar com ele.

Edward Strange
fonte
1
De qual padrão você está falando? Eu sei que muitas coisas do boost foram adicionadas à lib std C ++, o sistema de arquivos também será adicionado?
McLeary
7
"Será incorporado ao próximo padrão de qualquer maneira" E não é
Anton K
@AntonK, talvez C ++ 2017?
Alessandro Jacopson
6
@AlessandroJacopson Cool, parece incluído no C ++ 17 - en.cppreference.com/w/cpp/filesystem
Anton K
1

_splitpath é uma boa solução CRT.

Ofek Shilon
fonte
1

Estou tão surpreso que ninguém mencionou a forma padrão em Posix

Por favor, use basename / dirname construções.

man basename

Utkarsh Kumar
fonte
1
As funções POSIX têm suas desvantagens. Em particular, eles podem modificar o buffer que você passa (eles realmente significam que a assinatura é basname(char * path)e não basename(const char * path)), e as implementações que não fazem isso parecem ter que usar um buffer estático que os torna inseguros para thread (em princípio, você também pode retornar resultados alocados dinamicamente, mas isso o torna dependente de allocfunções familiares, o que é estranho em C ++).
dmckee --- ex-moderador gatinho
-1

O C ++ padrão não fará muito por você a esse respeito, pois os nomes de caminho são específicos da plataforma. Você pode analisar manualmente a string (como na resposta do código de luz), usar os recursos do sistema operacional (por exemplo, http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ), ou provavelmente o melhor abordagem, você pode usar uma biblioteca de sistema de arquivos de terceiros como boost :: filesystem.

Roda dentada
fonte
O C ++ 1z do padrão está atualmente tentando adotar a biblioteca do sistema de arquivos boost, tornando a compatibilidade com a plataforma um problema muito menor. Ainda está nos cabeçalhos experimentais do MSVC, pelo menos.
kayleeFrye_onDeck
-6

Basta usar este: ExtractFilePath (your_path_file_name)

fxPiotr
fonte
5
Eu acredito que este é um método Delphi, não algo em C ++.
Ian Hunter