¿Cómo construir una cadena de ruta completa (de forma segura) a partir de cadenas separadas?

91

¿C ++ tiene algún equivalente a la función de Python os.path.join? Básicamente, estoy buscando algo que combine dos (o más) partes de la ruta de un archivo para que no tenga que preocuparse por asegurarse de que las dos partes encajen perfectamente. Si está en Qt, también sería genial.

Básicamente, pasé una hora depurando algo de código y al menos parte de eso fue porque root + filenametenía que ser así root/ + filename, y estoy buscando evitar eso en el futuro.

sas4740
fuente
Posiblemente relacionado lejanamente: stackoverflow.com/questions/5772992/… (específicamente, relacionado con esa pregunta es boost complete)
Lightness Races in Orbit

Respuestas:

45

Echa un vistazo a QDir para eso:

QString path = QDir(dirPath).filePath(fileName);
Stephen Chu
fuente
3
Tenga en cuenta que Qt es GPL. Podría ser un factor decisivo para muchos usos.
rustyx
4
@RustyX es LGPL, para ser precisos.
Pa_
99

Solo como parte de la biblioteca Boost.Filesystem . Aquí hay un ejemplo:

#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;
}

Aquí hay un ejemplo de compilación y ejecución (específico de la plataforma):

$ g++ ./test.cpp -o test -lboost_filesystem -lboost_system
$ ./test 
/tmp/foo.txt
Azeem
fuente
1
Esto también está en TR2, que probablemente comenzará a distribuirse con compiladores el próximo año.
ildjarn
1
@Vlad: Sí, no es fácil de descubrir, pero odio hacer clic en los enlaces de documentos de Boost y darme cuenta tardíamente de que estoy viendo una versión antigua, así que edito los enlaces específicos de la versión de las personas cuando los encuentro. :-P
Ildjarn
1
@ildjarn: Lo que parece funcionar muy bien ahora ... pero espere hasta que cambien algo sobre el sitio o los documentos de la biblioteca dada. Es peor que dejar el enlace específico de la versión a partir de ese momento.
Fred Nurk
1
@Fred: Obviamente, si la funcionalidad o la pregunta son específicas de la versión, no cambio la URL. En este caso, no lo es, así que lo hice.
ildjarn
1
@ildjarn: ¿Cómo puede predecir lo que cambiará una biblioteca determinada en el futuro para saber si todas las respuestas que edite tendrán sentido para todas las versiones futuras?
Fred Nurk
24

Similar a la respuesta de @ user405725 (pero sin usar boost), y mencionada por @ildjarn en un comentario, esta funcionalidad está disponible como parte de std :: filesystem . El siguiente código se compila usando Homebrew GCC 9.2.0_1 y usando la bandera --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
fuente
4
A partir de C ++ 17, esto se fusionó en el encabezado <filesystem> (no experimental). Consulte en.cppreference.com/w/cpp/filesystem .
Eli_B
9

Al menos en Unix / Linux, siempre es seguro unir partes de una ruta por /, incluso si algunas partes de la ruta ya terminan en /, root/pathes decir, es equivalente a root//path.

En este caso, todo lo que realmente necesita es unir cosas /. Dicho esto, estoy de acuerdo con otras respuestas que boost::filesystemes una buena opción si está disponible para usted porque es compatible con múltiples plataformas.

Frankc
fuente
2
QT es independiente del separador de ruta. Si imprime la ruta absoluta de un archivo en Windows, la salida es "C: /Users/Name/MyFile.txt" con el separador / (unix). boost :: filesystem es genial pero, en mi opinión, si el proyecto está basado en Qt, no es necesario agregar una dependencia para la biblioteca boost.
LoSciamano
7

Si desea hacer esto con Qt, puede usar el QFileInfoconstructor:

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

Con C ++ 11 y Qt puede hacer esto:

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
fuente
3

En Qt, solo utilícelo /en el código cuando use Qt API ( QFile, QFileInfo). Hará lo correcto en todas las plataformas. Si tiene que pasar una ruta a una función que no es de Qt, o desea formatearla para mostrarla al usuario, use, QDir:toNativeSeparators()por ejemplo:

QDir::toNativeSeparators( path );

Reemplazará /por el equivalente nativo (es decir, \en Windows). La otra dirección se hace vía QDir::fromNativeSeparators().

Frank Osterfeld
fuente