【问题标题】:Using std::regex to filter input使用 std::regex 过滤输入
【发布时间】:2016-08-13 02:04:56
【问题描述】:

我有一个难看的字符串,它由几个 URI 组成。

:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg

我想要做的是去掉每个出现的字符:/.,,这样我就可以有一个字符串作为有效的文件名。

我写了这个简单的正则表达式就是为了做到这一点:[^(:/,.)] 根据http://www.regexpal.com/,这似乎是正确的正则表达式。

但是,当我运行以下 C++ 代码时,我没有得到我所期望的(只是字母数字字符和下划线),我只得到了序列中的第一个字母数字字符:S

我在 std::regex 上做错了什么,或者我的 regex 表达式关闭了?

#include <iostream>
#include <regex>
#include <string>

static const std::string filenames {R"(:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg)"};
static const std::regex filename_extractor("[^(:/,.)]");

int main() {
    std::smatch filename_match;
    if(std::regex_search(filenames, filename_match, filename_extractor))
    {
        std::cout << "Number of filenames: " << filename_match.size() << std::endl;
        for(std::size_t i = 0; i < filename_match.size(); ++i)
        {
            std::cout << i << ": " << filename_match[i] << std::endl;
        }
    }

    return 0;
}

【问题讨论】:

  • 我怀疑你想要std::regex_replace,但这可能最好不要使用正则表达式。也许看看std::remove_if
  • 我不明白。您的字符串包含,:/,而不是:/,?你不能分开吗?

标签: c++ regex


【解决方案1】:

std::smatchsize() 返回子表达式的数量 + 1(与 (),您没有)。

解决方案

您需要反复拨打std::regex_search,或使用std::regex_iterator

此外,您的正则表达式实际上只搜索了一个字符。 您需要使用+ 来搜索最长的字符序列:[^(:/,.)]+

这是您的代码,包含来自cppreference.com 的示例:

#include <iostream>
#include <iterator>
#include <regex>
#include <string>

static const std::string filenames {R"(:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg,:/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg)"};
static const std::regex filename_extractor("[^(:/,.)]+");

int main() {
    auto files_begin = std::sregex_iterator(filenames.begin(), filenames.end(), filename_extractor);

    for (auto i = files_begin; i != std::sregex_iterator(); ++i) {
        std::string filename = i->str(); 
        std::cout << filename << '\n';
    }   

    return 0;
}

但是,这也会返回中间“目录”。 如果你使用正则表达式[^(:,)]+,你会得到期望你想要的结果:

/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/0_301_0.svg
/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02011.svg
/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02012.svg
/SymbolStandards/JMSymbology/MIL_STD_2525D_Symbols/02110000.svg

您的问题已解释

std::regex_search 仅搜索第一次出现的正则表达式,以及其中的任何子表达式。

例如,表达式ab([cd])([ef]) 将匹配字符串xxabcfxxabdef。 第一个匹配是 abcf 部分,c 是第一个子表达式 [cd] 的匹配,e 是第二个子表达式 [ef] 的匹配。

第二个匹配是abde(不是abdef!),其中e是第二个子表达式的匹配。

使用std::regex_search,您搜索第一个匹配项,匹配器返回完整的first 匹配项和子表达式的匹配项。 如果要查找更多匹配项,则必须从字符串的其余部分开始搜索 (std::smatch::suffix())。

此外,正则表达式[ef] 仅匹配单个字符。 [ef]+ 将匹配 es 和 fs 的最长序列。 因此,ab([cd])([ef]) 的第二个子表达式与上述目标字符串的匹配将匹配ef,而不仅仅是e

【讨论】:

    【解决方案2】:

    我认为std::regex_replace 是你需要的:

    #include <regex>
    #include <string>
    #include <iostream>
    
    const std::string filenames {R"(:/MIL_STD/0_3.svg,:/SS/2525D/02011.svg)"};
    const std::regex filename_extractor("[(:/,.)]");
    
    int main()
    {
        std::string r;
    
        std::regex_replace(std::back_inserter(r),
            filenames.begin(), filenames.end(), filename_extractor, "");
    
        std::cout << "before: " << filenames << '\n';
        std::cout << " after: " << r << '\n';
    }
    

    但是我认为正则表达式对于删除字符可能有点矫枉过正,您可以使用std::remove_copy_if 更有效地做到这一点:

    #include <string>
    #include <iostream>
    #include <algorithm>
    
    const std::string filenames {R"(:/MIL_STD/0_3.svg,:/SS/2525D/02011.svg)"};
    const std::string filename_extractor("(:/,.)");
    
    int main()
    {
        std::string r;
    
        std::remove_copy_if(filenames.begin(), filenames.end(),
            std::back_inserter(r), [](char c)
        {
            return filename_extractor.find(c) != std::string::npos;
        });
    
        std::cout << "before: " << filenames << '\n';
        std::cout << " after: " << r << '\n';
    }
    

    【讨论】:

      猜你喜欢
      • 2013-09-29
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多