从文件路径中获取目录 C++

31

如何最简单地获取文件所在的目录?我正在使用这个来查找工作目录。

string filename = "C:\MyDirectory\MyFile.bat" 

在这个示例中,我应该得到"C:\MyDirectory"。


3
宠物恼人之处:那是一个狭窄的STL字符串吗?在Windows上处理所有文件时,您应该使用Unicode字符串。 - Rup
@Rup:所以现在我们知道你更喜欢使用UTF16了。供您参考,Windows将文件名视为UTF-16字符的不透明数组。请注意,UTF-16仍然是一种可变长度字符编码;它实际上并没有比UTF-8带来太多优势。如果您为了实现的简单性而辩称UCS-2(固定长度字符),我会理解的,但反过来,Windows也会将其视为UCS-16。 - sehe
@sehe 除了当它解析路径组件的文件名时,显然是例外的 :-p 我的观点是:1)不要使用不能表示所有字符的代码页,因为你很容易会遇到麻烦;2)从 UI 输入的内容被传递为 UTF-16,来自文件系统 API 的名称被传递为 UTF-16,你从注册表等处读取的文件名也被传递为 UTF-16,并且所有文件系统 API 都接受 UTF-16 字符:很少有好理由使往返转换为 UTF-8。这只会给你自己带来麻烦,如果你定义你的 API 只接受 UTF-8 文件名,那么你会给我带来麻烦。 - Rup
1
亲爱的同事们,为什么要重复造轮子呢?使用已经被测试过并由可能知道自己在做什么的人编写的库。 - Dmitry Ledentsov
编辑尝试被拒绝了。Nubok,如果您看到这条消息,请不要通过编辑更正问题 ,并且请不要从答案中删除重要信息 - Kyle Strand
显示剩余4条评论
13个回答

28

这个初始化是不正确的,因为你需要转义反斜杠:

string filename = "C:\\MyDirectory\\MyFile.bat";

如果存在,则提取目录:

string directory;
const size_t last_slash_idx = filename.rfind('\\');
if (std::string::npos != last_slash_idx)
{
    directory = filename.substr(0, last_slash_idx);
}

对于此示例,我应该得到的答案仅限于此。为了得到正确的解决方案,应使用路径处理库或操作系统调用。 - Dmitry Ledentsov
5
如果文件名(合法地)使用正斜杠,则失败。如果文件名是相对路径,则无法获取(绝对)目录。此外,它还依赖于编码被正确设置。当有Offirmo的(可移植的)Boost解决方案时,我不会将其标记为“正确”。 - DevSolar

26

简述:

请注意,因为在Windows上它可以作为替代路径分隔符,所以您必须同时搜索/

#include <string>
#include <iostream>

std::string dirnameOf(const std::string& fname)
{
     size_t pos = fname.find_last_of("\\/");
     return (std::string::npos == pos)
         ? ""
         : fname.substr(0, pos);
}

int main(int argc, const char *argv[])
{
     const std::string fname = "C:\\MyDirectory\\MyFile.bat";

     std::cout << dirnameOf(fname) << std::endl;
}

20

2
并不是每个人都使用boost。 - Craig B
据我所知,当路径仅为文件名时(即路径为空),此方法无法正常工作。 - einpoklum

14

C++17提供了std::filesystem::path。在C++11中可能需要链接-lstdc++fs才能使用该函数。请注意,该函数不会验证路径是否存在;使用std::filesystem::status确定文件类型(可能是filetype::notfound)。


很好的回答,如果一个人正在使用c++17。从我所看到的,这是它最终被采纳的地方。 - Craig B

13

MFC 的方式;

#include <afx.h>

CString GetContainingFolder(CString &file)
{
    CFileFind fileFind;
    fileFind.FindFile(file);
    fileFind.FindNextFile();
    return fileFind.GetRoot();
}

或者,更简单的方法是

CString path(L"C:\\my\\path\\document.txt");
path.Truncate(path.ReverseFind('\\'));

第一个例子需要文件存在。 - OneWorld
没有CString.Truncate方法。https://msdn.microsoft.com/ja-jp/library/ms908314.aspx - OneWorld
1
@OneWorld 很有趣。不确定我在哪里找到的。CString::Left怎么样?https://msdn.microsoft.com/zh-cn/library/ms928948.aspx - Steztric
OP并没有询问MFC,而是特别提到了C++。 - Craig B
@CraigB MFC是C++的一个库。 - Steztric

9

C++17引入了std::filesystem::parent_path方法,可以用于获取文件路径中的上级目录:

#include <filesystem>
#include <iostream>

int main() {
    std::string filename = "C:\\MyDirectory\\MyFile.bat";
    std::string directory = std::filesystem::path(filename).parent_path().u8string();
    std::cout << directory << std::endl;
}

6

虽然这个问题比较老,但我想添加一个答案以便于对其他人有所帮助。
在Visual C++中,您可以使用CString或字符数组。

CString filename = _T("C:\\MyDirectory\\MyFile.bat");  
PathRemoveFileSpec(filename); 

输出:

C:\MyDirectory

在您的头文件中包含Shlwapi.h

MSDN链接,这里您可以查看示例。


这段代码无法编译。请查看Andrew Kozlov的答案。 - OneWorld
很多C++代码运行在没有CString的系统上。 - Craig B
这并不是函数用法的最佳示例。它接受一个可变的字符数组指针作为输入参数,ATL CStrings恰好可以隐式转换为该类型,但该函数也可用于任何C风格的字符串。BOOL PathRemoveFileSpec(char* pszPath) - undefined

5

鉴于缺乏类似C#的dirname函数,这绝对是最佳方法,因为它不依赖于路径格式。 - Dana

5

以下是一个非常简单的跨平台方案(从string::find_last_of这个示例进行了调整):

std::string GetDirectory (const std::string& path)
{
    size_t found = path.find_last_of("/\\");
    return(path.substr(0, found));
}

这适用于斜杠可以是向后或向前指向(或混合)的两种情况,因为它只是在字符串 path 中查找任一最后出现的斜杠。

然而,我个人更喜欢使用 Boost::Filesystem 库来处理此类操作。以下是一个示例:

std::string GetDirectory (const std::string& path)
{
    boost::filesystem::path p(path);
    return(p.parent_path().string());
}

虽然知道从字符串获取目录路径是你唯一需要的功能,Boost库可能有点过于臃肿(特别是因为Boost::Filesystem是为数不多的非头文件的Boost库之一)。不过,据我所知,Boost::Filesystem已经被批准包含在TR2标准中,但可能要等到C++14或C++17标准(基于 这篇文章 的说法)才能完全使用,所以根据你的编译器(以及你目前读到的时间),你可能不再需要单独编译它们,因为它们可能已经随你的系统一起提供了。例如,Visual Studio 2012已经可以使用某些TR2文件系统组件(根据 这篇文章 的说法),不过我还没试过,因为我仍在使用Visual Studio 2010...


1
这是适用于winapi的正确解决方案:
CString csFolder = _T("c:\temp\file.ext");
PathRemoveFileSpec(csFolder.GetBuffer(0));
csFolder.ReleaseBuffer(-1);

此函数已被弃用。我们建议使用 PathCchRemoveFileSpec 函数替代它。 - YePhIcK
OP没有提到winapi。 - Craig B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接