【问题标题】:Using Boost find_last to find any of使用 Boost find_last 查找任何
【发布时间】:2015-03-05 15:46:10
【问题描述】:

这是一个函数,它返回指向 C 字符串路径中文件名部分的指针。 StringT 应该是 char*wchar_t*

template <typename StringT>
StringT GetFilenamePos(StringT path) {
    typedef boost::iterator_range<typename boost::range_iterator<StringT>::type> StringItRange;
    StringItRange lastFound = boost::find_last(path, L"\\");
    StringT filename = lastFound.empty()
                        ? path
                        : lastFound.end();
    return filename;
}

我不仅想搜索\,还想搜索/,有没有办法用Boost 构造一个查找器对象,在一个字符串遍历中执行此操作(如find_any_of)?

我想在一次搜索遍历中执行此操作,并且不进行任何复制。

第二点,不太重要;有没有办法改变 L"\\" 文字,以便根据模板参数选择宽文字或普通文字?

【问题讨论】:

  • 对于您的第二点,this 会做什么? (如果确实如此,则归功于Steve Jessop's answer
  • std::string::find_last_of 是解决方案吗?
  • @megabyte1024 我想要一个指向现有 C 字符串中文件名开头的指针,这是无法做到的,否则它完全符合我的要求。

标签: c++ boost find


【解决方案1】:

更新 2 正如帕斯卡曾经说过的:

“我把这封信写得比平时长了,因为我没时间把它写短”

我终于找到时间缩短它了!现在,它不再需要提升:

3。使用std::string::find_last_of

您可以非常直接地使用它。这里本质上是一个单行线——除了需要以与字符类型无关的方式指定分隔符集。

在本例中,我选择使用 boost::string_ref 来返回对输入字符串子范围的用户友好的 const 引用。

请注意,使用boost::string_ref 是可选的,不需要链接依赖项。如果您愿意,可以将其更改为 std::pair&lt;iterator, iterator&gt;

Live On Coliru

#include <string>
#include <boost/utility/string_ref.hpp>

template <typename StringT, typename C = typename StringT::value_type>
boost::basic_string_ref<C> GetFilename(StringT const& path) {
    static const C separators[4] = { '\\', '/', ':', 0 };
    return boost::basic_string_ref<C>(path).substr(path.find_last_of(separators) + 1);
}

#include <iostream>

int main() {
    for (std::string fname : {
            "/tmp/somedir/../other//./beautiful filenames.txt.0",
            "simple.txt",
            "",
        })
    {
        std::cout << "'" << fname << "' -> '" << GetFilename(fname) << "'\n";
    }

    for (std::wstring fname : {
            L"D:\\Program Files (64)\\Something Else Entirely\\but works.just-the-same.exe",
            L"\\UNCNAME\\Something Else Entirely\\network.dat",
            L"D://testing/test123",
            L"http://user:password@hostname:port/test123/resource?query=parameter#yo"
            L"E:beautiful filenames.txt.0",
            L"simple.txt",
            L"",
        })
    {
        std::wcout << "'" << fname << "' -> '" << GetFilename(fname) << "'\n";
    }
}

打印

'/tmp/somedir/../other//./beautiful filenames.txt.0' -> 'beautiful filenames.txt.0'
'simple.txt' -> 'simple.txt'
'' -> ''
'D:\Program Files (64)\Something Else Entirely\but works.just-the-same.exe' -> 'but works.just-the-same.exe'
'\UNCNAME\Something Else Entirely\network.dat' -> 'network.dat'
'D://testing/test123' -> 'test123'
'http://user:password@hostname:port/test123/resource?query=parameter#yo' -> 'resource?query=parameter#yo'
'E:beautiful filenames.txt.0' -> 'beautiful filenames.txt.0'
'simple.txt' -> 'simple.txt'
'' -> ''

更新由于您希望无复制行为,这里有一个使用 Boost Regex 的版本

2。使用 Boost 正则表达式

此解决方案也返回适当字符类型的string_ref

事实证明,这种方法比要求的要重得多,但您可能仍然希望看到/使用它来完成不那么琐碎的任务。

Live On Coliru

#include <boost/regex.hpp>
#include <boost/utility/string_ref.hpp>

template <typename StringT, typename C = typename StringT::value_type>
boost::basic_string_ref<C> GetFilename(StringT const& path) {

    // it's quite tricky to initialze the pattern generically:
    static auto re = [] {
        char const pattern[] = "[^\\\\/:]*$";
        std::basic_string<C> spattern(pattern, pattern + sizeof(pattern)-1);
        return boost::basic_regex<C>(spattern, boost::regex_constants::optimize);
    }();

    // the rest is plain sailing:
    boost::match_results<typename StringT::const_iterator> mr;
    if (boost::regex_search(path.begin(), path.end(), mr, re) && mr[0].matched)
        return { &*(mr[0].first), size_t(mr[0].length()) };
    return path;
}

#include <iostream>

int main() {

    std::string fname = "/tmp/somedir/../other//./beautiful filenames.txt.0";
    std::cout << GetFilename(fname) << "\n";
    std::wstring wname = L"D:\\Program Files (64)\\Something Else Entirely\\but works.just-the-same.exe";
    std::wcout << GetFilename(wname) << "\n";
}

打印:

beautiful filenames.txt.0
but works.just-the-same.exe

1。使用 Boost 文件系统

旧答案是:这似乎是 Boost Filesystem 的工作。

Live On Coliru

#include <boost/filesystem.hpp>
#include <iostream>
namespace fs = boost::filesystem;

template <typename StringT>
StringT GetFilename(StringT const& path) {
    return fs::path(path).filename().string<StringT>();
}

int main() {
#ifndef _WIN32
    std::string fname = "/tmp/somedir/../other//./beautiful filenames.txt.0";
    std::cout << GetFilename(fname) << "\n";
#else
    std::wstring wname = L"D:\\Program Files (64)\\Something Else Entirely\\but works.just-the-same.exe";
    std::wcout << GetFilename(wname) << "\n";
#endif
}

这应该打印出来

beautiful filenames.txt.0

but works.just-the-same.exe

取决于您的平台

【讨论】:

  • 很好很简短,但我确实想要一个简单的单遍历搜索,它可以为我提供指向现有字符串的指针。这将在可能包含数十万个文件的循环中使用;我想避免任何复制。
  • 好点。我没明白,因为你有返回类型 StringT
  • @FelixDombek 添加了一个基于 Boost Regex 的具有用户友好界面的通用非复制实现。我很确定使用 Boost String Algorithms 可以让它更短更高效,但遗憾的是我现在没有时间
  • @FelixDombek 好消息:我找到了进一步简化它的时间。它现在本质上是一个只需要标准库的单行器。 Live On Coliru
  • 谢谢,太棒了。我不确定使用冒号作为分隔符是一个好主意,因为(1)像E:file.txt 这样的路径实际出现在哪里,(2)它也是命名的流分隔符符号。但这个解决方案正是我想要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-21
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 2012-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多