Chcę wstawić spacje „n” (lub dowolny ciąg) na początku ciągu w C ++. Czy istnieje bezpośredni sposób na zrobienie tego przy użyciu ciągów std :: strings lub char *?
Np. W Pythonie możesz to po prostu zrobić
>>> "." * 5 + "lolcat"
'.....lolcat'
Chcę wstawić spacje „n” (lub dowolny ciąg) na początku ciągu w C ++. Czy istnieje bezpośredni sposób na zrobienie tego przy użyciu ciągów std :: strings lub char *?
Np. W Pythonie możesz to po prostu zrobić
>>> "." * 5 + "lolcat"
'.....lolcat'
Odpowiedzi:
W szczególnym przypadku powtórzenia pojedynczego znaku możesz użyć std::string(size_type count, CharT ch):
std::string(5, '.') + "lolcat"
NB. Nie można tego używać do powtarzania ciągów wieloznakowych.
Nie ma bezpośredniego idiomatycznego sposobu na powtarzanie ciągów w C ++ jako odpowiednik operatora * w Pythonie lub operatora x w Perlu. Jeśli powtarzasz pojedynczy znak, konstruktor dwuargumentowy (jak sugerowały poprzednie odpowiedzi) działa dobrze:
std::string(5, '.')
To jest wymyślony przykład tego, jak można użyć strumienia ostringstream do powtórzenia ciągu n razy:
#include <sstream>
std::string repeat(int n) {
std::ostringstream os;
for(int i = 0; i < n; i++)
os << "repeat";
return os.str();
}
W zależności od implementacji może to być nieco bardziej wydajne niż zwykłe konkatenowanie łańcucha n razy.
Użyj jednej z form string :: insert:
std::string str("lolcat");
str.insert(0, 5, '.');
Spowoduje to wstawienie „.....” (pięć kropek) na początku ciągu (pozycja 0).
Wiem, że to stare pytanie, ale szukałem tego samego i znalazłem prostsze rozwiązanie. Wygląda na to, że cout ma tę funkcję wbudowaną w cout.fill (), zobacz link do pełnego wyjaśnienia
http://www.java-samples.com/showtutorial.php?tutorialid=458
cout.width(11);
cout.fill('.');
cout << "lolcat" << endl;
wyjścia
.....lolcat
cout << "" << endl;
Dla celów przykładu dostarczonych przez OP std :: konstruktor struny jest wystarczająca: std::string(5, '.'). Jeśli jednak ktoś szuka funkcji do wielokrotnego powtarzania 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;
}
Jak wspomniał Commodore Jaeger, nie sądzę, aby żadna z pozostałych odpowiedzi faktycznie odpowiadała na to pytanie; pytanie dotyczy tego, jak powtórzyć ciąg, a nie znak.
Chociaż odpowiedź udzielona przez Commodore jest poprawna, jest dość nieefektywna. Oto szybsza implementacja, chodzi o zminimalizowanie operacji kopiowania i alokacji pamięci przez pierwsze wykładnicze zwiększenie ciągu:
#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;
}
Możemy również zdefiniować an, operator*aby uzyskać coś bliższego wersji Pythona:
#include <utility>
std::string operator*(std::string str, std::size_t n)
{
return repeat(std::move(str), n);
}
Na moim komputerze jest to około 10x szybsze niż implementacja podana przez Commodore i około 2x szybsze niż naiwne rozwiązanie typu „dodaj n - 1 razy” .
+=wewnątrz twojej pętli for również jest coś w rodzaju pętli, która wykonuje str.size()iteracje. str.size()rośnie w każdej iteracji pętli zewnętrznej, więc po każdej iteracji zewnętrznej pętla wewnętrzna musi wykonać więcej iteracji. Twoja i naiwna implementacja „kopiuj n razy” w sumie oba n * periodznaki kopiowania . Twoja implementacja wykonuje tylko jedną alokację pamięci z powodu początkowej reserve. Chyba sprofilowałeś swoją implementację na raczej małą stri dużą n, ale nie też na dużą stri małą n.
stri małymi npodejściami. Uważam, że jest to bardziej związane z ogólnym potokiem niż z prognozowaniem gałęzi jako takim, należy również rozważyć kwestie związane z dopasowaniem danych . Powinieneś zadać nowe pytanie, aby wyjaśnić, dlaczego jest to bardziej przyjazne dla procesora / pamięci, jestem pewien, że zyskałoby duże zainteresowanie i otrzymał lepszą odpowiedź niż mogę udzielić tutaj.
rep movsbjest jednym z najbardziej wydajnych sposobów kopiowania, przynajmniej w przypadku średnich i dużych kopii. Jego implementacja z mikrokodowaniem ma prawie stałe obciążenie rozruchowe (zarówno na AMD, jak i na Intelu), np. Na Sandybridge, ~ 15 do 40 cykli plus 4 cykle na linię pamięci podręcznej 64B (w najlepszym przypadku) . W przypadku małych kopii pętla SSE jest najlepsza, ponieważ nie ma narzutu związanego z uruchamianiem. Ale wtedy jest to uzależnione od błędnych prognoz branży.
ITNOA
Możesz do tego użyć funkcji 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();
}