如何从C ++中的路径提取文件名和扩展名


77

我使用.log以下语法存储了一个文件列表:

c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg

我想从该文件中提取名称和扩展名。您能否举一个简单的例子来做到这一点?

Answers:


178

要提取不带扩展名的文件名,使用升压::文件系统::路径::的,而不是丑陋的std :: string :: find_last_of(“”)

boost::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext
std::cout << "filename only          : " << p.stem() << std::endl;     // file

同意 最简洁地回答问题。
AndyUK

13
实际上,p.filename()是路径类型,在隐式转换时将用引号引起来,因此您将获得:文件名和扩展名:“ file.ext”您可能需要p.filename()。string()代替。
詹姆斯·希尔斯霍恩,2016年

6
使用C ++ 14 / C ++ 17可以使用std::experimental::filesystemresp std::filesystem。请参阅下面来自Yuchen Zhong的帖子。
罗伊·丹顿

1
询问者想要一种简单的方法。单靠此功能为项目添加提升并不是一个简单的方法。std :: filesystem是简单的方法。
KKlouzal,

1
C ++ 17将<filesystem>包含在标准库中。使用新的编译器...或导入Boost。
Nickolay Merkin

31

对于C ++ 17

#include <filesystem>

std::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext"
std::cout << "filename only: " << p.stem() << std::endl;              // "file"

有关文件系统的参考:http : //en.cppreference.com/w/cpp/filesystem


正如@RoiDanto所建议的那样,对于输出格式,std::out可以用引号将输出引起来,例如:

filename and extension: "file.ext"

如果需要的话,可以转换std::filesystem::pathstd::stringby p.filename().string(),例如:

filename and extension: file.ext

嘿@RoiDanton,谢谢编辑!我刚才检查的参考链接示例代码再次,它似乎并不认为有必要的返回类型从转换std::filesystem::pathstd::string为了能够使用std::coutzh-cn.cppreference.com/w/cpp/filesystem/path/filename但是,如果您有其他看法,请随时发表评论或编辑帖子。
Yuchen Zhong

没错,std::cout可以依靠隐式转换。但是,由于std::cout说file.ext和file之后的注释,.string()必须添加到注释中,或者它们应该是“ file.ext”和“ file”。使用Visual C ++确实没有区别(即使没有string()输出也没有引号),但是对于gcc 6.1,如果.string()省略了输出,则带有引号。参见coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
罗伊·丹顿

@RoiDanton,嘿,这很有趣。我将再次更新帖子。感谢您分享!
Yuchen Zhong

19

如果您想要一种安全的方法(即在平台之间可移植并且不对路径进行假设),建议使用boost::filesystem

它看起来像这样:

boost::filesystem::path my_path( filename );

然后,您可以从该路径中提取各种数据。这是路径对象的文档。


顺便说一句:还请记住,为了使用类似

c:\foto\foto2003\shadow.gif

您需要\在字符串文字中转义:

const char* filename = "c:\\foto\\foto2003\\shadow.gif";

/改用:

const char* filename = "c:/foto/foto2003/shadow.gif";

这仅适用于在""引号中指定文字字符串,从文件加载路径时不存在此问题。


