如何在C ++中将字符串重复可变的次数?


127

我想在C ++中的字符串开头插入“ n”个空格(或任何字符串)。是否可以使用std :: strings或char *字符串直接进行此操作?

例如,在Python中,您可以轻松完成

>>> "." * 5 + "lolcat"
'.....lolcat'
c++ 

有人使用QString提供答案吗?
Akiva

Answers:



39

在C ++中,没有直接惯用的方式来重复字符串,等效于Python中的*运算符或Perl中的x运算符。如果您要重复一个字符,则由两个参数组成的构造函数(如先前答案所建议)可以很好地工作:

std::string(5, '.')

这是一个人为设计的示例,说明如何使用ostringstream重复一个字符串n次:

#include <sstream>

std::string repeat(int n) {
    std::ostringstream os;
    for(int i = 0; i < n; i++)
        os << "repeat";
    return os.str();
}

取决于实现方式,这可能比简单地串联字符串n次更为有效。


17

使用string :: insert的一种形式:

std::string str("lolcat");
str.insert(0, 5, '.');

这将在字符串的开头(位置0)插入“ .....”(五个点)。


15
OP要求重复字符串,而不是字符。
布伦特

@Brent OP要求同时输入两个-“'n'空格(或任何字符串)”,然后继续以单个句点作为字符串来证明其意图。英语不是很多人的第一语言,因此您有时需要猜测他们的确切要求,而在分析后,问题实际上是在问如何用单个字符做到这一点。对不起,您发现我的回答无济于事,您需要对此投反对票。
camh'Dec

13

我知道这是一个古老的问题,但是我一直想做同样的事情,并且发现了我认为更简单的解决方案。看来cout具有使用cout.fill()内置的此功能,请参阅链接以获取“完整”说明

http://www.java-samples.com/showtutorial.php?tutorialid=458

cout.width(11);
cout.fill('.');
cout << "lolcat" << endl;

输出

.....lolcat

6
仅点:将最后一行更改为...cout << "" << endl;
musefan 2012年

9

对于由OP提供的例子的目的的std :: string的构造函数就足够了:std::string(5, '.')。但是,如果有人正在寻找一个函数来重复std :: string多次:

std::string repeat(const std::string& input, unsigned num)
{
    std::string ret;
    ret.reserve(input.size() * num);
    while (num--)
        ret += input;
    return ret;
}

8

正如Jaeger准将所说,我认为没有其他答案能真正回答这个问题。这个问题问如何重复一个字符串,而不是一个字符。

尽管准将给出的答案是正确的,但效率很低。这是一个更快的实现,其思想是通过首先指数增长字符串来最大程度地减少复制操作和内存分配:

#include <string>
#include <cstddef>

std::string repeat(std::string str, const std::size_t n)
{
    if (n == 0) {
        str.clear();
        str.shrink_to_fit();
        return str;
    } else if (n == 1 || str.empty()) {
        return str;
    }
    const auto period = str.size();
    if (period == 1) {
        str.append(n - 1, str.front());
        return str;
    }
    str.reserve(period * n);
    std::size_t m {2};
    for (; m < n; m *= 2) str += str;
    str.append(str.c_str(), (n - (m / 2)) * period);
    return str;
}

我们还可以定义一个operator*以获得更接近Python版本的信息:

#include <utility>

std::string operator*(std::string str, std::size_t n)
{
    return repeat(std::move(str), n);
}

在我的机器上,这比Commodore提供的实现快大约10倍,比简单的“追加n-1次”解决方案快大约2


您的实现不会“最小化复制”。请注意,+=for循环内部的for循环中也有某种循环会进行str.size()迭代。str.size()在每个外循环迭代中增长,因此在每个外循环之后,内循环必须执行更多的迭代。您和天真的“复制n次”实现总共复制了两个n * period字符。由于initial,您的实现只执行一次内存分配reserve。我猜你有一个相当小的异形你的实现str和一个大的n,但还没有大str和小n
Florian Kaufmann

@FlorianKaufmann不确定为什么选择攻击我的答案。但是,“最小复制”是指“复制操作”。这样的想法是,由于各种原因,复制少量大块比复制大量小块更有效。我可能会避免在朴素的方法上对输入字符串进行额外的分配。
丹尼尔(Daniel)

2
这是一条评论,指出我不相信您所说的解决方案在效率方面要比单纯的解决方案好得多。在我的测量中,与幼稚的解决方案相比,使用较小的字符串和许多重复项,您的代码会更快,但是使用较长的字符串和少量重复项,您的代码会更慢。您能否提供链接来详细解释为什么复制几个大块比复制许多小块具有更高性能的各种原因?我可以想到分支预测。关于CPU缓存,我不确定首选哪个版本。
Florian Kaufmann

@FlorianKaufmann我看到大无显著差异str,小n两种方法之间。我相信,这与整体流水线的关系远多于分支预测本身,还有数据对齐问题需要考虑。您应该问一个新的问题,为什么它对处理器/内存更友好,我敢肯定它会引起很多兴趣,并且得到比我在此给出的更好的答案。
丹尼尔(Daniel)

1
@FlorianKaufmann:在x86上,这rep movsb是最有效的复制方法之一,至少对于中型到大型复制而言。它的微编码实现具有一些恒定的启动开销(在AMD和Intel上),例如在Sandybridge上,大约15至40个周期,加上每条64B高速缓存行4个周期(最佳情况)。对于小副本,最好使用SSE循环,因为它没有启动开销。但这会受到分支错误预测的影响。
彼得·科德斯


5

ITNOA

您可以使用C ++函数执行此操作。

 std::string repeat(const std::string& input, size_t num)
 {
    std::ostringstream os;
    std::fill_n(std::ostream_iterator<std::string>(os), num, input);
    return os.str();
 }

1
“ ITNOA”到底是什么意思?找不到在线参考。
Folling
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.