C++ 从路径中获取文件名
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8520560/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get a file name from a path
提问by nidhal
What is the simplest way to get the file name that from a path?
从路径获取文件名的最简单方法是什么?
string filename = "C:\MyDirectory\MyFile.bat"
In this example, I should get "MyFile". without extension.
在这个例子中,我应该得到“MyFile”。没有扩展。
采纳答案by AndersK
_splitpathshould do what you need. You could of course do it manually but _splitpath
handles all special cases as well.
_splitpath应该做你需要的。您当然可以手动完成,但也可以_splitpath
处理所有特殊情况。
EDIT:
编辑:
As BillHoag mentioned it is recommended to use the more safe version of _splitpath
called _splitpath_swhen available.
正如 BillHoag 提到的,建议在可用时使用更安全的_splitpath
称为_splitpath_s 的版本。
Or if you want something portable you could just do something like this
或者如果你想要一些便携的东西,你可以做这样的事情
std::vector<std::string> splitpath(
const std::string& str
, const std::set<char> delimiters)
{
std::vector<std::string> result;
char const* pch = str.c_str();
char const* start = pch;
for(; *pch; ++pch)
{
if (delimiters.find(*pch) != delimiters.end())
{
if (start != pch)
{
std::string str(start, pch);
result.push_back(str);
}
else
{
result.push_back("");
}
start = pch + 1;
}
}
result.push_back(start);
return result;
}
...
std::set<char> delims{'\'};
std::vector<std::string> path = splitpath("C:\MyDirectory\MyFile.bat", delims);
cout << path.back() << endl;
回答by hmjd
A possible solution:
一个可能的解决方案:
string filename = "C:\MyDirectory\MyFile.bat";
// Remove directory if present.
// Do this before extension removal incase directory has a period character.
const size_t last_slash_idx = filename.find_last_of("\/");
if (std::string::npos != last_slash_idx)
{
filename.erase(0, last_slash_idx + 1);
}
// Remove extension if present.
const size_t period_idx = filename.rfind('.');
if (std::string::npos != period_idx)
{
filename.erase(period_idx);
}
回答by Pixelchemist
The task is fairly simple as the base filename is just the part of the string starting at the last delimeter for folders:
该任务相当简单,因为基本文件名只是从文件夹的最后一个分隔符开始的字符串的一部分:
std::string base_filename = path.substr(path.find_last_of("/\") + 1)
If the extension is to be removed as well the only thing to do is find the last .
and take a substr
to this point
如果扩展名也被删除,唯一要做的就是找到最后一个.
并走到substr
这一步
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
Perhaps there should be a check to cope with files solely consisting of extensions (ie .bashrc
...)
也许应该进行检查以处理仅由扩展名组成的文件(即.bashrc
...)
If you split this up into seperate functions you're flexible to reuse the single tasks:
如果将其拆分为单独的功能,则可以灵活地重用单个任务:
template<class T>
T base_name(T const & path, T const & delims = "/\")
{
return path.substr(path.find_last_of(delims) + 1);
}
template<class T>
T remove_extension(T const & filename)
{
typename T::size_type const p(filename.find_last_of('.'));
return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
}
The code is templated to be able to use it with different std::basic_string
instances (i.e. std::string
& std::wstring
...)
代码被模板化,以便能够在不同的std::basic_string
实例中使用它(即std::string
& std::wstring
...)
The downside of the templation is the requirement to specify the template parameter if a const char *
is passed to the functions.
模板的缺点是如果将 aconst char *
传递给函数,则需要指定模板参数。
So you could either:
所以你可以:
A) Use only std::string
instead of templating the code
A) 仅使用std::string
而不是模板化代码
std::string base_name(std::string const & path)
{
return path.substr(path.find_last_of("/\") + 1);
}
B) Provide wrapping function using std::string
(as intermediates which will likely be inlined / optimized away)
B)提供包装功能使用std::string
(作为可能被内联/优化掉的中间体)
inline std::string string_base_name(std::string const & path)
{
return base_name(path);
}
C) Specify the template parameter when calling with const char *
.
C) 调用时指定模板参数const char *
。
std::string base = base_name<std::string>("some/path/file.ext");
Result
结果
std::string filepath = "C:\MyDirectory\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;
Prints
印刷
MyFile
回答by James Kanze
The simplest solution is to use something like boost::filesystem
. If
for some reason this isn't an option...
最简单的解决方案是使用类似boost::filesystem
. 如果由于某种原因这不是一个选择......
Doing this correctly will require some system dependent code: under
Windows, either '\\'
or '/'
can be a path separator; under Unix,
only '/'
works, and under other systems, who knows. The obvious
solution would be something like:
这样做正确将需要一些系统相关的代码:在Windows下,要么'\\'
或者'/'
可以是一个路径分隔符; 在Unix下,只能'/'
工作,而在其他系统下,谁知道呢。显而易见的解决方案是这样的:
std::string
basename( std::string const& pathname )
{
return std::string(
std::find_if( pathname.rbegin(), pathname.rend(),
MatchPathSeparator() ).base(),
pathname.end() );
}
, with MatchPathSeparator
being defined in a system dependent header
as either:
,MatchPathSeparator
在系统相关的头文件中定义为:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '/';
}
};
for Unix, or:
对于 Unix,或:
struct MatchPathSeparator
{
bool operator()( char ch ) const
{
return ch == '\' || ch == '/';
}
};
for Windows (or something still different for some other unknown system).
对于 Windows(或者对于其他一些未知系统仍然不同的东西)。
EDIT: I missed the fact that he also wanted to suppress the extention. For that, more of the same:
编辑:我错过了他也想抑制扩展的事实。为此,还有更多相同之处:
std::string
removeExtension( std::string const& filename )
{
std::string::const_reverse_iterator
pivot
= std::find( filename.rbegin(), filename.rend(), '.' );
return pivot == filename.rend()
? filename
: std::string( filename.begin(), pivot.base() - 1 );
}
The code is a little bit more complex, because in this case, the base of
the reverse iterator is on the wrong side of where we want to cut.
(Remember that the base of a reverse iterator is one behind the
character the iterator points to.) And even this is a little dubious: I
don't like the fact that it can return an empty string, for example.
(If the only '.'
is the first character of the filename, I'd argue
that you should return the full filename. This would require a little
bit of extra code to catch the special case.)
}
代码稍微复杂一点,因为在这种情况下,反向迭代器的基础位于我们想要剪切的错误一侧。(请记住,反向迭代器的基址是迭代器指向的字符后面的一个。)甚至这也有点可疑:例如,我不喜欢它可以返回空字符串的事实。(如果 only'.'
是文件名的第一个字符,我认为您应该返回完整的文件名。这需要一些额外的代码来捕捉特殊情况。)}
回答by Skrymsli
You can also use the shell Path APIs PathFindFileName, PathRemoveExtension. Probably worse than _splitpath for this particular problem, but those APIs are very useful for all kinds of path parsing jobs and they take UNC paths, forward slashes and other weird stuff into account.
您还可以使用外壳路径 API PathFindFileName、PathRemoveExtension。对于这个特定问题,可能比 _splitpath 更糟糕,但是这些 API 对于各种路径解析作业都非常有用,并且它们考虑了 UNC 路径、正斜杠和其他奇怪的东西。
wstring filename = L"C:\MyDirectory\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart);
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx
The drawback is that you have to link to shlwapi.lib, but I'm not really sure why that's a drawback.
缺点是您必须链接到 shlwapi.lib,但我不确定为什么这是一个缺点。
回答by plhn
If you can use boost,
如果你可以使用升压,
#include <boost/filesystem.hpp>
path p("C:\MyDirectory\MyFile.bat");
string basename = p.filename().string();
//or
//string basename = path("C:\MyDirectory\MyFile.bat").filename().string();
This is all.
这就是全部。
I recommend you to use boost library. Boost gives you a lot of conveniences when you work with C++. It supports almost all platforms.
If you use Ubuntu, you can install boost library by only one line sudo apt-get install libboost-all-dev
(ref. How to Install boost on Ubuntu?)
我建议您使用 boost 库。当您使用 C++ 时,Boost 为您提供了很多便利。它支持几乎所有平台。如果你使用 Ubuntu,你可以只用一行来安装 boost 库sudo apt-get install libboost-all-dev
(参考。如何在 Ubuntu 上安装 boost?)
回答by Rian Quinn
Function:
功能:
#include <string>
std::string
basename(const std::string &filename)
{
if (filename.empty()) {
return {};
}
auto len = filename.length();
auto index = filename.find_last_of("/\");
if (index == std::string::npos) {
return filename;
}
if (index + 1 >= len) {
len--;
index = filename.substr(0, len).find_last_of("/\");
if (len == 0) {
return filename;
}
if (index == 0) {
return filename.substr(1, len - 1);
}
if (index == std::string::npos) {
return filename.substr(0, len);
}
return filename.substr(index + 1, len - index - 1);
}
return filename.substr(index + 1, len - index);
}
Tests:
测试:
#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>
TEST_CASE("basename")
{
CHECK(basename("") == "");
CHECK(basename("no_path") == "no_path");
CHECK(basename("with.ext") == "with.ext");
CHECK(basename("/no_filename/") == "no_filename");
CHECK(basename("no_filename/") == "no_filename");
CHECK(basename("/no/filename/") == "filename");
CHECK(basename("/absolute/file.ext") == "file.ext");
CHECK(basename("../relative/file.ext") == "file.ext");
CHECK(basename("/") == "/");
CHECK(basename("c:\windows\path.ext") == "path.ext");
CHECK(basename("c:\windows\no_filename\") == "no_filename");
}
回答by eliasetm
The Simplest way in C++17 is:
C++17 中最简单的方法是:
use the #include <filesystem>
and filename()
for filename with extension and stem()
without extension.
使用#include <filesystem>
和filename()
作为带扩展名和stem()
不带扩展名的文件名。
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
string filename = "C:\MyDirectory\MyFile.bat";
std::cout << fs::path(filename).filename() << '\n'
<< fs::path(filename).stem() << '\n'
<< fs::path("/foo/bar.txt").filename() << '\n'
<< fs::path("/foo/bar.txt").stem() << '\n'
<< fs::path("/foo/.bar").filename() << '\n'
<< fs::path("/foo/bar/").filename() << '\n'
<< fs::path("/foo/.").filename() << '\n'
<< fs::path("/foo/..").filename() << '\n'
<< fs::path(".").filename() << '\n'
<< fs::path("..").filename() << '\n'
<< fs::path("/").filename() << '\n';
}
output:
输出:
MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
Reference: cppreference
参考:cppreference
回答by jave.web
From C++ Docs - string::find_last_of
来自 C++ 文档 - string::find_last_of
#include <iostream> // std::cout
#include <string> // std::string
void SplitFilename (const std::string& str) {
std::cout << "Splitting: " << str << '\n';
unsigned found = str.find_last_of("/\");
std::cout << " path: " << str.substr(0,found) << '\n';
std::cout << " file: " << str.substr(found+1) << '\n';
}
int main () {
std::string str1 ("/usr/bin/man");
std::string str2 ("c:\windows\winhelp.exe");
SplitFilename (str1);
SplitFilename (str2);
return 0;
}
Outputs:
输出:
Splitting: /usr/bin/man
path: /usr/bin
file: man
Splitting: c:\windows\winhelp.exe
path: c:\windows
file: winhelp.exe
回答by Adam Erickson
The boost
filesystem
library is also available as the experimental/filesystem
library and was merged into ISO C++ for C++17. You can use it like this:
该boost
filesystem
库也可用作experimental/filesystem
库,并已合并到 C++17 的 ISO C++ 中。你可以这样使用它:
#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
int main () {
std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}
Output:
输出:
"bar.txt"
It also works for std::string
objects.
它也适用于std::string
对象。