2
肯定要+1。主站点上的例子给出了一个方法来搜索目录:使用path.extension()方法来搜索日志(见boost.org/doc/libs/1_36_0/libs/filesystem/doc/index.htm
汤姆·

的确,在大多数情况下这是可行的方法,但是在某些情况下,这涉及到对外部库的不希望的依赖关系。如果您只想使用C ++标准提供的功能,建议您查看C ++ regex,在这里您可以定义一个正则表达式来执行您想要的操作(Internet上有很多示例)。优点-由于一些其他的依赖关系而没有开销。但是,这也留下了一个问题-是否需要多平台?无论您使用的是Windows还是Linux,Boost都会照顾路径样式。使用正则表达式,您必须自己做。
rbaleksandar 2014年

17

您必须从中的文件读取文件名std::string。您可以使用的字符串提取运算符std::ostream。将文件名保存在中后std::string,您可以使用该std::string::find_last_of方法查找最后一个分隔符。

像这样:

std::ifstream input("file.log");
while (input)
{
    std::string path;
    input >> path;

    size_t sep = path.find_last_of("\\/");
    if (sep != std::string::npos)
        path = path.substr(sep + 1, path.size() - sep - 1);

    size_t dot = path.find_last_of(".");
    if (dot != std::string::npos)
    {
        std::string name = path.substr(0, dot);
        std::string ext  = path.substr(dot, path.size() - dot);
    }
    else
    {
        std::string name = path;
        std::string ext  = "";
    }
}

2
不想成为聪明的驴子,但它应该是path.substr而不是path.substring,对吗?
比约恩

3

不是代码,而是这里的想法:

  1. std::string从输入流(std::ifstream)中读取一个,每个读取的实例将是完整路径
  2. find_last_of对字符串做一个\
  3. 从该位置到末尾提取一个子字符串,这将为您提供文件名
  4. 进行find_last_offor .,任一侧的子字符串都将为您提供名称+扩展名。

而-1表示不可携带:)
Kos 2010年

为什么要投反对票?如果我说的话有什么问题,请告诉我,我会解决的!
尼姆(Nim)2010年

2
@Kos,那太残酷了!它符合OP的要求,文件是基于Windows的,并且没有可移植性要求!
尼姆(Nim)2010年

至少,有效的Windows路径也可以使用目录分隔/。而且我什至不知道路径规范中是否存在更多警告,所以我的想法很简单-如果有一个很好的库可以满足我的需求,那么我应该使用它,因为它可能比我能更好地实现我的目标。;)
Kos 2010年

3
@Nim,但是没有boost::insects::disperser<T>通用模板吗?:)
Kos

0

我还使用此代码段确定适当的斜杠字符:

boost::filesystem::path slash("/");
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();

然后将斜杠替换为操作系统的首选斜杠。如果人们经常在Linux / Windows之间进行部署,则很有用。


0

对于linux或unix机器,操作系统具有两个处理路径和文件名的功能。使用man 3 basename获得有关这些功能的更多信息。使用系统提供的功能的优点是您不必安装boost或编写自己的功能。

#include <libgen.h>
       char *dirname(char *path);
       char *basename(char *path);

手册页中的示例代码:

   char *dirc, *basec, *bname, *dname;
           char *path = "/etc/passwd";

           dirc = strdup(path);
           basec = strdup(path);
           dname = dirname(dirc);
           bname = basename(basec);
           printf("dirname=%s, basename=%s\n", dname, bname);

由于basename()函数的参数类型为非const,因此在C ++代码内部使用它有点不直观。这是我代码库中的一个简单示例:

string getFileStem(const string& filePath) const {
   char* buff = new char[filePath.size()+1];
   strcpy(buff, filePath.c_str());
   string tmp = string(basename(buff));
   string::size_type i = tmp.rfind('.');
   if (i != string::npos) {
      tmp = tmp.substr(0,i);
   }
   delete[] buff;
   return tmp;
}

使用new / delete并不是很好的样式。我可以将其放入try / catch块中,以防两次调用之间发生某些情况。


0

尝试以下技巧,从c ++中没有扩展名的路径中提取文件名,而c ++中没有外部库:

#include <iostream>
#include <string>

using std::string;

string getFileName(const string& s) {
char sep = '/';
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
string filename = s.substr(i+1, s.length() - i);
size_t lastindex = filename.find_last_of("."); 
string rawname = filename.substr(0, lastindex); 
return(rawname);
}

return("");
}

int main(int argc, char** argv) {

string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}

-1

尼古拉·默金(Nickolay Merkin)和钟雨辰(Yuchen Zhong)的回答很不错,但是从评论中您可以看到它并不完全准确。

打印时隐式转换为std :: string会将文件名括在引号中。评论也不正确。

path::filename()path::stem()返回一个新的路径对象,并path::string()返回一个对字符串的引用。因此,类似的内容std::cout << file_path.filename().string() << "\n"可能会导致悬挂引用出现问题,因为引用所指向的字符串可能已被破坏。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.