Die Formatierungsbibliothek in C++20: Der Formatstring
Der Beitrag setzt die Serie zu Formatstrings in C++20 fort und taucht tiefer in die Formatspezifikation ein.​

(Bild: MyImages - Micha/Shutterstock)
- Rainer Grimm
In meinem letzten Artikel habe ich "Softwareentwicklung: Die Formatierungsbibliothek in C++20" vorgestellt. Heute werde ich tiefer in die Formatspezifikation des Formatstrings eintauchen.
Anfangen möchte ich mit einer kurzen Zusammenfassung des Formatstrings.
- Syntax:
std::format(FormatString, Args)
Der Formatstring FormatString besteht aus
- Gewöhnlichen Zeichen (außer
{
und}
) - Escape-Sequenzen
{{
und}}
, die durch{
und}
ersetzt werden - Ersetzungsfelder
Ein Ersatzfeld hat das Format { }
.
Argument-ID
Man kann eine Argument-ID und einen Doppelpunkt innerhalb des Ersetzungsfeldes verwenden, gefolgt von einer Formatangabe. Beide Komponenten sind optional.
Dank der Argument-ID lassen sich bestimmte Argumente neu anordnen oder ansprechen. Mit der Argument-ID lässt sich der Index der Argumente in Args angeben. Die IDs beginnen mit 0. Wenn keine Argument-ID angegeben ist, werden die Felder in der gleichen Reihenfolge ausgefüllt, in der die Argumente angegeben werden. Entweder müssen alle Ersetzungsfelder eine Argument-ID verwenden oder keine.
// formatArgumentID.cpp
#include <format>
#include <iostream>
#include <string>
int main() {
std::cout << '\n';
std::cout << std::format("{} {}: {}!\n",
"Hello", "World", 2020); // (1)
std::cout << std::format("{1} {0}: {2}!\n",
"World", "Hello", 2020); // (2)
std::cout << std::format("{0} {0} {1}: {2}!\n",
"Hello", "World", 2020); // (3)
std::cout << std::format("{0}: {2}!\n",
"Hello", "World", 2020); // (4)
std::cout << '\n';
}
(1) zeigt die Argumente in der angegebenen Reihenfolge an. Im Gegensatz dazu werden in (2) das erste und das zweite Argument neu angeordnet, in (3) wird das erste Argument zweimal angezeigt und in (4) wird das zweite Argument ignoriert.
Der Vollständigkeit halber ist hier die Ausgabe des Programms:
Die Anwendung des Arguments id mit der Formatspezifikation macht die Formatierung von Text in C++20 sehr leistungsfähig.
Formatspezifikation
Ich werde nicht die formale Formatspezifikation fĂĽr Datentypen, String-Typen und Chrono-Typen vorstellen. FĂĽr Datentypen und std::string finden sich
die vollständigen Details hier: standard format specification. Dementsprechend findet man die Details für Chrono-Typen hier: chrono format specification.
Stattdessen stelle ich eine pragmatische Beschreibung des Formatstrings fĂĽr Datentypen, String-Typen und Chrono-Typen vor.
fill_align(opt) sign(opt) #(opt) 0(opt) width(opt) precision(opt) L(opt) type(opt)
Alle Teile sind optional (opt). In den folgenden Abschnitten gehe ich auf die Features dieser Formatspezifikation genauer ein.
FĂĽllzeichen und Ausrichtung
Das FĂĽllzeichen ist optional (jedes Zeichen auĂźer {
oder }
) und wird von einer Ausrichtungsangabe gefolgt. Um das FĂĽllzeichen zu verwenden, muss man die Ausrichtung angeben.
- Füllzeichen: Standardmäßig wird das Leerzeichen verwendet
- Ausrichtung:
<
: links (Standard fĂĽr Werte, die keine Zahlen sind)>
: rechts (Standard fĂĽr Zahlen)^
: zentriert
// formatFillAlign.cpp
#include <format>
#include <iostream>
int main() {
std::cout << '\n';
int num = 2020;
std::cout << std::format("{:6}", num) << '\n';
std::cout << std::format("{:6}", 'x') << '\n';
std::cout << std::format("{:*<6}", 'x') << '\n';
std::cout << std::format("{:*>6}", 'x') << '\n';
std::cout << std::format("{:*^6}", 'x') << '\n';
std::cout << std::format("{:6d}", num) << '\n';
std::cout << std::format("{:6}", true) << '\n';
std::cout << '\n';
}
Die Standardausrichtung hängt von den verwendeten Datentypen ab. Im Gegensatz zum iostream-Operator werden boolesche Werte standardmäßig als true
oder false
dargestellt.
Vorzeichen, #
und 0
Vorzeichen, #
und 0
sind nur gĂĽltig, wenn ein Ganzzahl- oder FlieĂźkommatyp verwendet wird.
Das Vorzeichen kann die folgenden Werte haben:
+
: Das Vorzeichen wird fĂĽr Null und positive Zahlen verwendet.-
: Das Vorzeichen wird nur fĂĽr negative Zahlen verwendet (Standard).- Leerzeichen: Ein fĂĽhrendes Leerzeichen wird fĂĽr nicht negative Zahlen verwendet und ein Minuszeichen fĂĽr negative Zahlen.
// formatSign.cpp
#include <format>
#include <iostream>
int main() {
std::cout << '\n';
std::cout << std::format("{0:},{0:+},{0:-},{0: }", 0) << '\n';
std::cout << std::format("{0:},{0:+},{0:-},{0: }", -0) << '\n';
std::cout << std::format("{0:},{0:+},{0:-},{0: }", 1) << '\n';
std::cout << std::format("{0:},{0:+},{0:-},{0: }", -1) << '\n';
std::cout << '\n';
}
Das #
bewirkt die alternative Form:
- Bei ganzzahligen Datentypen wird das Präfix
0b
,0
oder0x
für binäre, oktale oder hexadezimale dargestellte Typen verwendet. - Für Datentypen mit Fließkommazahlen wird immer ein Dezimalpunkt verwendet.
0
: AuffĂĽllen mit fĂĽhrenden Nullen
// formatAlternate.cpp
#include <format>
#include <iostream>
int main() {
std::cout << '\n';
std::cout << std::format("{:#015}", 0x78) << '\n';
std::cout << std::format("{:#015b}", 0x78) << '\n';
std::cout << std::format("{:#015x}", 0x78) << '\n';
std::cout << '\n';
std::cout << std::format("{:g}", 120.0) << '\n';
std::cout << std::format("{:#g}", 120.0) << '\n';
std::cout << '\n';
}
Wie geht es weiter?
Mit dem Formatbezeichner kann man die Genauigkeit, die Breite und den Datentyp des Wertes angeben. Darüber werde ich in meinem nächsten Artikel schreiben. (rme)