【问题标题】:Basic concepts with std::string references, std::regex and boost::filesystemstd::string 引用、std::regex 和 boost::filesystem 的基本概念
【发布时间】:2017-12-30 06:04:54
【问题描述】:

下面,我生成了损坏的代码和相同的固定版本。问题是我无法向自己完全解释为什么前者不起作用但后者起作用。我显然需要复习 C++ 语言的一些非常基本的概念:您能否提供关于我应该复习的内容的指针,并且可能还解释一下为什么我会得到我使用损坏的代码得到的结果。

在代码中提到的'../docs/'目录中,我只是简单地使用linux上的'touch'命令创建了多个不同长度的doc......html文件。

#include <iostream>
#include <regex>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

int main() {
        fs::path p("../docs/");

        for (auto& dir_it : fs::directory_iterator(p)) {
                std::regex re = std::regex("^(doc[a-z]+)\\.html$");
                std::smatch matches;
                // BROKEN HERE:
                if (std::regex_match(dir_it.path().filename().string(), matches, re)) {
                        std::cout << "\t\t" <<dir_it.path().filename().string();
                        std::cout << "\t\t\t" << matches[1] << std::endl;
                }
        }

        return 0;
}

生产:

        documentati.html                        ati
        documentationt.html                     �}:ationt
        document.html                   document
        documenta.html                  documenta
        docume.html                     docume
        documentat.html                 documentat
        docum.html                      docum
        documentatio.html                       ��:atio
        documen.html                    documen
        docu.html                       docu
        documentation.html                      ��:ation
        documaeuaoeu.html                       ��:aoeu

注意 1:上面的错误是由超过一定长度的文件名触发的。我只明白那是因为 std::string 对象正在调整自己的大小。

注 2:上面的代码与以下问题中使用的代码非常相似,但使用的是 boost::regex_match 而不是 std::regex_match: Can I use a mask to iterate files in a directory with Boost?
它以前也适用于我,但现在我使用 GCC 5.4 而不是 GCC 4.6、std::regex 而不是 boost::regex、C++11 和一个更新版本的 boost::filesystem。哪个更改是相关的,导致工作代码被破坏?

固定:

#include <iostream>
#include <regex>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

int main() {
        fs::path p("../docs/");

        for (auto& dir_it : fs::directory_iterator(p)) {
                std::regex re = std::regex("^(doc[a-z]+)\\.html$");
                std::smatch matches;
                std::string p = dir_it.path().filename().string();
                if (std::regex_match(p, matches, re)) {
                        std::cout << "\t\t" <<dir_it.path().filename().string();
                        std::cout << "\t\t\t" << matches[1] << std::endl;
                }
        }

        return 0;
}

产生:

        documentati.html                        documentati
        documentationt.html                     documentationt
        document.html                   document
        documenta.html                  documenta
        docume.html                     docume
        documentat.html                 documentat
        docum.html                      docum
        documentatio.html                       documentatio
        documen.html                    documen
        docu.html                       docu
        documentation.html                      documentation
        documaeuaoeu.html                       documaeuaoeu

使用 boost 1.62.0-r1 和 gcc (Gentoo 5.4.0-r3),boost::filesystem 文档似乎没有提供任何关于 path().filename().string() 返回的明确指示: http://www.boost.org/doc/libs/1_62_0/libs/filesystem/doc/reference.html#path-filename
看来这取决于:
Why does boost::filesystem::path::string() return by value on Windows and by reference on POSIX?

【问题讨论】:

    标签: c++ regex string c++11 boost-filesystem


    【解决方案1】:

    std::smatch 不存储组成匹配的字符的副本,而只存储成对的迭代器到被搜索的原始字符串中。

    第一个片段将一个临时字符串传递给std::regex_match。当您开始检查matches 时,该字符串已经消失,matches 持有的迭代器都悬空了。然后程序通过尝试使用它们来表现出未定义的行为。

    在第二个片段中,传递给std::regex_match 的字符串在使用matches 时仍然有效,所以一切正常。


    注意 1: 问题的表现取决于文件名长度,这可能是由于小字符串优化所致。 std::string 的实现通常会在类实例中保留一个小缓冲区,并在那里存储短字符串;而较长的字符串分配在堆上。以前被临时字符串占用的堆栈空间可能仍然完好无损,而堆空间已被重用于其他分配。无论哪种方式,该程序都表现出未定义的行为 - “似乎工作”是 UB 的一种可能表现。


    注 2: path::string() 是按值返回还是按引用返回并不重要。 path::filename() 按值返回,因此无论是 dir_it.path().filename() 临时对象的成员,还是由 string() 即时制造,dir_it.path().filename().string() 都会过早销毁。

    【讨论】:

    • 哦!现在我看到它,它似乎很明显。 +1。感谢您的宝贵时间。
    猜你喜欢
    • 2011-04-23
    • 2018-05-31
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 1970-01-01
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多