std :: string引用,std :: regex和boost :: filesystem的基本概念

时间:2017-07-24 15:14:28

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

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

在' ../ docs /'在代码中引用的目录,我只是使用了触摸' linux上的命令创建了一些各种长度的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?

1 个答案:

答案 0 :(得分: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()即时制作。< / p>