有没有C++等效的getcwd函数?

47

我通过以下命令查看 C 语言的 getcwd 函数:

man 3 cwd

我怀疑 C++ 也有类似的函数,可以返回 std::string 类型的结果。

如果有的话,它叫什么名字?在哪里可以找到它的文档?

谢谢!


4
为什么不直接使用std::string cwd = getcwd();并让构造函数完成它的工作? - LiraNuna
6
如果你不释放getcwd()返回的内存,它会泄漏吗?如果是这样,那么你应该在创建字符串后释放它,而不是在不再需要它时释放,这更方便。如果不是这样,那么初始化字符串将不会泄漏内存。 - David Thornley
4
我可以证实,使用valgrind检测后发现,字符串 cwd = getcwd(NULL, 0); 存在内存泄漏问题! - Natalie Adams
如果buf为NULL,则getcwd()使用malloc(3)动态分配缓冲区。 - Gediminas
8个回答

42

尽管你已经接受了一个答案,但我还是要回答。

比包装getcwd调用更好的方法是使用boost::filesystem,在那里你可以从current_path()函数获得一个path对象。Boost文件系统库允许你做很多有用的事情,否则你需要大量的字符串解析来完成,例如检查文件/目录是否存在、获取父路径、使路径完整等等。看看它吧,它也是可移植的 - 而大部分你可能会用到的字符串解析代码可能不是。

更新(2016):文件系统已经作为技术规范于2015年发布,基于Boost Filesystem v3。这意味着它可能已经与你的编译器一起发布了(例如Visual Studio 2015)。对我而言,它似乎也有可能成为未来C++标准的一部分(我认为是C++17,但我不清楚当前的情况)。

更新(2017):文件系统库已经在C++17中与ISO C++合并,为。

std::filesystem::current_path();

3
当然,在某些情况下,仅为获取当前工作目录而包含boost::filesystem库可能不值得。但是,如果他/她正在进行大量的文件系统操作,那么最好全部使用boost。 - ctrlc-root
27
为什么每个答案都是“boost::this”和“boost::that”? - TheRealChx101
1
@chx101:我认为Boost并不总是最好的选择。但在某些情况下,使用已经存在且能够让你的生活更轻松的东西通常是一个好主意。请注意,现在在C++标准中的一些东西来自于Boost,并且据我所知,Boost文件系统将被列入技术规范。 - villintehaspam
6
我这么说的原因是每当有人问如何使用API实现某个任务时,答案总是:使用这个库。如果你正在进行一个大型项目,并且有时间去熟悉新的库,那么这很有道理,但是如果只是想获取当前工作目录,难道要为此下载一个库吗?第三方库只会让调试应用程序变得更加困难,而且像这种情况下只会增加不必要的代码,导致文件变得臃肿。 - TheRealChx101
@chx101:如果我理解正确,你的意思是说为了获取当前工作目录而抓取一个(大型)第三方库是不必要的。在某种程度上,我同意这一点,但关键是你不太可能只对目录感兴趣——你会用它来做一些事情,比如操作文件路径。与其编写自己的代码,很可能会出现错误和不可移植性,不如使用已经存在且可行的东西。Boost 对于某些用例来说可能过于庞大,但你不需要全部获取。 - villintehaspam
显示剩余7条评论

30

std::string的构造函数可以安全地接受char*作为参数。令人惊讶的是,Windows 版本也可以。

编辑:实际上这还有点复杂:

std::string get_working_path()
{
   char temp[MAXPATHLEN];
   return ( getcwd(temp, sizeof(temp)) ? std::string( temp ) : std::string("") );
}

内存不是问题--temp是一个基于栈的缓冲区,而std::string构造函数会进行一次复制。可能你可以一次完成,但我认为标准并不能保证这一点。

关于内存分配,通过POSIX:

getcwd()函数将当前工作目录的绝对路径名放置在由buf指向的数组中,并返回buf。复制到数组中的路径名不应包含任何符号链接组件。size参数是buf参数所指字符数组的字节数。如果buf是空指针,则getcwd()的行为是未指定的。


