zurück zum Artikel

Die Formatierungsbibliothek in C++20: Der Formatstring

Rainer Grimm

(Bild: MyImages - Micha/Shutterstock)

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

In meinem letzten Artikel habe ich "Softwareentwicklung: Die Formatierungsbibliothek in C++20 [1]" vorgestellt. Heute werde ich tiefer in die Formatspezifikation des Formatstrings eintauchen.

Modernes C++ – Rainer Grimm

Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig. Er schreibt gerne Artikel zu den Programmiersprachen C++, Python und Haskell, spricht aber auch gerne und häufig auf Fachkonferenzen. Auf seinem Blog Modernes C++ beschäftigt er sich intensiv mit seiner Leidenschaft C++.

Anfangen möchte ich mit einer kurzen Zusammenfassung des Formatstrings.

Der Formatstring FormatString besteht aus

Ein Ersatzfeld hat das Format { }.

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.

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 [2]. Dementsprechend findet man die Details für Chrono-Typen hier: chrono format specification [3].

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.

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.

// 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 sind nur gültig, wenn ein Ganzzahl- oder Fließkommatyp verwendet wird.

Das Vorzeichen kann die folgenden Werte haben:

// 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:

// 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';
   
}

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 [4])


URL dieses Artikels:
https://www.heise.de/-9610546

Links in diesem Artikel:
[1] https://www.heise.de/blog/Softwareentwicklung-Die-Formatierungsbibliothek-in-C-20-9602498.html
[2] https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification
[3] https://en.cppreference.com/w/cpp/chrono/system_clock/formatter#Format_specification
[4] mailto:rme@ix.de