使用std :: regex拆分一行并丢弃空元素

时间:2017-05-18 19:47:03

标签: c++ stl

我需要根据两个分隔符分割一行:' ';

以示例:

input : " abc  ; def  hij  klm  "
output: {"abc","def","hij","klm"}

如何修复下面的函数以丢弃第一个空元素?

std::vector<std::string> Split(std::string const& line) {
    std::regex seps("[ ;]+");
    std::sregex_token_iterator rit(line.begin(), line.end(), seps, -1);
    return std::vector<std::string>(rit, std::sregex_token_iterator());
}

// input : " abc  ; def  hij  klm  "
// output: {"","abc","def","hij","klm"}

下面是编译的完整示例:

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

std::vector<std::string> Split(std::string const& line) {
    std::regex seps("[ ;]+");
    std::sregex_token_iterator rit(line.begin(), line.end(), seps, -1);
    return std::vector<std::string>(rit, std::sregex_token_iterator());
}

int main()
{
    std::string line = " abc  ; def  hij  klm  ";
    std::cout << "input: \"" << line << "\"" << std::endl;

    auto collection = Split(line);

    std::cout << "output: {";
    auto bComma = false;
    for (auto oneField : collection)
    {
        std::cout << (bComma ? "," : "") << "\"" << oneField << "\"";
        bComma = true;
    }
    std::cout << "} " << std::endl;
}

4 个答案:

答案 0 :(得分:3)

到目前为止,我可以看到除了其他问题中提到的几种可能性之外的其他可能性。第一个是在构建向量时使用std::remove_copy_if

// regex stuff here
std::vector<std::string> tokens;
std::remove_copy_if(rit, std::sregex_token_iterator(), 
                    std::back_inserter(tokens),
                    [](std::string const &s) { return s.empty(); });

另一种可能性是创建一个适当地对字符进行分类的区域设置,并从那里读取:

struct reader: std::ctype<char> {
    reader(): std::ctype<char>(get_table()) {}
    static std::ctype_base::mask const* get_table() {
        static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());

        rc[' '] = std::ctype_base::space;
        rc[';'] = std::ctype_base::space;

        // at a guess, newlines are probably still separators too:
        rc['\n'] = std::ctype_base::space;
        return &rc[0];
    }
};

一旦我们有了这个,我们告诉流在读取(或写入)流时使用该语言环境:

std::stringstream input(" abc  ; def  hij  klm  ");

input.imbue(std::locale(std::locale(), new reader));

然后我们可能想要清除代码,只在标记之间插入逗号,而不是在每个标记之后。幸运的是,我写了一些code to handle that fairly neatly some time ago。使用它,我们可以相当简单地从上面的输入复制标记到标准输出:

std::cout << "{ ";
std::copy(std::istream_iterator<std::string>(input), {}, 
    infix_ostream_iterator<std::string>(std::cout, ", "));  
std::cout << " }";

结果:“{abc,def,hij,klm}”,正如您所期望/希望的那样 - 没有任何额外的克服可以弥补它开始做错事。

答案 1 :(得分:2)

你总是可以在函数末尾添加一个额外的步骤来完全删除空字符串,使用erase-remove idiom

std::vector<std::string> Split(std::string const& line) {
    std::regex seps("[ ;]+");
    std::sregex_token_iterator rit(line.begin(), line.end(), seps, -1);
    auto tokens = std::vector<std::string>(rit, std::sregex_token_iterator());
    tokens.erase(std::remove_if(tokens.begin(),
                                tokens.end(),
                                [](std::string const& s){ return s.empty(); }),
                 tokens.end());
    return tokens;
}

答案 2 :(得分:1)

如果你不想在填充它后从向量中删除元素,你也可以遍历迭代器范围并构建跳过空匹配的向量,如

std::vector<std::string> Split(std::string const& line) {
    std::regex seps("[ ;]+");
    std::sregex_token_iterator rit(line.begin(), line.end(), seps, -1), end;
    std::vector<std::string> tokens;
    for(;rit != end; ++rit);
        if (rit->length() != 0)
            tokens.push_back(*rit)
    return tokens;
}

答案 3 :(得分:0)

如果有人想使用std :: remove_copy_if复制基于Jerry Coffin输入修改的功能:

std::vector<std::string> SplitLine(std::string const& line, const std::regex seps) 
{
    std::sregex_token_iterator rit(line.begin(), line.end(), seps, -1);
    std::vector<std::string> tokens;
    std::remove_copy_if(rit, std::sregex_token_iterator(),
        std::back_inserter(tokens),
        [](std::string const &s) { return s.empty(); });
    return tokens;
}