2015-09-29 77 views
1

我有以下代碼從一個任意格式的文件解析一束的部件編號(產品的部件的基本序列號)的。如何使用std :: regex_search查找所有出現的模式?

auto buildPartNumberRegexString(bool preFlash) -> std::string 
{ 
    std::ostringstream patternBuilder; 

    // The original, raw literal as tested on https://regex101.com/ is: 
    // 
    // @@PART_NUMBER_POST_FLASH\<\s*(\S+)\s*\,\s*(\d+)\s*\>@@ 
    // 
    // In C++, each backslash needs to be doubled. Alternatively, we could use raw string literals (R"\w"). 

    patternBuilder << "@@PART_NUMBER_" << (preFlash ? "PRE" : "POST") 
     << "_FLASH\\<\\s*(\\S+)\\s*\\,\\s*(\\d+)\\s*\\>@@"; 

    return patternBuilder.str(); 
} 

auto parsePartNumberAddresses(const std::string& templateFileContent, bool preFlash) -> ParamAddressContainer 
{ 
    const std::regex regEx(buildPartNumberRegexString(preFlash)); 
    std::smatch match; 

    if (std::regex_search(templateFileContent, match, regEx)) 
    { 
     assert(match.size() > 1); 
     const std::size_t capturedGroups = match.size() - 1; 

     assert(capturedGroups % 2 == 0); 
     const std::size_t partNumberAddressesFound = capturedGroups/2; 

     ParamAddressContainer results; 
     results.reserve(partNumberAddressesFound); 

     std::cerr << "DEBUG: capturedGroups = " << capturedGroups << ", partNumberAddressesFound = " << partNumberAddressesFound 
      << "\n"; 

     for (std::size_t i = 0; i < partNumberAddressesFound; ++i) 
     { 
      const std::size_t paramIdMatchIndex = i * 2 + 1; 
      const std::string paramIdString = match.str(paramIdMatchIndex); 
      const std::string paramIndexString = match.str(paramIdMatchIndex + 1); 

      results.emplace_back(util::string_funcs::fromString<ParamId_t> (paramIdString), 
       util::string_funcs::fromString<ParamIndex_t> (paramIndexString)); 
     } 

     std::cerr << "DEBUG: Going to read the following part numbers (" << (preFlash ? "pre" : "post") << "-flash):\n\n"; 

     for (const auto& paramAddress : results) 
     { 
      std::cerr << "\t" << std::hex << std::noshowbase << paramAddress.paramId << std::dec << "<" << paramAddress.paramIndex 
       << ">\n"; 
     } 

     return results; 
    } 

    return ParamAddressContainer(); 
} 

我寫了「美化」正則表達式(即沒有逃脫實際反斜槓需要雙反斜線)在buildPartNumberRegexString功能的註釋。

,我使用這個正則表達式可能是這樣的一個樣本文件:

Component alpha;@@PART_NUMBER_POST_FLASH<F12C,0>@@ 
Component beta;@@PART_NUMBER_POST_FLASH<F12C,1>@@ 

我測試過我正則表達式,使用相同的樣本文件,在https://regex101.com/和它的作品,正是因爲它應該,匹配兩個事件並提取所需的匹配組。問題是,當我嘗試通過std :: regex做同樣的事情時,它只找到第一個匹配項。現在https://regex101.com/我不得不啓用修改(全球,所有的比賽,不要第一場比賽返回)的正則表達式查找所有的比賽。我假設(希望),類似的標誌是可用於std::regex_search,但可用的標誌(http://en.cppreference.com/w/cpp/regex/match_flag_type)的說明似乎並沒有列出任何符合我的要求。當然,必須有一種方法來查找一個以上的模式,對嗎?有人有想法嗎?

+0

std :: regex_iterator上的cppreference.com頁面顯示瞭如何獲得所有匹配..猜我應該添加一個註釋到std :: regex_search – Cubbi

+0

Cubbi,我不確定它確實如此。關於cppreference調用的示例在數組中的所有字符串上重複使用regex_search。在我的情況下,我沒有一個字符串數組,我只有一個字符串。我知道我可以將樣本文件分成幾行,但正如我所說的,輸入可以任意格式化,並且不能保證每行總是有一個匹配。單行可能有2個或更多。 – antred

+1

是的,這並不明顯。添加了一個筆記。 – Cubbi

回答

1

如果對不起人都覺得這不值得發佈一個答案,以我自己的問題,但我想我會後我的更新和工作的其他人誰也正在尋找一個解決方案。好了,遵循Cubbi的建議,我決定使用std :: regex_iterator,我更喜歡反覆調用std :: regex_search。下面是我的修訂parsePartNumberAddresses功能:

auto parsePartNumberAddresses(const std::string& templateFileContent, bool preFlash) -> ParamAddressContainer 
{ 
    const std::regex regEx(buildPartNumberRegexString(preFlash)); 

    const auto begin_iterator = std::sregex_iterator(templateFileContent.cbegin(), templateFileContent.cend(), regEx); 
    const auto end_iterator = std::sregex_iterator(); 

    ParamAddressContainer results; 

    for (std::sregex_iterator it = begin_iterator; it != end_iterator; ++it) 
    { 
     const std::smatch& match = *it; 
     assert(match.size() == 3); 

     const std::string paramIdString = match.str(1); 
     const std::string paramIndexString = match.str(2); 

     results.emplace_back(util::string_funcs::fromString<ParamId_t> (paramIdString), 
      util::string_funcs::fromString<ParamIndex_t> (paramIndexString)); 
    } 

    std::cerr << "DEBUG: Going to read the following part numbers (" << (preFlash ? "pre" : "post") << "-flash):\n\n"; 

    for (const auto& paramAddress : results) 
    { 
     std::cerr << "\t" << std::hex << std::noshowbase << paramAddress.paramId << std::dec << "<" << paramAddress.paramIndex 
      << ">\n"; 
    } 

    return results; 
} 

這個工程完全按照預期,給予所有比賽主題的字符串中。 :)

UPDATE:如rici所示,刪除多餘的std :: distance()以及results.reserve()調用來防止評估正則表達式兩次。

+1

做整個正則表達式搜索兩次,以便您可以預測「結果」是假經濟(至少可以這麼說)。 – rici

+0

rici,我不認爲我做了兩次。除非每次迭代結果時重新評估正則表達式。那會發生什麼? 編輯:從cppreference報價:在建設和**每增量**,它調用std :: regex_search [...] 呃,你可能是對的。 :-( – antred

+1

只是溝matches_found'的'計算。'emplace_back'將在必要時調整。 – rici

相關問題