2017-07-24 96 views
-1

下面,我產生了破碎的代碼和相同的固定版本。問題是我不能完全向自己解釋爲什麼前者不起作用,但後者卻起作用。我顯然需要回顧一下C++語言的一些非常基本的概念:能否提供我應該查看的內容的指針,並且可能還會解釋爲什麼我會用破碎的代碼得到結果。基本概念與std ::字符串引用,std :: regex和boost :: filesystem

在代碼中引用的'../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 

使用升壓1.62.0-R1和gcc(Gentoo的5.4.0-R3),升壓::文件系統文件不會出現()。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

std::smatch不存儲構成匹配的字符的副本,但是隻有成對的迭代器存儲到被搜索的原始字符串中。

第一個片段將一個臨時字符串傳遞給std::regex_match。當你開始檢查matches時,該字符串消失了,matches所持有的迭代器全部懸空。該程序然後通過嘗試使用它們展示未定義的行爲。

在第二個片段中,傳遞到std::regex_match的字符串在使用matches時仍然存在,所以一切正常。


注1:那問題表現不同,這取決於文件名長度可能是由於小串的優化。 std::string實現通常會在類實例中保留一個小緩衝區,並在那裏存儲短字符串;而更長的字符串被分配在堆上。可能由temp字符串先前佔用的堆棧空間仍然完好無損,而堆空間已被重新用於其他分配。該方案展示未定義的行爲 - 「似乎有效」是UB的一種可能的表現形式。


注2:它並沒有多大關係是否path::string()收益率的值或引用。 path::filename()按價值返回,因此dir_it.path().filename().string()將被破壞得太早,無論它是臨時對象dir_it.path().filename()的成員,還是由string()實時製造。

+0

哦!現在我明白了,看起來很明顯。 +1。謝謝你的時間。 – augustin