Mam do formatu std::string
z sprintf
i wysłać go do strumienia pliku. W jaki sposób mogę to zrobić?
c++
string
formatting
stdstring
c++-standard-library
Max Frai
źródło
źródło
boost::format
(jak tutaj wykorzystuje rozwiązanie KennyTM ).boost::format
już obsługuje również operatorów strumieniowych C ++! Przykład:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
ma najmniej wierszy kodu ... jest recenzowany i ładnie integruje się ze strumieniami C ++.std::format
został dodany do C ++ 20 BTW: stackoverflow.com/a/57286312/895245 Awesome!C++20
wczoraj i zobaczyłem, że zostałC++20
skopiowanyboost
(już po raz milionowy), dodając gostd::format
doC++20
specyfikacji! Byłem bardzo, bardzo szczęśliwy! Wykorzystano prawie każdy plik C ++, który napisałem w ciągu ostatnich 9 latboost::format
. dodanie oficjalnego wyjścia w stylu printf do strumieni w C ++ przejdzie długą drogę IMO dla całego C ++.Odpowiedzi:
Nie możesz tego zrobić bezpośrednio, ponieważ nie masz dostępu do zapisu do bufora bazowego (do C ++ 11; patrz komentarz Dietricha Eppa ). Najpierw musisz to zrobić ciągiem c, a następnie skopiować go do std :: string:
Ale nie jestem pewien, dlaczego po prostu nie używałbyś strumienia ciągów? Zakładam, że masz konkretne powody, aby nie tylko to zrobić:
źródło
char buf[100];
sprawia, że to rozwiązanie nie jest bardzo niezawodne. Ale najważniejszy pomysł już istnieje.asprintf
, który przydziela nowy ciąg z wystarczającą ilością miejsca do przechowywania wyniku. Następnie skopiuj to dostd::string
jeśli chcesz i pamiętaj ofree
oryginale. Możliwe jest również umieszczenie tego w makrze, aby każdy dobry kompilator pomógł ci sprawdzić format - nie chcesz umieszczać tam,double
gdzie%s
jest oczekiwaneNowoczesne C ++ sprawia, że jest to bardzo proste.
C ++ 20
C ++ 20 wprowadza
std::format
, co pozwala dokładnie to zrobić. Używa zastępczych pól podobnych do tych w Pythonie :Sprawdź pełną dokumentację ! To ogromna poprawa jakości życia.
C ++ 11
Z c ++ 11 s
std::snprintf
, to już stało się bardzo łatwe i bezpieczne zadanie.Powyższy fragment kodu jest licencjonowany na podstawie CC0 1.0 .
Objaśnienie linia po linii:
Cel: Napisz do
char*
używając,std::snprintf
a następnie przekonwertuj to nastd::string
.Najpierw określamy pożądaną długość tablicy znaków przy użyciu specjalnego warunku w
snprintf
. Od cppreference.com :Oznacza to, że pożądany rozmiar to liczba znaków plus jeden , tak więc terminator zerowy usiądzie za wszystkimi innymi znakami i może zostać ponownie odcięty przez konstruktor łańcucha. Ten problem został wyjaśniony przez @ alexk7 w komentarzach.
snprintf
zwróci liczbę ujemną, jeśli wystąpi błąd, więc sprawdzamy, czy formatowanie działało zgodnie z oczekiwaniami. Nieprzestrzeganie tego może prowadzić do cichych błędów lub przydzielenia ogromnego bufora, jak wskazał @ead w komentarzach.Następnie przydzielamy nową tablicę znaków i przypisujemy ją do
std::unique_ptr
. Jest to ogólnie zalecane, ponieważ nie trzeba ręczniedelete
ponownie tego .Pamiętaj, że nie jest to bezpieczny sposób przydzielania
unique_ptr
typów zdefiniowanych przez użytkownika, ponieważ nie można cofnąć przydziału pamięci, jeśli konstruktor zgłasza wyjątek!Następnie możemy oczywiście po prostu użyć
snprintf
zgodnie z przeznaczeniem i zapisać sformatowany ciąg dochar[]
.Na koniec tworzymy i zwracamy nowe
std::string
, upewniając się, że na końcu pominięto terminator zerowy.Można zobaczyć przykład w akcji tutaj .
Jeśli chcesz także użyć
std::string
na liście argumentów, spójrz na tę treść .Dodatkowe informacje o Visual Studio użytkowników :
Jak wyjaśniono w tej odpowiedzi , Microsoft zmienił nazwę
std::snprintf
na_snprintf
(tak, bezstd::
). MS dodatkowo ustawiło to jako przestarzałe i zaleca użycie_snprintf_s
zamiast tego, jednak_snprintf_s
nie zaakceptuje bufora jako zera lub mniejszego niż sformatowane wyjście i nie obliczy długości wyjść, jeśli to nastąpi. Aby więc pozbyć się ostrzeżeń o wycofaniu podczas kompilacji, możesz wstawić następujący wiersz na górze pliku, który zawiera użycie_snprintf
:Końcowe przemyślenia
Wiele odpowiedzi na to pytanie zostało napisanych przed C ++ 11 i używa stałych długości buforów lub vargs. Jeśli nie utkniesz ze starymi wersjami C ++, nie poleciłbym używania tych rozwiązań. Idealnie jest przejść na C ++ 20.
Ponieważ rozwiązanie C ++ 11 w tej odpowiedzi używa szablonów, może generować sporo kodu, jeśli jest często używane. Jednak jeśli nie tworzysz środowiska z bardzo ograniczoną przestrzenią na pliki binarne, nie będzie to stanowić problemu i nadal stanowi znaczną poprawę w stosunku do innych rozwiązań, zarówno pod względem przejrzystości, jak i bezpieczeństwa.
Jeśli wydajność przestrzeni jest bardzo ważna, te dwa rozwiązania z vargs i vsnprintf mogą być przydatne. NIE UŻYWAJ żadnych rozwiązań o ustalonych długościach buforów, które wymagają jedynie problemów.
źródło
return string(&buf[0], size);
lub coś podobnego. Po drugie, jeśli zwrócisz taki ciąg c, spowoduje to niezdefiniowane zachowanie, ponieważ wektor zawierający wartości, na które wskazujesz, zostanie unieważniony po powrocie. Po trzecie, kiedy zacząłem uczyć się C ++, standard nie określał, w jakiej kolejności elementy muszą być przechowywane wewnątrzstd::vector
, więc dostęp do jego pamięci za pomocą wskaźnika był nieokreślonym zachowaniem. Teraz to zadziała, ale nie widzę korzyści z robienia tego w ten sposób.std::string
zostanie zbudowany z niejawnie przekonwertowanego wektora ( inicjowanie kopii ), który jest następnie zwracany jako kopia, jak sugeruje podpis funkcji. Ponadto elementy astd::vector
są i zawsze miały być przechowywane w sposób ciągły . Uważam jednak, że może to nie przynieść korzyści.return string(buf.get(), buf.get() + size);
powinna być w przeciwnymreturn string(buf.get(), buf.get() + size - 1);
razie otrzymasz ciąg z zerowym znakiem na końcu. Stwierdziłem, że tak jest w przypadku gcc 4.9.Rozwiązanie C ++ 11, które wykorzystuje
vsnprintf()
wewnętrznie:Bezpieczniejsze i bardziej wydajne (przetestowałem to i jest szybsze) podejście:
fmt_str
Jest przekazywane przez wartość za zgodne z wymaganiamiva_start
.UWAGA: „Bezpieczniejsza” i „szybsza” wersja nie działa na niektórych systemach. Dlatego oba są nadal wymienione. Również „szybsze” zależy całkowicie od poprawności kroku wstępnego przydziału, w przeciwnym razie
strcpy
renderowanie będzie wolniejsze.źródło
size
w każdej iteracji, gdy można ją uzyskać przy pierwszym wywołaniuvsnprintf()
.boost::format()
zapewnia pożądaną funkcjonalność:Począwszy od streszczenia bibliotek formatu Boost:
źródło
C ++ 20 obejmie,
std::format
który przypominasprintf
API, ale jest w pełni bezpieczny dla typów, działa z typami zdefiniowanymi przez użytkownika i używa składni łańcuchowej formatu podobnego do Pythona. Oto, w jaki sposób będziesz mógł sformatowaćstd::string
i zapisać go w strumieniu:lub
Alternatywnie możesz użyć biblioteki {fmt}, aby sformatować ciąg i zapisać go
stdout
lub strumień plików za jednym razem:Podobnie jak w przypadku
sprintf
większości innych odpowiedzi tutaj, niestety używają one varargs i są z natury niebezpieczne, chyba że użyjesz czegoś takiego jakformat
atrybut GCC, który działa tylko z literałymi ciągami formatu. Możesz zobaczyć, dlaczego te funkcje są niebezpieczne na następującym przykładzie:gdzie
string_format
jest wdrożenie z odpowiedzi Erika Aronesty. Ten kod się kompiluje, ale najprawdopodobniej ulegnie awarii podczas próby uruchomienia:Zastrzeżenie : Jestem autorem {fmt} i C ++ 20
std::format
.źródło
error: 'fmt' has not been declared
fmt
implementacja została dodana do C ++ 20! stackoverflow.com/a/57286312/895245 fmt obecnie twierdzi, że ma wsparcie. Świetna robota!Jeśli chcesz tylko składnię podobną do printf (bez samodzielnego wywoływania printf), spójrz na Format Boost .
źródło
Napisałem własny przy użyciu vsnprintf, więc zwraca ciąg zamiast konieczności tworzenia własnego bufora.
Możesz więc używać go jak
źródło
vsnprintf
bezpośrednio w ciągu.std::unique_ptr<char[]> buffer (new char[size]);
Aby sformatować
std::string
w sposób „sprintf”, wywołajsnprintf
(argumentynullptr
i0
), aby uzyskać potrzebną długość bufora. Napisz swoją funkcję przy użyciu szablonu variadic C ++ 11 w następujący sposób:Kompiluj z obsługą C ++ 11, na przykład w GCC:
g++ -std=c++11
Stosowanie:
źródło
char buf[length + 1];
zamiastchar* buf = new char[length + 1];
?char[]
achar*
nowością polega na tym, że w pierwszym przypadku buf byłby przydzielany na stosie. Jest odpowiedni dla małych buforów, ale ponieważ nie możemy zagwarantować rozmiaru wynikowego łańcucha, nieco lepiej jest użyćnew
. Na przykład na mojej maszyniestring_sprintf("value: %020000000d",5)
wypisz skandaliczną liczbę zer wiodących przed liczbą 5, zrzuty rdzenia podczas używania tablicy na stosie, ale działa OK, gdy używasz dynamicznie przydzielonej tablicynew char[length + 1]
std::move
jest to zbyteczne .[edycja: 20/05/25] lepiej jeszcze ...:
W nagłówku:
Funkcja
PRINTSTRING(r)
służy do obsługi interfejsu graficznego lub terminala lub innych specjalnych potrzeb wyjściowych#ifdef _some_flag_
, domyślnie:[edytuj '17 / 8/31] Dodanie variadic wersji szablonów „vtspf (..)”:
która jest w rzeczywistości wersją rozdzielaną przecinkami (zamiast) niekiedy przeszkadzających
<<
operatorów, używanych w ten sposób:[edytuj] Dostosowany do wykorzystania techniki zawartej w odpowiedzi Erika Aronesty (powyżej):
[poprzednia odpowiedź]
Bardzo późna odpowiedź, ale dla tych, którzy, podobnie jak ja, lubią „sprintf”: napisałem i używam następujących funkcji. Jeśli Ci się spodoba, możesz rozwinąć opcje%, aby ściślej pasowały do sprintfów; te tam obecnie są wystarczające na moje potrzeby. Używasz stringf () i stringfappend () tak samo jak sprintf. Pamiętaj tylko, że parametry ... muszą być typami POD.
źródło
fmt
jako odniesienie działa dobrze. (Ale przypuszczam, że ludzie będą chcieli bawić się zabawkami, więc ...) Jeśli są jakieś inne domniemane „błędy”, czy mógłbyś to rozwinąć?Oto jak robi to Google:
StringPrintf
(licencja BSD)i Facebook robi to w dość podobny sposób:
StringPrintf
(licencja Apache)Oba zapewniają również wygodę
StringAppendF
.źródło
Moje dwa centy za to bardzo popularne pytanie.
Cytując manpage z
printf
-Jak funkcji :Innymi słowy, rozsądna implementacja C ++ 11 powinna wyglądać następująco:
Działa całkiem dobrze :)
Szablony Variadic są obsługiwane tylko w C ++ 11. Odpowiedź z pixelpoint pokazuje podobną technikę przy użyciu starszych stylów programowania.
To dziwne, że C ++ nie ma czegoś takiego po wyjęciu z pudełka. Ostatnio dodali
to_string()
, co moim zdaniem jest wielkim krokiem naprzód. Zastanawiam się, czy dodadzą one.format
operatora dostd::string
ostatecznie ...Edytować
Jak wskazał alexk7, A
+1
jest potrzebne na wartości zwracanejstd::snprintf
, ponieważ musimy mieć miejsce na\0
bajt. Intuicyjnie, w przypadku większości architektur brakujące+1
spowodujerequired
częściowe zastąpienie liczby całkowitej przez0
. Stanie się tak po oszacowaniurequired
rzeczywistego parametru dlastd::snprintf
, więc efekt nie powinien być widoczny.Problem ten może się jednak zmienić, na przykład przy optymalizacji kompilatora: co, jeśli kompilator zdecyduje się użyć rejestru dla
required
zmiennej? Jest to rodzaj błędów, które czasami powodują problemy z bezpieczeństwem.źródło
bytes
bufora, prawdopodobnie ponadrequired
liczbą całkowitą (która na szczęście w tym momencie jest już oceniana).nullptr
argument jako bufor, eliminującchar b;
wiersz w kodzie. ( Źródło )Korzystanie z C99 snprintf i C ++ 11
źródło
Testowane, odpowiedź na pytanie o jakość produkcji
Ta odpowiedź dotyczy ogólnego przypadku przy użyciu technik zgodnych ze standardami. To samo podejście podano jako przykład na stronie CppReference.com u dołu strony. W przeciwieństwie do ich przykładów, ten kod spełnia wymagania pytania i jest testowany w terenie w robotyce i aplikacjach satelitarnych. Poprawiono także komentowanie. Jakość projektu omówiono poniżej.
Przewidywalna wydajność liniowa
Dwa przejścia są niezbędne dla bezpiecznej, niezawodnej i przewidywalnej funkcji wielokrotnego użytku zgodnie ze specyfikacją pytania. Domniemania dotyczące rozkładu wielkości vargów w funkcji wielokrotnego użytku są złym stylem programowania i należy ich unikać. W tym przypadku arbitralnie duże reprezentacje zmiennej długości vargs są kluczowym czynnikiem przy wyborze algorytmu.
Ponowna próba przepełnienia jest wykładniczo nieefektywna, co jest kolejnym powodem omawianym, gdy komitet normalizacyjny C ++ 11 omówił powyższą propozycję zapewnienia suchego przebiegu, gdy bufor zapisu jest zerowy.
W powyższej implementacji gotowej do produkcji, pierwszy przebieg jest takim suchym przebiegiem w celu ustalenia wielkości alokacji. Alokacja nie występuje. Przetwarzanie dyrektyw printf i czytanie vargów stało się niezwykle skuteczne przez dziesięciolecia. Kod wielokrotnego użytku powinien być przewidywalny, nawet jeśli trzeba poświęcić niewielką nieefektywność dla trywialnych przypadków.
Bezpieczeństwo i niezawodność
Andrew Koenig powiedział niewielkiej grupie z nas po swoim wykładzie na wydarzeniu w Cambridge: „Funkcje użytkownika nie powinny polegać na wykorzystaniu niepowodzenia wyjątkowej funkcjonalności”. Od tego czasu jego mądrość okazała się prawdziwa. Naprawione i zamknięte błędy bezpieczeństwa często wskazują na powtórne włamania w opisie otworu wykorzystanego przed naprawą.
Jest to wspomniane w formalnej propozycji zmiany standardów dla funkcji bufora zerowego w Alternative to sprintf, C9X Revision Wniosek , Dokument ISO IEC WG14 N645 / X3J11 96-008 . Dowolnie długi ciąg wstawiany według dyrektywy drukowania „% s” w ramach ograniczeń dynamicznej dostępności pamięci nie jest wyjątkiem i nie powinien być wykorzystywany do tworzenia „wyjątkowej funkcjonalności”.
Rozważ propozycję obok przykładowego kodu podanego na dole strony C ++ Reference.org, do której link znajduje się w pierwszym akapicie tej odpowiedzi.
Ponadto testowanie przypadków awarii rzadko jest tak wiarygodne jak przypadki sukcesu.
Ruchliwość
Wszyscy główni dostawcy systemów operacyjnych udostępniają kompilatory w pełni obsługujące std :: vsnprintf jako część standardów c ++ 11. Hosty z produktami dostawców, które nie utrzymują już dystrybucji, powinny być wyposażone w g ++ lub clang ++ z wielu powodów.
Wykorzystanie w stosie
Wykorzystanie stosu w pierwszym wywołaniu do std :: vsnprintf będzie mniejsze lub równe zużyciu drugiego i zostanie zwolnione przed rozpoczęciem drugiego wywołania. Jeśli pierwsze wywołanie przekroczy dostępność stosu, std :: fprintf również się nie powiedzie.
źródło
C ++ 20
std::format
Przybył! Funkcja jest opisana na stronie : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html i używa
.format()
składni podobnej do Pythona .Oczekuję, że użycie będzie wyglądało tak:
Spróbuję, kiedy wsparcie dotrze do GCC, GCC 9.1.0
g++-9 -std=c++2a
nadal go nie obsługuje.Interfejs API doda nowy
std::format
nagłówek:Istniejąca
fmt
biblioteka twierdzi, że zaimplementuje ją, jeśli potrzebujesz wypełnienia: https://github.com/fmtlib/fmti był wcześniej wspomniany w: formatowanie std :: string jak sprintf
źródło
Na podstawie odpowiedzi udzielonej przez Erika Aronesty:
Pozwala to uniknąć konieczności odrzucania,
const
którego wynik.c_str()
był w pierwotnej odpowiedzi.źródło
źródło
_vscprintf
to jest. Myślę, że powinieneś rozwinąć tę odpowiedź.string nie ma tego, czego potrzebujesz, ale ma std :: stringstream. Użyj ciągu znaków, aby utworzyć ciąg, a następnie wyodrębnij ciąg. Oto pełna lista rzeczy, które możesz zrobić. Na przykład:
da Ci 10 miejsc po przecinku z precyzją podczas drukowania podwójnego lub zmiennoprzecinkowego.
źródło
Możesz spróbować:
źródło
Jeśli korzystasz z systemu, który ma asprintf (3) , możesz łatwo go owinąć:
źródło
format
, ponieważ mówi gcc, aby sprawdził typy argumentów i dał przyzwoite ostrzeżenie za pomocą -Wall:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
va_end
. „jeśli va_end nie zostanie wywołany przed powrotem funkcji wywołującej va_start lub va_copy, zachowanie jest niezdefiniowane.” - docsthrow std::bad_alloc();
, ponieważ nie używam wyjątków C ++ w mojej bazie kodu, a dla ludzi, którzy to robią, mogą łatwo dodać go na podstawie na komentarz źródłowy i twój komentarz tutaj.To jest kod, którego używam do zrobienia tego w moim programie ... To nic nadzwyczajnego, ale robi to sztuczkę ... Uwaga, będziesz musiał dostosować swój rozmiar odpowiednio do potrzeb. MAX_BUFFER to dla mnie 1024.
źródło
vsnprintf
bezpośrednio w ciągu.Wziął pomysł z Dacav i odpowiedź pixelpoint . Grałem trochę i dostałem to:
Z rozsądną praktyką programowania uważam, że kod powinien wystarczyć, jednak wciąż jestem otwarty na bezpieczniejsze alternatywy, które są wciąż wystarczająco proste i nie wymagałyby C ++ 11.
A oto kolejna wersja, która wykorzystuje bufor początkowy, aby zapobiec ponownemu wywołaniu,
vsnprintf()
gdy bufor początkowy jest już wystarczający.(Okazuje się, że ta wersja jest po prostu podobna do odpowiedzi Piti Ongmongkolkul , tyle że nie używa
new
idelete[]
określa rozmiar podczas tworzeniastd::string
.Chodzi o to, aby nie używać
new
idelete[]
sugerować użycie stosu nad stertą, ponieważ nie musi on wywoływać funkcji alokacji i dezalokacji, jednak jeśli nie zostanie właściwie użyty, buforowanie przepełnień w niektórych (być może starych lub być może po prostu słabe) systemy. Jeśli jest to problem, zdecydowanie sugeruję użycienew
idelete[]
zamiast tego. Zauważ, że jedyny problem dotyczy alokacji, jakvsnprintf()
to się już nazywa z limitami, więc określenie limitu na podstawie wielkości przydzielonej w drugim buforze również by je zapobiegło.)źródło
Zwykle używam tego:
Wada: nie wszystkie systemy obsługują vasprint
źródło
Poniżej nieznacznie zmodyfikowana wersja odpowiedzi @iFreilicht, zaktualizowana do C ++ 14 (użycie
make_unique
funkcji zamiast surowej deklaracji) i dodano obsługęstd::string
argumentów (na podstawie artykułu Kenny'ego Kerra )Wynik:
W razie potrzeby możesz połączyć tę odpowiedź z oryginalną.
źródło
Biblioteka Poco Foundation ma bardzo wygodną funkcję formatowania, która obsługuje std :: string zarówno w formacie formatującym, jak i wartościach:
źródło
Możesz sformatować dane wyjściowe C ++ w cout przy użyciu pliku nagłówka iomanip. Upewnij się, że dołączasz plik nagłówka iomanip, zanim użyjesz funkcji pomocniczych, takich jak setprecision, setfill itp.
Oto fragment kodu, którego użyłem w przeszłości, aby wydrukować średni czas oczekiwania w wektorze, który „skumulowałem”.
Oto krótki opis tego, jak możemy sformatować strumienie C ++. http://www.cprogramming.com/tutorial/iomanip.html
źródło
Mogą wystąpić problemy, jeśli bufor nie jest wystarczająco duży, aby wydrukować ciąg. Musisz określić długość sformatowanego ciągu przed wydrukowaniem tam sformatowanej wiadomości. Robię do tego własnego pomocnika (testowane na Windows i Linux GCC ) i możesz go użyć.
String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa
String.cpp:
String.h:
źródło
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
- czy można bezpiecznie założyć, że bufor łańcucha ma miejsce na kończący znak null? Czy istnieją implementacje, które nie przydzielają rozmiaru + 1 znaków. Czy byłoby to bezpieczniejszedst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
data
ic_str
są synonimami.źródło
Bardzo, bardzo proste rozwiązanie.
źródło
Zdaję sobie sprawę, że na to pytanie udzielono odpowiedzi wiele razy, ale jest to bardziej zwięzłe:
przykład:
Zobacz także http://rextester.com/NJB14150
źródło
AKTUALIZACJA 1 : dodano
fmt::format
testyPrzeprowadziłem własne badanie wokół metod, które tu wprowadziłem i uzyskałem diametralnie przeciwne wyniki w porównaniu z wymienionymi tutaj.
Użyłem 4 funkcji w 4 metodach:
vsnprintf
+std::unique_ptr
vsnprintf
+std::string
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
funkcja zfmt
bibliotekiDo testowania zaplecza
googletest
użył.for_each
Realizacja pochodzi stąd: iteracyjne nad krotkiTesty:
The
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.unsued.hpp :
unused.cpp :
WYNIKI :
Jak widać implementacja przez
vsnprintf
+std::string
jest równafmt::format
, ale szybsza niż przezvsnprintf
+std::unique_ptr
, co jest szybsze niż przezstd::ostringstream
.Testy zostały skompilowane
Visual Studio 2015 Update 3
i uruchomione wWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.źródło