std::transform 如何不返回(而不抛出),只是跳过?

发布于 2024-12-09 06:54:47 字数 2862 浏览 1 评论 0原文

我想显示文件夹内容(没有任何文件夹系统),因此有一个 std::set ,其中包含 std::strings 以及一些给定的路径我们想要像普通文件系统一样搜索某些目录的文件夹内容。因此,有了一组:

    set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");

和搜索字符串 "bin/obj/Debug/" 我们想要获得 3 个项目 - 文件夹,2 个文件:

CloudServerPrototype/
vc100.idb
vc100.pdb

但我们也得到一个空行。如何不获取它以及如何在未找到项目的情况下抛出错误?

整个代码:

#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>

using namespace std;

struct get_pertinent_part
{
    const std::string given_string;

    get_pertinent_part(const std::string& s)
        :given_string(s)
    {
    }

    std::string operator()(const std::string& s)
    {
        std::string::size_type first = 0;

        if (s.find(given_string) == 0)
        {
            first = given_string.length();
        }
        else
        {
            return "";
        }

        std::string::size_type count = std::string::npos;
        std::string::size_type pos = s.find_last_of("/");
        if (pos != std::string::npos && pos > first)
        {
            count = pos + 1 - first;
        }

        return s.substr(first, count);
    }
};

void directory_listning_without_directories_demo()
{
    set<string> output;
    set<string> demo_set;

    demo_set.insert("file1");
    demo_set.insert("file2");
    demo_set.insert("folder/file1");
    demo_set.insert("folder/file2");
    demo_set.insert("folder/folder/file1");
    demo_set.insert("folder/folder/file2");
    demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
    demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
    demo_set.insert("bin/obj/Debug/vc100.idb");
    demo_set.insert("bin/obj/Debug/vc100.pdb");


    std::transform(demo_set.begin(),
        demo_set.end(),
        std::inserter(output, output.end()),
        get_pertinent_part("bin/obj/Debug/"));

    std::copy(output.begin(),
        output.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));
}

int main()
{
    directory_listning_without_directories_demo();
    cin.get();
    return 0;
}

基于 这个很好的答案。

I want to display folder contents displaying (not having any folder system) So Having a std::set<string_file_names> with std::strings in it and some given path to some dir we want to search for folder contents as in normal fs. so having set of:

    set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");

and search string "bin/obj/Debug/" we want to get 3 Items - folder, 2 files:

CloudServerPrototype/
vc100.idb
vc100.pdb

But we get also an empty line. How to not get it and how to throw an error in case of non items found?

Entire code:

#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>

using namespace std;

struct get_pertinent_part
{
    const std::string given_string;

    get_pertinent_part(const std::string& s)
        :given_string(s)
    {
    }

    std::string operator()(const std::string& s)
    {
        std::string::size_type first = 0;

        if (s.find(given_string) == 0)
        {
            first = given_string.length();
        }
        else
        {
            return "";
        }

        std::string::size_type count = std::string::npos;
        std::string::size_type pos = s.find_last_of("/");
        if (pos != std::string::npos && pos > first)
        {
            count = pos + 1 - first;
        }

        return s.substr(first, count);
    }
};

void directory_listning_without_directories_demo()
{
    set<string> output;
    set<string> demo_set;

    demo_set.insert("file1");
    demo_set.insert("file2");
    demo_set.insert("folder/file1");
    demo_set.insert("folder/file2");
    demo_set.insert("folder/folder/file1");
    demo_set.insert("folder/folder/file2");
    demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
    demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
    demo_set.insert("bin/obj/Debug/vc100.idb");
    demo_set.insert("bin/obj/Debug/vc100.pdb");


    std::transform(demo_set.begin(),
        demo_set.end(),
        std::inserter(output, output.end()),
        get_pertinent_part("bin/obj/Debug/"));

    std::copy(output.begin(),
        output.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));
}

int main()
{
    directory_listning_without_directories_demo();
    cin.get();
    return 0;
}

code sample based on this grate answer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

枕梦 2024-12-16 06:54:47

