在 C++ 中使用 stringstream 获取字符串中的所有 N 个连续字符

Posted

技术标签:

【中文标题】在 C++ 中使用 stringstream 获取字符串中的所有 N 个连续字符【英文标题】:Get all N consecutive characters in string using stringstream in C++ 【发布时间】:2021-09-15 01:42:15 【问题描述】:

我想要一个可以将std::string 对象窗口化为长度为N 的分区的东西 - 例如(使用函数update):

int main() 
  std::string s = "abcdefg";
  update<2>(s);
  return 0;

调用上面应该会导致:

ab
bc
cd
ef
fg

我有以下版本的update 函数:

template<std::size_t size>
void update(std::string s) 
    std::string result(size, '\0');
    std::stringstream sss;
    int iterations = s.length() - size;
    for (int i = 0; i<iterations; i++) 
        ss.read(&result[0], result.size());
        std::cout << result << std::endl;
    
    return;

但这会跳过初始字符位于奇数索引处的组合(在我的情况下组合的数量是正确的,即使有重复)

ab
cd
ef
gf
gf

附带说明的是,如果有任何尾随字符,则应从打印值中省略这些字符(尽管我认为这将被for 循环的参数所涵盖)

最后一点是,我希望它尽可能优化,因为我通常会使用长度非常大的字符串(>5M 个字符长) - 我目前的解决方案可能不是最好的,所以我很开放到替代策略的建议。

【问题讨论】:

我使用的一条黄金法则是,当我可以命名一个进程的一部分时,这暗示它应该是一个函数本身。在你的情况下,我会分开读取输入和查找那些连续的字符串,编写一个函数std::set&lt;std::string&gt; consecutive(const std::string&amp; input, size_t length)。这样,您只需要调试不起作用的部分而不是整个事情。您可以重复使用它,例如在不读取用户输入的上下文中。 【参考方案1】:

使用 C++17,您可以这样做,这样更具可读性:

void update(std::string_view s, int size) 
    const int iterations = s.size() - size;
    for (int i = 0; i <= iterations; i++) 
        std::cout << s.substr(i, size) << "\n";
    

string_view 正是为此目的而制作的,用于快速读取字符串。 string_view::substr 是 const 复杂度,而 string::substr 是线性的。

附带说明,除了 Nick 提到的,您的代码还有一些其他小问题:

std::endl fflush 流,它严重影响性能。在这里你可以使用 '\n' 来换行。 最后的 return 绝对是多余的,void 函数不需要 return 模板化的目的是什么?这将很容易使您的代码膨胀,而没有任何可衡量的性能提升。只需将 N 作为参数传递即可。 你的 main 也被声明为 void 并且应该是 int(更是如此,因为你确实在最后返回了一个值)

【讨论】:

感谢您的回答 - 这肯定很有帮助,最后还有 cmets - 如果它比其他答案更能满足我的需求,我会尝试解决方案并接受答案【参考方案2】:

使用 range-v3,您可以使用 sliding 视图:

std::string s = "abcdefg";
for (auto r : s | ranges::views::sliding(2)) 
    std::cout << r << std::endl;

Demo

【讨论】:

这看起来不错 - 我会试试看它是否最适合我!【参考方案3】:

您对 ss.read 的调用将始终读取两个字符,然后将字符串流中的 ptr 推进 2 个字符。所以你永远不会在每一行的开头阅读/重复前一个字符。

如果你想“按你的方式”做,那么你必须单独跟踪最后一个字符。


    #include <iostream>
    #include <sstream>
    
    template<std::size_t size>
    void update(std::string s) 
        std::string result(size, '\0');
        char lastChar;
        std::stringstream sss;
        int iterations = s.length() - size;
        int read = 0;
        if (ss.readsome(&result[0], 1)) 
            lastChar = result[0];
        
        for (int i = 0; i < iterations; i++) 
            if (read = ss.readsome(&result[0], size - 1)) 
                std::cout << lastChar << result << std::endl;
                lastChar = result[read - 1];
            
        
    

话虽如此,以上绝对不是最好的方法性能明智。您应该能够在没有任何字符串流或读取函数的情况下完成所有这些操作,只需迭代字符串。像这样的


    #include <iostream>
    
    void update(std::string s, size_t size) 
        int len = s.length();
        for (int i = 1; i < len; i+=size-1) 
            fwrite(&s[i-1], size, 1, stdout);
            putchar('\n');
        
    

【讨论】:

我觉得最后一个sn-p是我需要的!感谢您的详细回答,如果它比其他人更好地满足我的需求,我会接受它 如果性能不太重要,我会使用 Jarod42 使用子字符串的示例,它更具可读性。我相当确定 substr 构造了一个新字符串(我试图避免)。 使用substr的是styku,是来自std::string_view而不是来自std::string。 :) 哎呀!您是对的,您的解决方案看起来也很可行且非常干净(尽管我不熟悉范围库或它的性能)。

以上是关于在 C++ 中使用 stringstream 获取字符串中的所有 N 个连续字符的主要内容,如果未能解决你的问题,请参考以下文章

C++ Stringstream 只拾取第一个字符串

使用 stringstream::str() 更新后 C++ stringstream 无法正常工作

❥关于C++之stringstream典型用法

❥关于C++之stringstream典型用法

使用 ostringstream 或 stringstream 将 C++ Int 转换为字符串

C++使用stringstream分割字符串