Formatierte Ausgabe in Python               (C) 2019-2021 T.Birnthaler OSTC GmbH
=============================

Doku:

  (Sehr hilfreiche Beispielseite!)
  format Function
  printf-style String Formatting
  printf-style String Formatting
  String Methods
  Format-specification Mini-Language
  Format String Syntax
  Custom String Formatting
  Formatter Class
  Formatted String Literals
  Template Strings

In Python gibt es 3 Arten, FORMATIERTE Zeichenketten mit FESTEN Textteilen und
VARIABLEN Datenteilen darin zu erzeugen. Alle 3 beruhen auf dem Prinzip, in
eine SCHABLONE/ein TEMPLATE (FMT-String mit festem TEXT + PLATZHALTERN) Werte
im passenden Format einzubauen und den Ergebnisstring zurückzugeben:

 +------+-------------------+-----------------+---------------------+--------+
 | Stil | Modern Flex Länge | Bezeichnung     | Syntax              | Platzh.|
 +------+-------------------+-----------------+---------------------+--------+
 | C    | ALT    NIED LANG  | Analog C-printf | "FMT" % (WERTE)     | %...   |
 | Py 1 | MODERN MITT LANG  | String-Funktion | "FMT".format(WERTE) | {...}  |
 | Py 2 | MODERN HOCH KOMP  | Format-String   | F"FMT+WERTE"        | {...}  |
 +------+-------------------+-----------------+---------------------+--------+
  (ALT/MODERN=veraltet/neu, NIED/MITT/HOCH=Flexibilität, LANG/KOMP=lang/kompakt)

Hier ein Beispiel für alle 3 Stile mit dem gleichen Ergebnis: Folgende 3 Werte

  z = 5678      # Ganzzahl
  k = 3.1415    # Fließkommazahl
  t = "hallo"   # String

werden per Platzhalter mit passendem Formattyp (d=decimal, f=float, s=string)
in einem String eingefügt:

  print( "Zahl %6d, Wert %6.2f, Text %-10s" % (z, k, t))
  print( "Zahl {0:6d}, Wert {1:6.2f}, Text {2:10s}".format(z, k, t))
  print( "Zahl {a:6d}, Wert {b:6.2f}, Text {c:10s}".format(a=z, b=k, c=t))
  print(F"Zahl {z:6d}, Wert {k:6.2f}, Text {t:10s}")

Es ergibt sich jeweils der gleiche Ergebnisstring (Zahlen rechtsbündig, String
linksbündig):

  "Zahl   5678, Wert   3.14, Text hallo     "
        ------       ------       ----------
          6d          6.2f         -10s/10s

Die PLATZHALTER werden also per % oder {} dargestellt, der TYP-BUCHSTABE am
Ende eines Platzhalters legt den Datentyp des einzufüllenden Werts fest (Python
prüft dies auch). Die WERTE zu den Platzhaltern werden festgelegt durch:

  1)  Reihenfolge im Tupel nach %-Zeichen (KEINE Umordnung + Mehrfachverwendung)
  2a) Position im .format()-Aufruf        (Umordnung + Mehrfachverw. möglich)
  2b) Name im .format()-Aufruf            (Umordnung + Mehrfachverw. möglich)
  3)  Variablenname im Platzhalter {}     (Umordnung + Mehrfachverw. möglich)

Folgende TYP-BUCHSTABEN sind in allen Stilen verfügbar:

  +-----+-------------+--------------------------------------------------------+
  | Typ | Name        | Bedeutung                                              |
  +-----+-------------+--------------------------------------------------------+
  | s   | string      | Zeichenkette (DEFAULT, weglassbar)                     |
  | c   | character   | Zeichen per Code-Nummer (Unicode)                      |
  +-----+-------------+--------------------------------------------------------+
  | d   | decimal     | Ganzzahl im Dezimalformat                              |
  | i   | integer     | Ganzzahl im Dezimalformat (nur im C-Stil, analog "d")  |
  | n   | number      | Ganzzahl analog "d" (aber Trennzeichen gemäß locale)   |
  +-----+-------------+--------------------------------------------------------+
  | b   | binary      | Ganzzahl im Binärformat (nur Ziffern 0/1)              |
  | o   | octal       | Ganzzahl im Oktalformat (nur Ziffern 0-7)              |
  | x X | hexadecimal | Ganzzahl im Hexadezimalformat (x=abcdef, X=ABCDEF)     |
  +-----+-------------+--------------------------------------------------------+
  | f F | float       | Fließkommazahl im Festpunktformat (inf/nan <-> INF/NAN)|
  | e E | exponent    | Fließkommazahl im Exponentialformat mit e/E-Zeichen    |
  | g G | general     | "f/F" oder "e/E" je nachdem wo mehr Ziffern dargestellt|
  | n   | number      | Fließkommazahl analog "g" (Trennzeichen gemäß locale)  |
  +-----+-------------+--------------------------------------------------------+
  | %   | percent     | Fließkommazahl analog "f" * 100 + %-Zeichen danach     |
  +-----+-------------+--------------------------------------------------------+