由于没有transform_if,执行此操作的正确方法是首先将具有非零长度 get_pertinent_part 结果的路径复制到另一个容器中,然后在该新容器上运行转换。

或者,您可以编写类似transform_if 的内容,将转换函数的结果与谓词进行比较。这是我未经测试的尝试:

template <class InIt, class OutIt, class UnaryFunction, class Predicate>
void transform_if_value(InIt first, InIt last, OutIt out, UnaryFunction fn, Predicate pred)
{
    for ( ; first != last; ++ first)
    {
        auto val = fn(*first);
        if (pred(val))
            *out++ = val;
    }
}

然后你可以使用它,例如

transform_if_value(
    demo_set.begin(),
    demo_set.end(),
    std::inserter(output, output.begin()),
    get_pertinent_part("bin/obj/Debug/"),
    [](const std::string& s) {return !s.empty();});

对于更酷的语法,请查看 boost 的 Range 适配器:

boost::copy(
    demo_set | boost::adaptors::transformed(get_pertinent_part("bin/obj/Debug/"))
             | boost::adaptors::filtered([](const std::string& s) {return !s.empty();}),
    std::inserter(output, output.begin()));

As there is no transform_if, a proper way to do this would be to to first copy_if the paths that have a non-zero-length get_pertinent_part result into another container, and then run the transform on that new container.

Alternatively, you could write something like transform_if that compares the result of your transform function with a predicate. Here's my untested shot at it:

template <class InIt, class OutIt, class UnaryFunction, class Predicate>
void transform_if_value(InIt first, InIt last, OutIt out, UnaryFunction fn, Predicate pred)
{
    for ( ; first != last; ++ first)
    {
        auto val = fn(*first);
        if (pred(val))
            *out++ = val;
    }
}

Then you could use that like

transform_if_value(
    demo_set.begin(),
    demo_set.end(),
    std::inserter(output, output.begin()),
    get_pertinent_part("bin/obj/Debug/"),
    [](const std::string& s) {return !s.empty();});

For even cooler syntax, check out boost's Range adaptors:

boost::copy(
    demo_set | boost::adaptors::transformed(get_pertinent_part("bin/obj/Debug/"))
             | boost::adaptors::filtered([](const std::string& s) {return !s.empty();}),
    std::inserter(output, output.begin()));
箜明 2024-12-16 06:54:47

您可以使用 std::accumulate (不是来自 而是来自 )来累加 匹配到你的输出。

std::set<string> const&
operator()(std::set<string> const& out, std::string const& candidate) const
{
    if(/* same logic to check if candidate matches */) {
        out.insert(/* insert processed/transformed path */);
    }
    return out;
}

调用方式:

std::accumulate(
    demo_set.begin(), demo_set.end()
    , std::ref(output)
    , get_pertinent_path("bin/obj/Debug/") );

请注意 std::ref 的使用(来自 )。您不希望输出集被移动(加上对 std::accumulate 的调用结果被忽略,因此不会看到任何更改)。或者,如果您不想依赖 std::ref,则可以使用指针。

编辑:呵呵,重新审视这个想法,这可以说并不比使用 std::for_each 并将输出的引用传递给函子的构造函数更好。 YMMV。

You can use std::accumulate (not from <algorithm> but from <numeric>) to, well, accumulate the matches into your output.

std::set<string> const&
operator()(std::set<string> const& out, std::string const& candidate) const
{
    if(/* same logic to check if candidate matches */) {
        out.insert(/* insert processed/transformed path */);
    }
    return out;
}

Call as:

std::accumulate(
    demo_set.begin(), demo_set.end()
    , std::ref(output)
    , get_pertinent_path("bin/obj/Debug/") );

Note the use of std::ref (from <functional>). You don't want the output set to be moved around (plus the result of the call to std::accumulate is ignored so no changes will be visible). Alternatively, if you don't want a dependency on std::ref, you can use pointers.

edit: heh, revisiting this idea, this is arguably no better than using std::for_each and passing a reference to the ouput to the constructor of the functor. YMMV.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文