c++ 如何在不进行任何动态分配的情况下将string_view拆分为多个string_view对象

wj8zmpe1  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(195)

下面的片段来自这个答案。

#include <string>
#include <vector>

void tokenize(std::string str, std::vector<string> &token_v){
    size_t start = str.find_first_not_of(DELIMITER), end=start;

    while (start != std::string::npos){
        // Find next occurence of delimiter
        end = str.find(DELIMITER, start);
        // Push back the token found into vector
        token_v.push_back(str.substr(start, end-start));
        // Skip all occurences of the delimiter to find new start
        start = str.find_first_not_of(DELIMITER, end);
    }
}

对于这样的缓冲器:

std::array<char, 150> buffer;

我想有一个sting_view(指向缓冲区),并将其传递给tokenizer函数,令牌应该通过out参数以std::string_view s的形式返回(而不是向量),它还将返回提取的令牌的数量。

size_t tokenize( const std::string_view inputStr,
                 const std::span< std::string_view > foundTokens_OUT,
                 const size_t expectedTokenCount )
{
    // implementation
}

int main( )
{
    std::array<char, 150> buffer { " @a hgs -- " };
    const std::string_view sv { buffer.data( ), buffer.size( ) };
    const size_t expectedTokenCount { 4 };

    std::array< std::string_view, expectedTokenCount > foundTokens; // the span for storing found tokens

    const size_t num_of_found_tokens { tokenize( sv, foundTokens, expectedTokenCount ) };

    if ( num_of_found_tokens == expectedTokenCount )
    {
        // do something
        std::clog << "success\n" << num_of_found_tokens << '\n';
    }

    for ( size_t idx { }; idx < num_of_found_tokens; ++idx )
    {
        std::cout << std::quoted( foundTokens[ idx ] ) << '\n';
    }
}

如果有人能实现一个类似的tokenize函数,但只针对基于空格和制表符拆分的string_view,我将不胜感激。(不支持选项卡)。另外,我希望这个函数在inputStr中找到的标记数超过expectedTokenCount时停止工作并返回expectedTokenCount + 1。这样显然效率更高。
下面是我的虚拟版本:

size_t tokenize( const std::string_view inputStr,
                 const std::span< std::string_view > foundTokens_OUT,
                 const size_t expectedTokenCount )
{
    if ( inputStr.empty( ) )
    {
        return 0;
    }

    size_t start { inputStr.find_first_not_of( ' ' ) };
    size_t end { start };

    size_t foundTokensCount { };

    while ( start != std::string_view::npos && foundTokensCount < expectedTokenCount )
    {
        end = inputStr.find( ' ', start );
        foundTokens_OUT[ foundTokensCount++ ] = inputStr.substr( start, end - start );
        start = inputStr.find_first_not_of( ' ', end );
    }

    return foundTokensCount;
}

注意:范围库还没有适当的支持(至少在GCC上),所以我试图避免这种情况。

yfjy0ee7

yfjy0ee71#

我试着自己写一个,但它没有像预期的那样工作(不支持标签)。
如果你想支持用空格和制表符拆分,那么你可以使用find_first_not_of的另一个重载:

size_type find_first_not_of(const CharT* s, size_type pos = 0) const;

它将发现第一个字符不等于由s指向的字符串中的任何字符。
因此,您的实现只需将find_first_not_of(' ')find(' ')更改为find_first_not_of(" \t")find_first_of(" \t")
Demo

lvjbypge

lvjbypge2#

这是我的实现(我之前写的),它可以处理以一个或多个分隔符开头、重复分隔符和以一个或多个分隔符结尾的输入:
它使用string_views来处理所有的事情,所以没有内存分配,但是要小心不要过早地丢弃输入字符串。string_views毕竟是非所有的。
在线演示:https://onlinegdb.com/tytGlOVnk

#include <vector>
#include <string_view>
#include <iostream>

auto tokenize(std::string_view string, std::string_view delimiters)
{
    std::vector<std::string_view> substrings;
    if (delimiters.size() == 0ul)
    {
        substrings.emplace_back(string);
        return substrings;
    }

    auto start_pos = string.find_first_not_of(delimiters);
    auto end_pos = start_pos;
    auto max_length = string.length();

    while (start_pos < max_length)
    {
        end_pos = std::min(max_length, string.find_first_of(delimiters, start_pos));

        if (end_pos != start_pos)
        {
            substrings.emplace_back(&string[start_pos], end_pos - start_pos);
            start_pos = string.find_first_not_of(delimiters, end_pos);
        }
    }

    return substrings;
}

int main()
{
    std::string_view test{ "The, quick! and brown fox. Jumped : over the lazy dog, or did he?" };

    auto tokens = tokenize(test, " ,!.?:");

    for (const auto token : tokens)
    {
        std::cout << token << "\n";
    }

    return 0;
}

相关问题