Вот как я делаю это в библиотеке, которую я строю поверх файловой системы boost:
Шаг 1: Определите «самый глубокий общий корень».По сути, это как наибольший общий знаменатель для 2 путей.Например, если у вас 2 пути: «C: \ a \ b \ c \ d» и «C: \ a \ b \ c \ l.txt», то общим корнем, который они оба используют, является «C: \ a».\ b \ c \ ".
Чтобы получить это, преобразуйте оба пути в абсолютную, НЕ каноническую форму (вы захотите сделать это для умозрительных путей и символических ссылок).
Шаг 2. Чтобы перейти от A к B, необходимо добавить суффикс A с достаточным количеством копий «../», чтобы переместить дерево каталогов к общему корню, а затем добавить строку для B, чтобы перейти к нему по дереву.В Windows вы можете иметь 2 пути без общего корня, поэтому переход от любого A к любому B не всегда возможен.
namespace fs = boost::filesystem;
bool GetCommonRoot(const fs::path& path1,
const fs::path& path2,
fs::path& routeFrom1To2,
std::vector<fs::path>& commonDirsInOrder)
{
fs::path pathA( fs::absolute( path1));
fs::path pathB( fs::absolute( path2));
// Parse both paths into vectors of tokens. I call them "dir" because they'll
// be the common directories unless both paths are the exact same file.
// I also Remove the "." and ".." paths as part of the loops
fs::path::iterator iter;
std::vector<fs::path> dirsA;
std::vector<fs::path> dirsB;
for(iter = pathA.begin(); iter != pathA.end(); ++iter) {
std::string token = (*iter).string();
if(token.compare("..") == 0) { // Go up 1 level => Pop vector
dirsA.pop_back();
}
else if(token.compare(".") != 0) { // "." means "this dir" => ignore it
dirsA.push_back( *iter);
}
}
for(iter = pathB.begin(); iter != pathB.end(); ++iter) {
std::string token = (*iter).string();
if(token.compare("..") == 0) { // Go up 1 level => Pop vector
dirsB.pop_back();
}
else if(token.compare(".") != 0) { // "." means "this dir" => ignore it
dirsB.push_back( *iter);
}
}
// Determine how far to check in each directory set
size_t commonDepth = std::min<int>( dirsA.size(), dirsB.size());
if(!commonDepth) {
// They don't even share a common root- no way from A to B
return false;
}
// Match entries in the 2 vectors until we see a divergence
commonDirsInOrder.clear();
for(size_t i=0; i<commonDepth; ++i) {
if(dirsA[i].string().compare( dirsB[i].string()) != 0) { // Diverged
break;
}
commonDirsInOrder.push_back( dirsA[i]); // I could use dirsB too.
}
// Now determine route: start with A
routeFrom1To2.clear();
for(size_t i=0; i<commonDepth; ++i) {
routeFrom1To2 /= dirsA[i];
}
size_t backupSteps = dirsA.size() - commonDepth; // # of "up dir" moves we need
for(size_t i=0; i<backupSteps; ++i) {
routeFrom1To2 /= "../";
}
// Append B's path to go down to it from the common root
for(size_t i=commonDepth; i<dirsB.size(); ++i) {
routeFrom1To2 /= dirsB[i]; // ensures absolutely correct subdirs
}
return true;
}
Это будет делать то, что вы хотите - вы идетевверх от A до тех пор, пока вы не нажмете на общую папку, и B оба являются потомками, затем перейдите к B. Возможно, вам не нужен возврат "commonDirsInOrder", который у меня есть, но возвращение "routeFrom1To2" - это то, что вызапрашивая.
Если вы планируете фактически изменить рабочий каталог на «B», вы можете напрямую использовать «routeFrom1To2».Имейте в виду, что эта функция создаст абсолютный путь, несмотря на все части "..", но это не должно быть проблемой.