Bir dizeyi C ++ 'da değişken sayıda kez nasıl tekrar edebilirim?


127

C ++ 'da bir dizenin başına' n 'boşluk (veya herhangi bir dize) eklemek istiyorum. Std :: strings veya char * strings kullanarak bunu yapmanın doğrudan bir yolu var mı?

Örneğin Python'da şunları yapabilirsiniz:

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

Birisi QString kullanarak bir cevap mı veriyor?
Akiva

Yanıtlar:



39

Python'daki * operatörüne veya Perl'deki x operatörüne eşdeğer C ++ 'da dizeleri tekrar etmenin doğrudan deyimsel bir yolu yoktur . Tek bir karakteri tekrarlıyorsanız, iki bağımsız değişkenli kurucu (önceki cevaplarda önerildiği gibi) iyi çalışır:

std::string(5, '.')

Bu, bir dizgeyi n kez yinelemek için bir ostringstream'i nasıl kullanabileceğinizin uydurma bir örneğidir:

#include <sstream>

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

Gerçekleştirmeye bağlı olarak, bu, dizeyi n kez bitiştirmekten biraz daha verimli olabilir.


17

String :: insert biçimlerinden birini kullanın:

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

Bu, dizinin başına (konum 0) "....." (beş nokta) ekleyecektir.


16
OP, bir karakter değil, bir dizeyi tekrar etmesini istedi.
Brent

@Brent OP hem - "'n' boşlukları (veya herhangi bir dizge)" istedi ve ardından dizge olarak tek bir nokta ile niyetini göstermeye devam etti. İngilizce pek çok insanın ilk dili değildir, bu yüzden bazen onların gereksinimlerini tam olarak tahmin etmeniz gerekir ve analiz edildiğinde asıl soru, bunu tek bir karakterle nasıl yapacağınızı sormaktır. Cevabımı, olumsuz oy vermeniz için yeterince yardımcı bulmadığınız için üzgünüm.
camh

13

Bunun eski bir soru olduğunu biliyorum, ama ben de aynı şeyi yapmak istiyordum ve daha basit bir çözüm olduğunu düşündüğüm şeyi buldum. Görünüşe göre cout, cout.fill () ile yerleşik bu işleve sahiptir, 'tam' açıklama için bağlantıya bakın

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

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

çıktılar

.....lolcat

6
sadece noktalar: son satırı şu şekilde değiştir ...cout << "" << endl;
musefan

9

OP tarafından sağlanan Örnek olması amacıyla std :: dize ait ctor yeterlidir: std::string(5, '.'). Bununla birlikte, herhangi biri std :: string'i birden çok kez tekrarlayacak bir işlev arıyor ise:

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

8

Commodore Jaeger'in bahsettiği gibi, diğer yanıtların hiçbirinin bu soruyu gerçekten yanıtladığını sanmıyorum; soru bir karakterin değil bir dizenin nasıl tekrarlanacağını sorar.

Commodore'un verdiği cevap doğru olsa da oldukça verimsiz. İşte daha hızlı bir uygulama, fikir, önce dizeyi katlanarak büyüterek kopyalama işlemlerini ve bellek ayırmalarını en aza indirmektir:

#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;
}

Bir operator*şeyi Python sürümüne yaklaştırmak için de tanımlayabiliriz :

#include <utility>

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

Benim makinemde bu, Commodore tarafından verilen uygulamadan yaklaşık 10 kat daha hızlı ve saf bir 'ekleme n - 1 kez' çözümünden yaklaşık 2 kat daha hızlı .


Uygulamanız, 'kopyalamayı en aza indirmez'. İçinizdeki +=for döngüsünün kendi içinde de str.size()yinelemeler yapan bir tür döngüye sahip olduğunu unutmayın . str.size()her dış döngü yinelemesinde büyür, bu nedenle her dış döngüden sonra iç döngü daha fazla yineleme yapmak zorundadır. Sizin ve saf 'n kez kopya' uygulaması, toplam her iki kopya n * periodkarakterinde. Uygulamanız, başlangıç ​​nedeniyle yalnızca bir bellek ayırma yapar reserve. Sanırım uygulamanızı oldukça küçük strve büyük olarak profilini çıkardınız n, ama aynı zamanda büyük strve küçük ile değil n.
Florian Kaufmann

@FlorianKaufmann Cevabıma neden saldırmayı seçtiğinden emin değilim. Ancak "kopyalamayı en aza indir" derken "kopyalama işlemleri" ni kastediyorum. Az sayıda büyük bloğun kopyalanmasının (çeşitli nedenlerle) çok sayıda küçük bloğun kopyalanmasından daha etkilidir. Girdi dizesinde saf yöntem üzerinden ek bir tahsisattan kaçınıyorum.
Daniel

2
Verimlilik açısından çözümünüzün naif çözümden çok daha iyi olduğu iddianıza inanmadığımı belirten bir yorumdu. Ölçümlerimde, naif çözüme kıyasla, kodunuz küçük dizeler ve birçok tekrarla daha hızlı, ancak uzun dizeler ve birkaç tekrarla daha yavaş. Birkaç büyük bloğu kopyalamanın birçok küçük bloğu kopyalamaktan daha yüksek bir performansa sahip olmasının çeşitli nedenlerini daha ayrıntılı olarak açıklayan bağlantılar sağlayabilir misiniz? Dal tahmini düşünebiliyorum. CPU önbelleği ile ilgili olarak hangi varyantın tercih edildiğinden emin değilim.
Florian Kaufmann

@FlorianKaufmann İki yaklaşım arasında büyük strve küçük narasında önemli bir fark görmüyorum . Bunun branş tahmininden çok genel boru hattıyla ilgisi olduğuna inanıyorum , ayrıca dikkate alınması gereken veri hizalama sorunları da var . Bunun neden daha işlemci / bellek dostu olduğuna dair ayrıntılar için yeni bir soru sormalısınız, eminim çok ilgi çekecek ve burada verebileceğimden daha iyi bir cevap alacaktır.
Daniel

1
@FlorianKaufmann: x86'da rep movsb, en azından orta ila büyük kopyalar için kopyalama yapmanın en etkili yollarından biridir. Mikro kodlu uygulaması bazı sabit başlangıç ​​ek yüküne sahiptir (hem AMD hem de Intel'de), örneğin Sandybridge'de ~ 15 ila 40 döngü, artı 64B önbellek satırı başına 4 döngü (en iyi durum) . Küçük kopyalar için, bir SSE döngüsü en iyisidir çünkü başlangıç ​​ek yüküne sahip değildir. Ama sonra dallara yönelik yanlış tahminlere tabi.
Peter Cordes


5

ITNOA

Bunu yapmak için C ++ işlevini kullanabilirsiniz.

 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" ne anlama geliyor? İnternette herhangi bir referans bulamıyorum.
folling
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.