Die Typinformation kann um weitere Angabe ergänzt werden, mit denen minimale +
maximale Breite, Füllzeichen, Ausrichtung, Anzahl Nachkommastellen und andere
Eigenschaften der Ausgabe kontrolliert werden.


C-printf-Stil
-------------
Formatelemente beginnen mit dem Zeichen "%" und enden mit einem Typbuchstaben
(ohne Zwischenraum, ein Prozentzeichen ist zu verdoppeln):

  % (MAP) FLAGS WIDTH .PREC LENMOD TYPE

Bedeutung:

  +-------+------------------------------------------------------------------+
  | MAP   | Dictionary Key (Wert muss ein Dictionary sein)                   |
  | FLAGS | "#" Alternative Form ("0o", "0x", "0X")                          |
  |       | "0" Mit "0" statt Leerzeichen auffüllen                          |
  |       | "-" Linksbündig (statt rechtsbündig)                             |
  |       | " " Vorzeichen "+" weglassen                                     |
  |       | "+" Vorzeichen "+" ausgeben                                      |
  | WIDTH | Minimale Breite (mit " " oder "0" aufgefüllt)                    |
  | .PREC | Maximale Breite bei Strings / Anzahl Nachkommastellen bei Zahlen |
  | LENMOD| "hlL" (in Python irrelevant)                                     |
  | TYPE  | "scdinboxXfFeEgG%" analog obiger Typtabelle                      |
  +-------+------------------------------------------------------------------+

Beispiele:

    zahl = 1234567.89
    "%+12.1f"  % zahl   # --> '  +1234567.9'
    "%+012.1f" % zahl   # --> '+001234567.9'
    "%-+12.1f" % zahl   # --> '+1234567.9  '
    "%--12.1f" % zahl   # --> '1234567.9   '
    "%- 12.1f" % zahl   # --> ' 1234567.9  '
    "%-12.1f"  % zahl   # --> '1234567.9   '
    "%-012.1f" % zahl   # --> '1234567.9   '
    "%012.1f"  % zahl   # --> '0001234567.9'
    "%f"       % zahl   # --> '1234567.890000'

    text = "hallowelt"
    "%s"       % text   # --> 'hallowelt'
    "%12s"     % text   # --> '   hallowelt'
    "%-12s"    % text   # --> 'hallowelt   '
    "%012s"    % text   # --> '   hallowelt'
    "%12.12s"  % text   # --> '   hallowelt'
    "%6.6s"    % text   # --> 'hallow'


Python 1 Stil
-------------
Die Formatangabe hat folgende Form (ohne Zwischenraum, INDEX ist eine
Positionsnummer (startet mit 0) oder ein Name, dessen Wert eingefüllt wird,
ein "{" oder "}" ist zu verdoppeln, !CONV und :SPEC sind optional):

  "{}"
  "{" INDEX/NAME ":"}"
  "{" INDEX/NAME ":" SPEC "}"
  "{" INDEX/NAME "!" CONV ":" SPEC "}"

Im Python-Stil 1 steht im Platzhalter vor einem ":" ein INDEX (startet mit 0),
welche die POSITION in der Werte-Liste im .format(WERTE)-Aufruf angibt
(Reihenfolge der Nummern beliebig, gleiche Nummer mehrfach erlaubt). Ohne Index
werden die Werte von links nach rechts in die Platzhalter eingefüllt (":" ist
trotzdem notwendig!). Alternativ sind auch Namen für die format()-Parameter
vergebbar, die vor dem ":" als Referenz verwendbar sind:

  F"Zahl {0:6d}, Wert {1:6.2f}, Text {2:10s}".format(z, k, t)       # Index expl.
  F"Zahl {:6d}, Wert {:6.2f}, Text {:10s}".format(z, k, t)          # Index autom.
  F"Zahl {a:6d}, Wert {b:6.2f}, Text {c:10s}".format(a=z, b=k, c=t) # Name

Ergibt:

  "Zahl   5678, Wert   3.14, Text hallo     "
  "Zahl   5678, Wert   3.14, Text hallo     "
  "Zahl   5678, Wert   3.14, Text hallo     "
        ------       ------       ----------
          6d          6.2f           10s

