我在c++中使用以下方法解析字符串:

using namespace std;

string parsed,input="text to be parsed";
stringstream input_stringstream(input);

if (getline(input_stringstream,parsed,' '))
{
     // do some processing.
}

使用单个字符分隔符进行解析是可以的。但是如果我想使用字符串作为分隔符呢?

例子:我想拆分:

scott>=tiger

用>=作为分隔符,这样我就可以得到斯科特和老虎。


当前回答

以下是我的看法。它处理边缘情况,并接受一个可选参数从结果中删除空条目。

bool endsWith(const std::string& s, const std::string& suffix)
{
    return s.size() >= suffix.size() &&
           s.substr(s.size() - suffix.size()) == suffix;
}

std::vector<std::string> split(const std::string& s, const std::string& delimiter, const bool removeEmptyEntries = false)
{
    std::vector<std::string> tokens;

    for (size_t start = 0, end; start < s.length(); start = end + delimiter.length())
    {
         size_t position = s.find(delimiter, start);
         end = position != std::string::npos ? position : s.length();

         std::string token = s.substr(start, end - start);
         if (!removeEmptyEntries || !token.empty())
         {
             tokens.push_back(token);
         }
    }

    if (!removeEmptyEntries &&
        (s.empty() || endsWith(s, delimiter)))
    {
        tokens.push_back("");
    }

    return tokens;
}

例子

split("a-b-c", "-"); // [3]("a","b","c")

split("a--c", "-"); // [3]("a","","c")

split("-b-", "-"); // [3]("","b","")

split("--c--", "-"); // [5]("","","c","","")

split("--c--", "-", true); // [1]("c")

split("a", "-"); // [1]("a")

split("", "-"); // [1]("")

split("", "-", true); // [0]()

其他回答

template<typename C, typename T>
auto insert_in_container(C& c, T&& t) -> decltype(c.push_back(std::forward<T>(t)), void()) {
    c.push_back(std::forward<T>(t));
}
template<typename C, typename T>
auto insert_in_container(C& c, T&& t) -> decltype(c.insert(std::forward<T>(t)), void()) {
    c.insert(std::forward<T>(t));
}
template<typename Container>
Container splitR(const std::string& input, const std::string& delims) {
    Container out;
    size_t delims_len = delims.size();
    auto begIdx = 0u;
    auto endIdx = input.find(delims, begIdx);
    if (endIdx == std::string::npos && input.size() != 0u) {
        insert_in_container(out, input);
    }
    else {
        size_t w = 0;
        while (endIdx != std::string::npos) {
            w = endIdx - begIdx;
            if (w != 0) insert_in_container(out, input.substr(begIdx, w));
            begIdx = endIdx + delims_len;
            endIdx = input.find(delims, begIdx);
        }
        w = input.length() - begIdx;
        if (w != 0) insert_in_container(out, input.substr(begIdx, w));
    }
    return out;
}

我查看了答案,没有看到一个基于迭代器的方法可以被送入范围循环,所以我做了一个。

这使用了c++ 17 string_views,所以它不应该分配字符串的副本。

struct StringSplit
{
    struct Iterator
    {
        size_t tokenStart_ = 0;
        size_t tokenEnd_ = 0;
        std::string str_;
        std::string_view view_;
        std::string delimiter_;
        bool done_ = false;

        Iterator()
        {
            // End iterator.
            done_ = true;
        }

        Iterator(std::string str, std::string delimiter)
            : str_{std::move(str)}, view_{str_}, delimiter_{
                                                     std::move(delimiter)}
        {
            tokenEnd_ = view_.find(delimiter_, tokenStart_);
        }

        std::string_view operator*()
        {
            return view_.substr(tokenStart_, tokenEnd_ - tokenStart_);
        }

        Iterator &operator++()
        {
            if (tokenEnd_ == std::string::npos)
            {
                done_ = true;
                return *this;
            }

            tokenStart_ = tokenEnd_ + delimiter_.size();
            tokenEnd_ = view_.find(delimiter_, tokenStart_);
            return *this;
        }

        bool operator!=(Iterator &other)
        {
            // We only check if both points to the end.
            if (done_ && other.done_)
            {
                return false;
            }

            return true;
        }
    };

    Iterator beginIter_;

    StringSplit(std::string str, std::string delim)
        : beginIter_{std::move(str), std::move(delim)}
    {
    }

    Iterator begin()
    {
        return beginIter_;
    }

    Iterator end()
    {
        return Iterator{};
    }
};

示例用法如下:

int main()
{
    for (auto token : StringSplit{"<>foo<>bar<><>bar<><>baz<><>", "<>"})
    {
        std::cout << "TOKEN: '" << token << "'" << std::endl;
    }
}

打印:

TOKEN: ''
TOKEN: 'foo'
TOKEN: 'bar'
TOKEN: ''
TOKEN: 'bar'
TOKEN: ''
TOKEN: 'baz'
TOKEN: ''
TOKEN: ''

它正确地处理字符串开头和结尾的空项。

该方法使用std::string::find,而不改变原始字符串,记住前一个子字符串标记的开始和结束。

#include <iostream>
#include <string>

int main()
{
    std::string s = "scott>=tiger";
    std::string delim = ">=";

    auto start = 0U;
    auto end = s.find(delim);
    while (end != std::string::npos)
    {
        std::cout << s.substr(start, end - start) << std::endl;
        start = end + delim.length();
        end = s.find(delim, start);
    }

    std::cout << s.substr(start, end);
}

以防将来,有人想跳出Vincenzo Pii答案的盒子函数

#include <vector>
#include <string>


std::vector<std::string> SplitString(
    std::string str,
    std::string delimeter)
{
    std::vector<std::string> splittedStrings = {};
    size_t pos = 0;

    while ((pos = str.find(delimeter)) != std::string::npos)
    {
        std::string token = str.substr(0, pos);
        if (token.length() > 0)
            splittedStrings.push_back(token);
        str.erase(0, pos + delimeter.length());
    }

    if (str.length() > 0)
        splittedStrings.push_back(str);
    return splittedStrings;
}

我还修复了一些错误,以便如果字符串的开头或结尾有分隔符,函数将不会返回空字符串

这个方法使用字符串find和字符串substr

vector<string> split(const string& str,const string delim){
vector<string> vtokens; 
size_t start = 0;
size_t end = 0;
while((end = str.find(delim,start))!=string::npos){
    vtokens.push_back(str.substr(start,end-start));
    start = end +1;
}
vtokens.push_back(str.substr(start));
return vtokens;
}