从路径中获取文件名

nid*_*hal 72 c++ visual-c++

从路径获取文件名的最简单方法是什么?

string filename = "C:\\MyDirectory\\MyFile.bat"
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我应该得到"MyFile".没有延期.

hmj*_*mjd 53

可能的解决方案:

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);
}
Run Code Online (Sandbox Code Playgroud)


Pix*_*ist 43

任务非常简单,因为基本文件名只是从文件夹的最后一个分隔符开始的字符串的一部分:

std::string base_filename = path.substr(path.find_last_of("/\\") + 1)
Run Code Online (Sandbox Code Playgroud)

如果要删除扩展名,那么唯一要做的就是找到最后一个.substr执行到此点

std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
Run Code Online (Sandbox Code Playgroud)

也许应该检查以处理仅由扩展组成的文件(即.bashrc......)

如果将其拆分为单独的函数,则可以灵活地重用单个任务:

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;
}
Run Code Online (Sandbox Code Playgroud)

该代码被模板化以便能够将它与不同的std::basic_string实例一起使用(即std::string&std::wstring...)

模板的缺点是如果将a const char *传递给函数,则需要指定模板参数.

所以你可以:

A)仅使用std::string而不是模板化代码

std::string base_name(std::string const & path)
{
  return path.substr(path.find_last_of("/\\") + 1);
}
Run Code Online (Sandbox Code Playgroud)

B)使用std::string(作为可能内联/优化的中间体)提供包装功能

inline std::string string_base_name(std::string const & path)
{
  return base_name(path);
}
Run Code Online (Sandbox Code Playgroud)

C)调用时指定模板参数const char *.

std::string base = base_name<std::string>("some/path/file.ext");
Run Code Online (Sandbox Code Playgroud)

结果

std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;
Run Code Online (Sandbox Code Playgroud)

打印

MyFile
Run Code Online (Sandbox Code Playgroud)


Jam*_*nze 38

最简单的解决方案是使用类似的东西boost::filesystem.如果由于某种原因这不是一个选择......

正确执行此操作将需要一些依赖于系统的代码:在Windows下,'\\'或者'/'可以是路径分隔符; 在Unix下,只有'/'工作,在其他系统下,谁知道.显而易见的解决方案是:

std::string
basename( std::string const& pathname )
{
    return std::string( 
        std::find_if( pathname.rbegin(), pathname.rend(),
                      MatchPathSeparator() ).base(),
        pathname.end() );
}
Run Code Online (Sandbox Code Playgroud)

,MatchPathSeparator在系统相关标头中定义为:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '/';
    }
};
Run Code Online (Sandbox Code Playgroud)

对于Unix,或者:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '\\' || ch == '/';
    }
};
Run Code Online (Sandbox Code Playgroud)

对于Windows(或某些其他未知系统仍然不同).

编辑:我错过了他也想要压制延伸的事实.为此,更多相同:

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 );
}
Run Code Online (Sandbox Code Playgroud)

代码有点复杂,因为在这种情况下,反向迭代器的基础位于我们想要切割的位置的错误一侧.(请记住,反向迭代器的基础是迭代器指向的字符后面的一个.)甚至这有点可疑:例如,我不喜欢它可以返回一个空字符串的事实.(如果唯一的'.'是文件名的第一个字符,我认为你应该返回完整的文件名.这需要一些额外的代码来捕捉特殊情况.)}

  • 如何使用`string :: find_last_of`而不是操纵反向迭代器? (8认同)

And*_*rsK 26

_splitpath应该做你需要的.你当然可以手动完成它,但也可以_splitpath处理所有特殊情况.

编辑:

正如BillHoag所说,建议在可用时使用更安全的_splitpath被叫_splitpath_s版本.

或者,如果你想要便携式的东西,你可以做这样的事情

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;
Run Code Online (Sandbox Code Playgroud)

  • 我有Visual Studio,**和**g ++,以及Sun CC.当有完美的便携式解决方案时,为什么我应该使用非标准的东西. (8认同)
  • @James,你的VS副本`stdlib.h`中没有`_splitpath`?然后你可能想要修复VS. (3认同)
  • 在我的机器上的任何包含中都没有`_splitpath`. (2认同)
  • @James,链接到该页面的页面表示它位于&lt;stdlib.h&gt;中。至于可移植性,也许您可​​以列举“完美的便携式解决方案”的一些例子? (2认同)
  • @Synetech 链接到的页面描述了 Microsoft 扩展,而不是 `&lt;stdlib.h&gt;`。最明显的便携式解决方案是 `boost::filesystem`。 (2认同)

Skr*_*sli 14

您还可以使用shell Path API PathFindFileName,PathRemoveExtension.对于这个特殊问题,可能比_splitpath更糟糕,但这些API对于各种路径解析作业非常有用,它们会考虑UNC路径,正斜杠和其他奇怪的东西.

wstring filename = L"C:\\MyDirectory\\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart); 
Run Code Online (Sandbox Code Playgroud)

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx

缺点是你必须链接到shlwapi.lib,但我不确定为什么这是一个缺点.


plh*_*lhn 14

如果你可以使用增强,

#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();
Run Code Online (Sandbox Code Playgroud)

这是所有的了.

我建议你使用boost库.使用C++时,Boost为您提供了许多便利.它几乎支持所有平台.如果你使用Ubuntu,你可以只安装一行boost库sudo apt-get install libboost-all-dev(参考.如何在Ubuntu上安装boost?)


Ria*_*inn 11

功能:

#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);
}
Run Code Online (Sandbox Code Playgroud)

测试:

#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");
}
Run Code Online (Sandbox Code Playgroud)


eli*_*etm 9

cpp17中最简单的方法是:

使用#include experimental/filesystem和filename()用于带扩展名的文件名和带有扩展名的stem().

   #include <iostream>
    #include <experimental/filesystem>
    namespace fs = std::experimental::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';
    }
Run Code Online (Sandbox Code Playgroud)

输出:

MyFile.bat
MyFile
"bar.txt"
".bar"
"."
"."
".."
"."
".."
"/"
Run Code Online (Sandbox Code Playgroud)

参考:cppreference


jav*_*web 7

来自C++ Docs - 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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Splitting: /usr/bin/man
 path: /usr/bin
 file: man
Splitting: c:\windows\winhelp.exe
 path: c:\windows
 file: winhelp.exe
Run Code Online (Sandbox Code Playgroud)


alv*_*eko 5

C++ 11变体(灵感来自James Kanze的版本),具有统一初始化和匿名内联lambda.

std::string basename(const std::string& pathname)
{
    return {std::find_if(pathname.rbegin(), pathname.rend(),
                         [](char c) { return c == '/'; }).base(),
            pathname.end()};
}
Run Code Online (Sandbox Code Playgroud)

但它不会删除文件扩展名.


Ada*_*son 5

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'
}
Run Code Online (Sandbox Code Playgroud)

输出:

"bar.txt"
Run Code Online (Sandbox Code Playgroud)

它也适用于std::string对象。