Im einfachsten Fall genügt "{}" als Platzhalter, diese werden von links nach
rechts mit den Werten in .format(...) in der notwendigen Breite gefüllt (eine
Typprüfung findet hier nicht statt):

  F"Zahl {}, Wert {}, Text {}".format(z, k, t)

Ergibt:

  "Zahl 5678, Wert 3.1415, Text hallo"
        ----       ------       -----


Python 2 Stil
-------------
Die Formatangabe hat folgende Form (ohne Zwischenraum, INDEX ist eine
Positionsnummer (startet mit 0) oder ein Name, dessen Wert eingefüllt wird,
ein "{" oder "}" ist zu verdoppeln, !CONV und :SPEC sind optional):

  "{" EXPR ":"}"
  "{" EXPR ":" SPEC "}"
  "{" EXPR "!" CONV ":" SPEC "}"

Bedeutung:

  +------+----------------------------------------------------------+
  | EXPR | Zu formatiertendes Objekt (Variablenname/Rechenausdruck) |
  | CONV | Festlegung der Konvertierung gemäß folgender Tabelle     |
  | SPEC | Festlegung der Formatierung gemäß folgender Tabelle      |
  +------+----------------------------------------------------------+

CONF konvertiert den Wert gemäß folgender Regeln:

  +----+---------+-----------------------------------+
  | !a | ascii   | ascii() auf Wert anwenden         |
  | !r | repr    | repr() auf Wert anwenden          |
  | !s | string  | str() auf Wert anwenden (Default) |
  +----+---------+-----------------------------------+

SPEC legt die Formatierung mit folgenden Komponenten fest (in dieser
Reihenfolge, ohne Zwischenraum, alles optional außer FILL erfordert ALIGN):

  +-------+------------------------------------------------------------------+
  | FILL  | Füllzeichen (Standard Leerzeichen " ", "}" nicht erlaubt         |
  | ALIGN | "<" (left), ">" (right), "^" (center), "=" (zw. Vorz. und Zahl)  |
  | SIGN  | "+" ("+"/"-" erzwingen), "-" ("-" falls nötig), " " (" "/"-")    |
  | #     | Setzt "0b", "0o" oder "0x" vor Ganzzahlen (abhängig von TYPE)    |
  | 0     | Zahlen mit "0" auffüllen (FILL ist allgemeiner)                  |
  | WIDTH | Minimale Breite (Mit " ", "0" oder FILL aufgefüllt)              |
  | ,     | Tausendertrenner erzeugen (3er-Gruppen)                          |
  | .PREC | Maximale Breite bei Strings / Anzahl Nachkommastellen bei Zahlen |
  | TYPE  | "scdinboxXfFeEgG%" analog obiger Typtabelle                      |
  +-------+------------------------------------------------------------------+

Beispiel:

  F"Zahl {z:6d}, Wert {k:6.2f}, Text {t:10s}")               # Variable
  F"Zahl {z*5+2:6d}, Wert {k/5.0:6.2f}, Text {t+'X':10s}")   # Rechenausdruck

Ergibt:

  "Zahl   5678, Wert   3.14, Text hallo     "
  "Zahl  28392, Wert   0.63, Text halloX    "
        ------       ------       ----------
          6d          6.2f           10s

Im Python Stil 2 lassen sich mit weiteren (eingeschachtelten) Klammern {...}
beliebige Teile der Format-Angabe per Variable oder Rechenausdruck füllen
(Makro-Logik):

  breite = 10
  nkst   = 3
  typ    = "d"
  F"Zahl {z:{breite}{typ}}, Wert {k:{breite}.{nkst}f}, Text {t:{breite}s}"

Ergibt:

  "Zahl       5678, Wert      3.142, Text hallo     "
        ----------       ----------       ----------
           10d              10.3f            10s

Weitere Beispiele zum Python Stil 1 + 2:

    zahl = 1234567.89

    "{:*=+12.1f}".format(zahl) # --> '+**1234567.9'
    "{:*> 12.1f}".format(zahl) # --> '** 1234567.9'
    "{:-012.1f}".format(zahl)  # --> '0001234567.9'
    "{:,.2f}".format(zahl)     # --> '1,234,567.89'

    F"{zahl:*=+12.1f}"         # --> '+**1234567.9'
    F"{zahl:*>+12.1f}"         # --> '** 1234567.9'
    F"{zahl:-012.1f}"          # --> '0001234567.9'
    F"{zahl:,.2f}"             # --> '1,234,567.89'