วิธีการทำซ้ำสตริงตัวแปรหลายครั้งใน C ++?


127

ฉันต้องการแทรกช่องว่าง 'n' (หรือสตริงใด ๆ ) ที่จุดเริ่มต้นของสตริงใน C ++ มีวิธีโดยตรงในการดำเนินการโดยใช้ std :: strings หรือ char * strings หรือไม่

เช่นใน Python คุณสามารถทำได้

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

มีคนให้คำตอบโดยใช้ QString?
Akiva

คำตอบ:



39

ไม่มีวิธีสำนวนโดยตรงในการทำซ้ำสตริงใน C ++ เทียบเท่ากับตัวดำเนินการ*ใน Python หรือตัวดำเนินการxใน Perl หากคุณทำซ้ำอักขระเดียวตัวสร้างสองอาร์กิวเมนต์ (ตามที่คำตอบก่อนหน้านี้แนะนำ) จะทำงานได้ดี:

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)


16
OP ขอให้ทำซ้ำสตริงไม่ใช่อักขระ
Brent

@ เบรนต์ OP ขอทั้ง - "'n' ช่องว่าง (หรือสตริงใดก็ได้)" จากนั้นก็แสดงเจตนาของพวกเขาโดยใช้จุดเดียวเป็นสตริง ภาษาอังกฤษไม่ใช่ภาษาแรกของคนจำนวนมากดังนั้นบางครั้งคุณต้องเดาความต้องการที่แน่นอนของพวกเขาและเมื่อวิเคราะห์แล้วคำถามก็คือถามว่าจะทำอย่างไรโดยใช้อักขระตัวเดียว ฉันขอโทษที่คุณพบว่าคำตอบของฉันไม่เป็นประโยชน์มากพอที่คุณต้องลงคะแนน
แคม

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

9

สำหรับวัตถุประสงค์ของตัวอย่างที่มีให้โดย OP ที่มาตรฐาน :: ctor 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;
}

นอกจากนี้เรายังสามารถกำหนด an 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 เท่า


การนำไปใช้ของคุณไม่ได้ 'ลดการคัดลอก' โปรดทราบว่า+=ภายในสำหรับลูปของคุณภายในยังมีการวนซ้ำบางประเภทที่str.size()ทำซ้ำ str.size()เพิ่มขึ้นในการวนซ้ำรอบนอกแต่ละครั้งดังนั้นหลังจากการวนซ้ำรอบนอกแต่ละครั้งวงในจะต้องทำซ้ำมากขึ้น การใช้งาน 'copy n times' ของคุณและไร้เดียงสาโดยรวมทั้งn * periodอักขระสำเนา reserveการดำเนินงานของคุณไม่จัดสรรหน่วยความจำเพียงหนึ่งเพราะการเริ่มต้น ผมคิดว่าคุณประวัติการดำเนินงานของคุณด้วยค่อนข้างเล็กstrและขนาดใหญ่nแต่ไม่ได้ยังมีขนาดใหญ่และขนาดเล็กstr n
Florian Kaufmann

@FlorianKaufmann ไม่แน่ใจว่าทำไมคุณถึงเลือกโจมตีคำตอบของฉัน แต่โดย "ลดการคัดลอก" ฉันหมายถึง 'การดำเนินการคัดลอก' แนวคิดที่ว่าการคัดลอกบล็อกขนาดใหญ่จำนวนน้อยจะมีประสิทธิภาพมากกว่า (ด้วยเหตุผลหลายประการ) มากกว่าการคัดลอกบล็อกขนาดเล็กจำนวนมาก ฉันอาจหลีกเลี่ยงการจัดสรรเพิ่มเติมในสตริงอินพุตด้วยวิธีไร้เดียงสา
Daniel

2
เป็นความคิดเห็นที่ระบุว่าฉันไม่เชื่อคำกล่าวอ้างของคุณว่าโซลูชันของคุณดีกว่าในแง่ของประสิทธิภาพมากกว่าโซลูชันที่ไร้เดียงสา ในการวัดของฉันเมื่อเทียบกับโซลูชันที่ไร้เดียงสารหัสของคุณจะเร็วกว่าด้วยสตริงขนาดเล็กและการทำซ้ำหลายครั้ง แต่ช้ากว่าด้วยสตริงที่ยาวและการทำซ้ำน้อย คุณช่วยอธิบายรายละเอียดเพิ่มเติมได้ไหมว่าทำไมการคัดลอกบล็อกขนาดใหญ่สองสามบล็อกจึงมีประสิทธิภาพสูงกว่าการคัดลอกบล็อกขนาดเล็กจำนวนมาก ฉันสามารถนึกถึงการทำนายสาขา เกี่ยวกับแคชของ CPU ฉันไม่แน่ใจว่าควรใช้ตัวแปรใด
Florian Kaufmann

@FlorianKaufmann ฉันไม่เห็นความแตกต่างอย่างมีนัยสำคัญสำหรับใหญ่strและเล็กnระหว่างสองแนวทางนี้ ฉันเชื่อว่านี่เป็นสิ่งที่เกี่ยวข้องกับไปป์ไลน์โดยรวมมากกว่าการคาดการณ์รายสาขานอกจากนี้ยังมีประเด็นการจัดตำแหน่งข้อมูลที่ต้องพิจารณา คุณควรถามคำถามใหม่สำหรับรายละเอียดว่าเหตุใดจึงเป็นมิตรกับโปรเซสเซอร์ / หน่วยความจำมากกว่าฉันแน่ใจว่ามันจะได้รับความสนใจเป็นอย่างมากและได้รับคำตอบที่ดีกว่าที่ฉันจะให้ได้
Daniel

1
@FlorianKaufmann: บน x86 rep movsbเป็นวิธีการคัดลอกที่มีประสิทธิภาพมากที่สุดวิธีหนึ่งอย่างน้อยสำหรับสำเนาขนาดกลางถึงขนาดใหญ่ การดำเนินงานไมโครรหัสมันมีอะไรบางอย่างที่อยู่ใกล้คงที่ค่าใช้จ่ายในการเริ่มต้น (ทั้ง AMD และ Intel) เช่นในแซนดีบริดจ์, ~ 15-40 รอบบวก 4 รอบต่อสายแคช 64B (กรณีที่ดีที่สุด) สำหรับสำเนาขนาดเล็ก SSE loop จะดีที่สุดเนื่องจากไม่มีค่าใช้จ่ายในการเริ่มต้นระบบ แต่มันขึ้นอยู่กับการคาดเดาผิดสาขา
Peter Cordes

6

คุณควรเขียนโปรแกรมควบคุมกระแสข้อมูลของคุณเอง

cout << multi (5) << "อะไรก็ได้" << "lolcat";


15
การเขียนโปรแกรมควบคุมกระแสเป็นวิธีที่ซับซ้อนมากในการทำสิ่งที่ง่ายมาก!
Zero

10
C ++ เป็นวิธีที่ซับซ้อนมากในการทำสิ่งที่ง่ายมาก
JDPeckham

6

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.