使用std :: regex过滤输入

时间:2016-08-12 17:24:59

标签: c++ regex

我有一个字符串的丑陋,它由几个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

我想要做的是删除每次出现的字符:/.,,这样我就可以拥有一个有效文件名的字符串。

我已经编写了这个简单的正则表达式来表达jus:[^(:/,.)] 根据{{​​3}},它似乎是正确的正则表达式。

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

我对std :: 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;
}

2 个答案:

答案 0 :(得分:3)

size()的{​​{1}}会返回子表达式的数量+ 1(包含std::smatch(,而您没有。)

解决方案

您需要反复拨打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]与第二个子表达式{{1}匹配}}

第二个匹配是e部分(不是[ef]!),其中abde是第二个子表达式的匹配。

使用abdef,搜索第一个匹配项,匹配器返回完整的第一个匹配项以及子表达式的匹配项。 如果要查找更多匹配项,则必须从字符串的其余部分(e)开始搜索。

此外,正则表达式std::regex_search仅匹配单个字符。 std::smatch::suffix()会匹配[ef][ef]+ s的最长序列。 因此,上面目标字符串的e的第二个子表达式匹配将匹配f,而不仅仅是ab([cd])([ef])

答案 1 :(得分: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';
}