Como construir uma string de caminho completo (com segurança) a partir de strings separadas?

89

O C ++ tem algum equivalente à função do python os.path.join? Basicamente, estou procurando algo que combine duas (ou mais) partes de um caminho de arquivo para que você não precise se preocupar em ter certeza de que as duas partes se encaixam perfeitamente. Se estiver no Qt, também seria legal.

Basicamente, passei uma hora depurando algum código e pelo menos parte dele foi porque root + filenametinha que ser root/ + filename, e estou procurando evitar isso no futuro.

sas4740
fonte
Possivelmente relacionado distante: stackoverflow.com/questions/5772992/… (especificamente, relacionado a essa questão é boost's complete)
Lightness Races in Orbit

Respostas:

44

Verifique o QDir para isso:

QString path = QDir(dirPath).filePath(fileName);
Stephen Chu
fonte
3
Esteja ciente de que Qt é GPL. Pode ser um problema para muitos usos.
enferrujado
2
@RustyX é LGPL, para ser mais preciso.
Pa_
97

Apenas como parte da biblioteca Boost.Filesystem . Aqui está um exemplo:

#include <iostream>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

int main ()
{
    fs::path dir ("/tmp");
    fs::path file ("foo.txt");
    fs::path full_path = dir / file;
    std::cout << full_path << std::endl;
    return 0;
}

Aqui está um exemplo de compilação e execução (específico da plataforma):

$ g++ ./test.cpp -o test -lboost_filesystem -lboost_system
$ ./test 
/tmp/foo.txt
Azeem
fonte
1
Isso também ocorre no TR2, que provavelmente começará a ser comercializado com compiladores no próximo ano.
ildjarn
1
@Vlad: Sim, não é facilmente detectável, mas odeio clicar nos links de documentos do Boost e tardiamente perceber que estou olhando para uma versão antiga, então edito os links específicos da versão das pessoas quando os encontro. :-P
ildjarn
1
@ildjarn: O que parece funcionar muito bem agora ... mas espere até que eles mudem algo sobre o site ou os documentos da biblioteca fornecida. É pior do que deixar o link específico da versão a partir de então.
Fred Nurk
1
@Fred: Obviamente, se a funcionalidade ou pergunta for específica da versão, não altero o URL. Neste caso, não é, então eu fiz.
ildjarn
1
@ildjarn: Como você prevê o que uma determinada biblioteca mudará no futuro, para saber se todas as respostas que editar farão sentido para todas as versões futuras?
Fred Nurk
24

Semelhante à resposta de @ user405725 (mas não usando boost), e mencionada por @ildjarn em um comentário, esta funcionalidade está disponível como parte de std :: filesystem . O código a seguir é compilado usando o Homebrew GCC 9.2.0_1 e usando o sinalizador --std=c++17:

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() 
{
    fs::path dir ("/tmp");
    fs::path file ("foo.txt");
    fs::path full_path = dir / file;
    std::cout << full_path << std::endl;
    return 0;
}
Shawn Blakesley
fonte
4
A partir do C ++ 17, isso foi incorporado ao cabeçalho (não experimental) <filesystem>. Consulte en.cppreference.com/w/cpp/filesystem .
Eli_B
9

Pelo menos no Unix / Linux, é sempre seguro juntar partes de um caminho por /, mesmo que algumas partes do caminho já terminem em /, ou seja, seja root/pathequivalente a root//path.

Nesse caso, tudo o que você realmente precisa é juntar as coisas /. Dito isso, concordo com outras respostas que boost::filesystemé uma boa escolha se estiver disponível para você porque oferece suporte a várias plataformas.

Frankc
fonte
2
QT é independente do separador de caminho. Se você imprimir o caminho absoluto de um arquivo no Windows, a saída será "C: /Users/Name/MyFile.txt" com o separador / (unix). boost :: filesystem é ótimo, mas, na minha opinião, se o projeto for baseado em Qt, não há necessidade de adicionar uma dependência para a biblioteca boost.
LoSciamano
7

Se você quiser fazer isso com Qt, pode usar o QFileInfoconstrutor:

QFileInfo fi( QDir("/tmp"), "file" );
QString path = fi.absoluteFilePath();
LoSciamano
fonte
4

Com C ++ 11 e Qt, você pode fazer isso:

QString join(const QString& v) {
    return v;
}

template<typename... Args>
QString join(const QString& first, Args... args) {
    return QDir(first).filePath(join(args...));
}

Uso:

QString path = join("/tmp", "dir", "file"); // /tmp/dir/file
Kainjow
fonte
3

No Qt, apenas use /em código ao usar a API Qt ( QFile, QFileInfo). Ele fará a coisa certa em todas as plataformas. Se você tiver que passar um caminho para uma função não-Qt, ou deseja formatá-lo para exibi-lo ao usuário, useQDir:toNativeSeparators() por exemplo:

QDir::toNativeSeparators( path );

Ele será substituído /pelo equivalente nativo (ou seja, \no Windows). A outra direção é feita via QDir::fromNativeSeparators().

Frank Osterfeld
fonte