2
这个 char * 指针会自动释放吗?还是会导致内存泄漏? - anon
2
这个答案和Liranuna的评论都使用了getcwd作为无参数函数,但是我看到的文档显示有两个参数。我是否阅读了错误的文档? - Rob Kennedy
1
@Bill,就我所知,那是一个非标准扩展。Linux、Mac和Windows都实现了它,这可能是“足够可移植”的,也可能不是。 - Rob Kennedy
2
@Bill,POSIX规范明确指出数据是复制的,而不是分配的,因此不需要释放内存。 - Kornel Kisielewicz
抱歉,我之前看的是没有参数的版本,而后来编辑更改为带有缓冲区的版本。如果您使用没有参数的版本(或非标准版本,如果给定了NULL缓冲区,则进行分配),则需要使用free函数。 - Bill
显示剩余5条评论

13

让我们尝试将这个简单的C调用重写为C++:

std::string get_working_path()
{
    char temp [ PATH_MAX ];

    if ( getcwd(temp, PATH_MAX) != 0) 
        return std::string ( temp );

    int error = errno;

    switch ( error ) {
        // EINVAL can't happen - size argument > 0

        // PATH_MAX includes the terminating nul, 
        // so ERANGE should not be returned

        case EACCES:
            throw std::runtime_error("Access denied");

        case ENOMEM:
            // I'm not sure whether this can happen or not 
            throw std::runtime_error("Insufficient storage");

        default: {
            std::ostringstream str;
            str << "Unrecognised error" << error;
            throw std::runtime_error(str.str());
        }
    }
}

问题在于,当你将一个库函数包装在另一个函数中时,必须假定所有功能都应该暴露,因为库不知道谁会调用它。所以,你必须处理错误情况而不是仅仅忽略它们或希望它们不会发生。
通常最好让客户端代码直接调用库函数,并在那一点上处理错误——客户端代码可能并不关心错误发生的原因,所以只需要处理通过/失败的情况,而不是所有错误代码。

1
有一个打字错误。第五行应该是 != 0 而不是 == 0 - Phantrast
1
C++是一种拥有所有你不需要的特性的语言,而你需要实现所有你需要的特性。 - Bersan

9

你只需要编写一个小包装器。

std::string getcwd_string( void ) {
   char buff[PATH_MAX];
   getcwd( buff, PATH_MAX );
   std::string cwd( buff );
   return cwd;
}

也许可以尝试使用以下代码来获取当前工作目录的字符串形式:std::string getcwd_string( void ) { string ret(PATH_MAX,0); getcwd( &ret[0], PATH_MAX ); ret.resize(ret.find_first_of('\0',0)); return ret; }需要避免使用VLA(变长数组),因为它在C++标准中并不是一个正式的部分,尽管大多数编译器都将其作为语言扩展进行支持。 - hanshenrik
你必须检查从getcwd返回的nullptr(NULL)。 - Doncho Gunchev

4

我在C语言中使用getcwd()函数的方式如下:

char * cwd;
cwd = (char*) malloc( FILENAME_MAX * sizeof(char) );
getcwd(cwd,FILENAME_MAX);

需要的头文件是stdio.h。 当我使用C编译器时,它工作得很好。
如果我使用C++编译器编译完全相同的代码,则会报告以下错误信息:
identifier "getcwd" is undefined

然后我包含了unistd.h并使用C++编译器进行编译。 这次,一切都可以使用。 当我切换回C编译器时,它仍然可以正常工作!

只要同时包含stdio.hunistd.h,上述代码就适用于C和C++编译器。


在两个编译器中验证后,+1 再进行更改后重新验证。 - Jesse Chisholm
请使用 PATH_MAX 而不是 FILENAME_MAX - Amir Fo

3

所有的C函数也是C++函数。如果你需要一个std::string,只需从getcwd获取的char*创建一个即可。


1
我还使用了如上另一个答案中所述的boost::filesystem。我只想补充一点,由于current_path()函数不返回std::string,您需要进行转换。
这是我所做的:
std::string cwd = boost::filesystem::current_path().generic_string();

-3
你可以创建一个新的函数,我更喜欢这种方式而不是链接到像boost这样的库(除非你已经在使用)。
 std::string getcwd()
 {
     char* buff;//automatically cleaned when it exits scope
     return std::string(getcwd(buff,255));
 }

2
buff指向未初始化的内存,如果你幸运的话会导致段错误。 - MarcD
糟糕!是的,分配内存。 - Cody Serino

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