基本概念 std::string 参考,std::regex 和 boost::filesystem
Basic concepts with std::string references, std::regex and boost::filesystem
下面,我生成了损坏的代码和相同的修复版本。问题是我无法向自己完全解释为什么前者不起作用而后者起作用。我显然需要复习 C++ 语言的一些非常基本的概念:你能否提供关于我应该复习的内容的指示,并且可能还解释为什么我得到我用损坏的代码得到的结果。
在代码中提到的'../docs/'目录中,我只是在linux上使用'touch'命令创建了一些文档......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 的明确指示() returns:
http://www.boost.org/doc/libs/1_62_0/libs/filesystem/doc/reference.html#path-filename
看来这取决于:
std::smatch
不会存储包含匹配项的字符的副本,而只会将迭代器对存储到被搜索的原始字符串中。
第一个片段将临时字符串传递给 std::regex_match
。当您着手检查 matches
时,该字符串已经消失并且 matches
持有的迭代器都悬空了。该程序然后通过尝试使用它们表现出未定义的行为。
在第二个片段中,传递给 std::regex_match
的字符串在使用 matches
时仍然存在,因此一切正常。
注意 1:问题根据文件名长度的不同而不同,这可能是由于小字符串优化所致。 std::string
实现通常在 class 实例中保留一个小缓冲区,并在那里存储短字符串;而较长的字符串分配在堆上。有可能之前被临时字符串占用的堆栈 space 仍然完好无损,而堆 space 已被重新用于其他分配。该程序以任何一种方式表现出未定义的行为 - "seems to work" 是 UB 的一种可能表现形式。
注2: path::string()
returns 是按值还是按引用并不重要。 path::filename()
returns 按值计算,因此 dir_it.path().filename().string()
会过早地被销毁,无论它是临时 dir_it.path().filename()
对象的成员,还是由 [=21 即时制造的=].
下面,我生成了损坏的代码和相同的修复版本。问题是我无法向自己完全解释为什么前者不起作用而后者起作用。我显然需要复习 C++ 语言的一些非常基本的概念:你能否提供关于我应该复习的内容的指示,并且可能还解释为什么我得到我用损坏的代码得到的结果。
在代码中提到的'../docs/'目录中,我只是在linux上使用'touch'命令创建了一些文档......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 的明确指示() returns:
http://www.boost.org/doc/libs/1_62_0/libs/filesystem/doc/reference.html#path-filename
看来这取决于:
std::smatch
不会存储包含匹配项的字符的副本,而只会将迭代器对存储到被搜索的原始字符串中。
第一个片段将临时字符串传递给 std::regex_match
。当您着手检查 matches
时,该字符串已经消失并且 matches
持有的迭代器都悬空了。该程序然后通过尝试使用它们表现出未定义的行为。
在第二个片段中,传递给 std::regex_match
的字符串在使用 matches
时仍然存在,因此一切正常。
注意 1:问题根据文件名长度的不同而不同,这可能是由于小字符串优化所致。 std::string
实现通常在 class 实例中保留一个小缓冲区,并在那里存储短字符串;而较长的字符串分配在堆上。有可能之前被临时字符串占用的堆栈 space 仍然完好无损,而堆 space 已被重新用于其他分配。该程序以任何一种方式表现出未定义的行为 - "seems to work" 是 UB 的一种可能表现形式。
注2: path::string()
returns 是按值还是按引用并不重要。 path::filename()
returns 按值计算,因此 dir_it.path().filename().string()
会过早地被销毁,无论它是临时 dir_it.path().filename()
对象的成员,还是由 [=21 即时制造的=].