Printf -Formatzeichenfolge

Ein Beispiel für die Printf -Funktion

Das Printf -Formatzeichenfolge ist ein Steuerparameter, der von einer Klasse von verwendet wird Funktionen in den Eingangs-/Ausgabebibliotheken von C und viele weitere Programmiersprachen. Die Zeichenfolge ist in einem einfachen geschrieben Vorlagesprache: Zeichen werden normalerweise buchstäblich in die Ausgabe der Funktion kopiert, aber aber Formatspezifiziererdie mit a beginnen % Zeichen, geben Sie den Ort und die Methode an, um ein Datenstück (wie z. B. eine Zahl) in Zeichen zu übersetzen.

"printf" ist der Name einer der Hauptausgabefunktionen für C und steht für "Druck fOrmatted ". PRINTF -Format -Saiten sind ergänzt zu SCANF -Format -Saiten, die formatierte Eingaben liefern (Lexing AKA. Parsing). In beiden Fällen bieten diese eine einfache Funktionalität und ein festes Format im Vergleich zu ausgefeilteren und flexibleren Vorlagenmotoren oder Lexern/Parern, sind jedoch für viele Zwecke ausreichend.

Viele andere Sprachen als C kopieren Sie die Printf -Format -String -Syntax genau oder genau in ihren eigenen E/A -Funktionen.

Fehlanpassungen zwischen den Formatspezifikatoren und der Art der Daten können zu Abstürzen und anderen Schwachstellen führen. Die Formatzeichenfolge selbst ist sehr oft a Saitenliteral, welches erlaubt Statische Analyse des Funktionsaufrufs. Es kann jedoch auch der Wert einer Variablen sein, die eine dynamische Formatierung ermöglicht, aber auch eine Sicherheitsanfälligkeit, die als ein bekannt ist Unkontrollierte Formatzeichenfolge Ausbeuten.

Geschichte

Frühe Programmiersprachen wie z. Forran Verwendete spezielle Aussagen mit völlig unterschiedlicher Syntax als andere Berechnungen, um Formatierungsbeschreibungen zu erstellen. In diesem Beispiel wird das Format in Zeile 601 angegeben, und der Befehl write bezieht sich auf die Zeilennummer:

   SCHREIBEN AUSGANG BAND 6, 601, IA, Ib, IC, BEREICH  601  FORMAT (4H A= ,I5,5H  B= ,I5,5H  C= ,I5,   &  8H  BEREICH= ,F10.2, 13H QUADRAT EINHEITEN) 

Algol 68 hatte mehr funktionsartig API, aber immer noch benutzte spezielle Syntax (die $ Abgrenzer umgeben Sonderformatierungssyntax):

printf(($"Farbe "g", Nummer 1 "6d,", Nummer 2 "4Zd,", Hex"16r2d,", schweben "-d.2d,", vorzeichenloser Wert"-3d"."l $,   "rot", 123456, 89, BEHÄLTER 255, 3.14, 250)); 

Die Verwendung der normalen Funktionsaufrufe und Datentypen vereinfacht jedoch die Sprache und den Compiler und ermöglicht die Implementierung der Eingabe/Ausgabe in derselben Sprache. Diese Vorteile überwiegen die Nachteile (z. B. ein vollständiger Mangel an Typensicherheit in vielen Fällen) und in den meisten neueren Sprachen ist I/A nicht Teil der Syntax.

C's printf hat seinen Ursprung in BCPL's writef Funktion (1966). Im Vergleich zu C und printf, *N ist ein BCPL Sprache Escape -Sequenz, die ein Newline -Zeichen darstellt (für das C die Escape -Sequenz verwendet \n) und die Reihenfolge der Feldbreite und des Typs der Formatspezifikation wird in umgekehrt writef:[1]

WriteF (" %i2-Queens Problem hat %i5 Lösungen*n", numqueens, count)

Wahrscheinlich war das erste Kopieren der Syntax außerhalb der C -Sprache die Unix printf Shell -Befehl, der zuerst in erschien Version 4, als Teil des Hafens nach C.[2]

Format -Platzhalterspezifikation

Die Formatierung erfolgt über Platzhalter innerhalb der Formatzeichenfolge. Wenn beispielsweise ein Programm das Alter einer Person ausdrucken wollte, könnte es die Ausgabe durch Präfixen mit "Ihr Alter IS" und die Verwendung des Zeichens der signierten Dezimalspezifizierer vorstellen d Um zu bezeichnen, dass die Ganzzahl für das Alter sofort nach dieser Nachricht angezeigt wird, können wir die Formatzeichenfolge verwenden:

printf("Dein Alter ist %d", das Alter); 

Syntax

Die Syntax für einen Format -Platzhalter ist

%[Parameter][Flaggen][Breite] [.Präzision][Länge]Typ

Parameterfeld

Das ist ein Posix Erweiterung und nicht in C99. Das Parameterfeld kann weggelassen werden oder sein:

Charakter Beschreibung
n$ n ist die Anzahl des Parameters, die mit diesem Formatspezifizierer angezeigt werden soll, sodass die bereitgestellten Parameter mehrmals unter Verwendung unterschiedlicher Formatspezifizierer oder in verschiedenen Ordnungen ausgegeben werden können. Wenn ein einzelner Platzhalter einen Parameter angibt, müssen alle restlichen Platzhalter auch einen Parameter angeben.
Zum Beispiel, printf("%2$d %2$#x; %1$d %1$#x",16,17) produziert 17 0x11; 16 0x10.

In diesem Merkmal wird hauptsächlich die Lokalisierung verwendet, wobei die Reihenfolge des Auftretens von Parametern aufgrund der sprachabhängigen Konvention variiert.

Bei der Nicht-Pox-Microsoft-Windows wird die Unterstützung für diese Funktion in einer separaten Printf_P-Funktion platziert.

Fahnen Feld

Das Feld Flags kann (in beliebiger Reihenfolge) null oder mehr sein von:

Charakter Beschreibung
-
(Minus)
Links ausrichten Sie die Ausgabe dieses Platzhalters. (Die Standardeinstellung besteht darin, die Ausgabe rechts auszurichten.)
+
(Plus)
PROFET EIN PLUS für positive signierte typische Typen. positiv = +, negativ = -.
(Die Standardeinstellung bereitet nichts vor positiven Zahlen vor.)

(Platz)
Vorbereitet einen Raum für positive signierte typische Typen. positiv = , negativ = -. Diese Flagge wird ignoriert, wenn die + Flagge existiert.
(Die Standardeinstellung bereitet nichts vor positiven Zahlen vor.)
0
(Null)
Wenn die Option "Breite" angegeben ist, präsentieren Sie Nullen für numerische Typen. (Die Standardeinstellung präsentiert Leerzeichen.)
Zum Beispiel, printf("%4X",3) produziert 3, während printf("%04X",3) produziert 0003.
'
(Apostroph)
Die Ganzzahl oder Exponent eines Dezimales hat die Tausende Gruppierungsabscheider angewendet.
#
(Hash)
Alternative Form:
Zum g und G Typen, nachfolgende Nullen werden nicht entfernt.
Zum f, F, e, E, g, G Typen, der Ausgang enthält immer einen Dezimalpunkt.
Zum o, x, X Typen, der Text 0, 0x, 0xwird jeweils auf Zahlen außerhalb Null vorbereitet.

Breite Feld

Das Breite Feld gibt a Minimum Anzahl der Zeichen für die Ausgabe und wird normalerweise verwendet, um Felder mit fester Breite in tabellarischer Ausgabe zu padeln, wobei die Felder ansonsten kleiner wären, obwohl sie keine Überschneidung übergroßer Felder verursacht.

Das Breitenfeld kann weggelassen werden, oder ein numerischer Ganzzahlwert oder ein dynamischer Wert, wenn er als ein weiteres Argument bestanden wird, wenn er durch einen Stern angezeigt wird *.[3] Zum Beispiel, printf("%*d", 5, 10) wird darin enden, dass 10 gedruckt werden, mit einer Gesamtbreite von 5 Zeichen.

Obwohl nicht Teil des Breitenfeldes, wird ein führendes Null als oben genannte Null-Padding - Flagge auch oben erwähnt.

Präzisionsfeld

Das Präzisionsfeld gibt normalerweise a an maximal Begrenzung der Ausgabe, abhängig vom jeweiligen Formattyp. Für schwimmende Punkte -Typen gibt es die Anzahl der Ziffern rechts vom Dezimalpunkt an, dass der Ausgang gerundet werden sollte. Für den String -Typ begrenzt es die Anzahl der Zeichen, die ausgegeben werden sollten, wonach die Zeichenfolge abgeschnitten wird.

Das Präzisionsfeld kann weggelassen werden, oder ein numerischer Ganzzahlwert oder ein dynamischer Wert, wenn er als ein anderes Argument bestanden wird, wenn er durch einen Stern angezeigt wird *. Zum Beispiel, printf("%.*s", 3, "abcdef") wird darin enden, dass abc gedruckt werden.

Länge Feld

Das Längenfeld kann weggelassen werden oder sein von:

Charakter Beschreibung
HH Für Ganzzahltypen Ursachen printf zu erwarten int-Gariertes Ganzzahlargument, das aus a gefördert wurde verkohlen.
h Für Ganzzahltypen Ursachen printf zu erwarten int-Gariertes Ganzzahlargument, das aus a gefördert wurde kurz.
l Für Ganzzahltypen Ursachen printf zu erwarten a lang-Garentarchitur Argument.

Für schwimmende Punkttypen wird dies ignoriert. schweben Argumente werden immer befördert zu doppelt bei Verwendung in einem Varargs -Aufruf.[4]

ll Für Ganzzahltypen Ursachen printf zu erwarten a lang Lang-Garentarchitur Argument.
L Für schwimmende Punkttypen, Ursachen printf zu erwarten a langes Doppel Streit.
z Für Ganzzahltypen Ursachen printf zu erwarten a size_t-Garentarchitur Argument.
j Für Ganzzahltypen Ursachen printf zu erwarten a intmax_t-Garentarchitur Argument.
t Für Ganzzahltypen Ursachen printf zu erwarten a ptrdiff_t-Garentarchitur Argument.

Darüber hinaus wurden vor der weit verbreiteten Verwendung der ISO C99-Erweiterungen mehrere plattformspezifische Längenoptionen vorhanden:

Figuren Beschreibung
I Für signierte ganzzahlige Typen, Ursachen printf zu erwarten ptrdiff_t-Garentarme Ganzzahlargument; für unsignierte ganzzahlige Typen, Ursachen printf zu erwarten size_t-Garentarchitur Argument. Häufig in Win32/Win64 -Plattformen gefunden.
I32 Für Ganzzahltypen Ursachen printf erwarten Sie ein 32-Bit-Argument für ein 32-Bit-Ganzzahl. Häufig in Win32/Win64 -Plattformen gefunden.
I64 Für Ganzzahltypen Ursachen printf erwarten Sie ein 64-Bit-Argument mit 64-Bit (Quad-Wort). Häufig in Win32/Win64 -Plattformen gefunden.
q Für Ganzzahltypen Ursachen printf erwarten Sie ein 64-Bit-Argument mit 64-Bit (Quad-Wort). Häufig in BSD -Plattformen gefunden.

ISO C99 enthält die inttypes.h Header-Datei, die eine Reihe von Makros für die Verwendung in plattformunabhängiger Verwendung enthält printf Codierung. Diese müssen außerhalb von Doppelquoten liegen, z. printf("%" PRId64 "\n", t);

Beispiele für Makros enthalten:

Makro Beschreibung
Prid32 Typischerweise gleichwertig zu I32d (Win32/Win64) oder d
Prid64 Typischerweise gleichwertig zu I64d (Win32/Win64), lld (32-Bit-Plattformen) oder ld (64-Bit-Plattformen))
PRII32 Typischerweise gleichwertig zu I32i (Win32/Win64) oder i
PRII64 Typischerweise gleichwertig zu I64i (Win32/Win64), lli (32-Bit-Plattformen) oder li (64-Bit-Plattformen))
Priu32 Typischerweise gleichwertig zu I32u (Win32/Win64) oder u
Priu64 Typischerweise gleichwertig zu I64u (Win32/Win64), llu (32-Bit-Plattformen) oder Lu (64-Bit-Plattformen))
Prix32 Typischerweise gleichwertig zu I32x (Win32/Win64) oder x
Prix64 Typischerweise gleichwertig zu I64x (Win32/Win64), llx (32-Bit-Plattformen) oder lx (64-Bit-Plattformen))

Typfeld

Das Typenfeld kann eines von:

Charakter Beschreibung
% Druckt ein wörtliches % Zeichen (dieser Typ akzeptiert keine Flags, Breite, Präzision, Länge Felder).
d, i int als signiert ganze Zahl. %d und %ich sind ein Synonym für die Ausgabe, unterscheiden sich jedoch bei Verwendung mit Scanf für Eingaben (wo verwendet %ich wird eine Zahl als hexadezimal interpretieren, wenn sie von vorangegangen ist 0xund Oktal, wenn es vorangegangen ist 0.))
u Druckendezimal Unsigned int.
f, F doppelt normal (Fixpunkt) Notation. f und F unterscheidet sich nur darin, wie die Saiten für eine unendliche Zahl oder NAN gedruckt werden (gedruckt werden ( Inf, Unendlichkeit und Nan zum f; Inf, UNENDLICHKEIT und Nan zum F).
e, E doppelt Wert in Standardform ( d.DDDe ±dd). Ein E Conversion verwendet den Brief E (statt e) Um den Exponenten einzuführen. Der Exponent enthält immer mindestens zwei Ziffern; Wenn der Wert Null ist, ist der Exponent 00. In Windows enthält der Exponent standardmäßig drei Ziffern, z. 1.5e002, aber dies kann durch microsoft-spezifisch verändert werden _set_output_format Funktion.
g, G doppelt entweder in normaler oder exponentieller Notation, je nachdem, was für seine Größe besser geeignet ist. g Verwendet unter Niederlassung Buchstaben, G Verwendet Buchstaben in Oberfall. Dieser Typ unterscheidet sich geringfügig von fester Punktnotation darin, dass unbedeutende Nullen rechts vom Dezimalpunkt nicht enthalten sind. Außerdem ist der Dezimalpunkt nicht in ganzen Zahlen enthalten.
x, X Unsigned int Als ein hexadezimal Nummer. x Verwendet unter Niederlassung Buchstaben und X Verwendet oberer Fall.
o Unsigned int im Oktal.
s NULL-terminierte Zeichenfolge.
c verkohlen (Charakter).
p Leere* (Zeiger auf void) in einem implementierungsdefinierten Format.
a, A doppelt in hexadezimaler Notation, beginnend mit 0x oder 0x. a Verwendet unter Niederlassung Buchstaben, A Verwendet Buchstaben in Oberfall.[5][6] (C ++ 11 iOstreams haben a Hexfloat das funktioniert gleich).
n Drucken Sie nichts, schreibt aber die Anzahl der bisher geschriebenen Zeichen in einen Ganzzahlzeigerparameter.
In Java druckt dies eine neue Linie.[7]

Platzhalter für benutzerdefinierte Format

Es gibt einige Implementierungen von printf-ähnliche Funktionen, die Verlängerungen zum Escape-Zeichen-basierend Mini-Sprachedamit der Programmierer eine spezifische Formatierungsfunktion für nicht entwickelte Typen haben. Eines der bekanntesten ist das (jetzt veraltete) Glibc's register_printf_function(). Es wird jedoch selten verwendet, da es mit der Überprüfung der statischen Format -Saitenkonflikte in Konflikt steht. Ein anderer ist VSTR -benutzerdefinierte Formatierer, damit das Hinzufügen von Multi-Charakter-Formatnamen hinzugefügt wird.

Einige Anwendungen (wie die Apache HTTP Server) schließen ihre eigenen ein printf-ähnliche Funktion und Einbetten von Erweiterungen in sie. Diese alle haben jedoch tendenziell die gleichen Probleme wie register_printf_function() hat.

Das Linux Kernel printk Funktion unterstützt eine Reihe von Möglichkeiten, um Kernelstrukturen mit dem Generikum anzuzeigen %p Spezifikation, durch Anhang Zusätzliche Formatzeichen.[8] Zum Beispiel, %pI4 Drucke an IPv4 Adresse in gepunkteter Dezimalform. Dies ermöglicht die Überprüfung der statischen Format -Zeichenfolge (der von der %p Teil) auf Kosten der vollständigen Kompatibilität mit normalem Druck.

Die meisten Sprachen, die a haben printf-Die ähnliche Funktion bearbeiten das Fehlen dieser Funktion, indem Sie nur die verwenden %s Formatieren und Konvertieren des Objekts in eine String -Darstellung.

Schwachstellen

Ungültige Konvertierungsspezifikationen

Wenn es zu wenige gibt Funktionsargumente Bereitstellung für die Versorgung von Werten für alle Konvertierungsspezifikationen in der Vorlagenzeichenfolge oder wenn die Argumente nicht von den richtigen Typen sind, sind die Ergebnisse undefiniert und können zum Absturz kommen. Implementierungen sind inkonsistent darüber, ob Syntaxfehler in der Zeichenfolge ein Argument konsumieren und welche Argumententyp sie verbrauchen. Übermäßige Argumente werden ignoriert. In mehreren Fällen hat das undefinierte Verhalten dazu geführt "Format -String -Angriff"Sicherheit Schwachstellen. In den meisten C- oder C- oder C ++ - Aufrufkonventionen können den Stapel weitergegeben werden, was bedeutet, dass im Fall von zu wenigen Argumenten Printf am Ende des aktuellen StackFrame vorbei lesen, sodass der Angreifer den Stapel lesen kann.

Einige Compiler wie Die GNU Compiler -Sammlungprüft statisch die Formatketten von printf-ähnlichen Funktionen und warnen vor Problemen (bei Verwendung der Flags -Wall oder -Wformat). GCC wird auch vor benutzerdefinierten Funktionen im Printf-Stil warnen, wenn das nicht standardmäßige "Format" __attribute__ wird auf die Funktion angewendet.

Feldbreite gegenüber expliziten Abgrenzern in der tabellarischen Ausgabe

Verwenden Sie nur Feldbreiten zur Bereitstellung von Tabellationen, wie mit einem Format wie %8d%8d%8d Für drei Ganzzahlen in drei 8-Charakter-Spalten garantiert sie nicht, dass die Feldtrennung beibehalten wird, wenn in den Daten große Zahlen auftreten. Der Verlust der Feldtrennung kann leicht zu einer beschädigten Ausgabe führen. In Systemen, die den Einsatz von Programmen als Bausteine ​​in Skripten fördern, können solche korrupten Daten häufig in eine weitere Verarbeitung weitergeleitet und korrupt werden, unabhängig davon, ob der ursprüngliche Programmierer erwartet wurde, dass die Ausgabe nur von menschlichen Augen gelesen wird. Solche Probleme können beseitigt werden, indem explizite Grenzwerte und sogar Räume in alle tabellarischen Ausgangsformate aufgenommen werden. Einfach das gefährliche Beispiel von zuvor zu ändern %7d %7d %7d Es geht darum, dies identisch zu formatieren, bis die Zahlen größer werden, aber dann ausdrücklich verhindert, dass sie aufgrund der explizit enthaltenen Räume mit der Ausgabe verschmolzen werden. Ähnliche Strategien gelten für Zeichenfolgedaten.

Speicher schreiben

Obwohl eine Ausgangsfunktion auf der Oberfläche, printf Ermöglicht das Schreiben an einen Speicherort, der durch ein Argument über die Argument übernimmt %n. Diese Funktionalität wird gelegentlich als Teil von ausgefeilteren Format -String -Angriffen verwendet.[9]

Das %n Funktionalität macht auch printf versehentlich Turing vollständig Auch mit einem wohlgeformten Satz von Argumenten. Ein Spiel mit Tic-Tac-Toe, das in der Formatkette geschrieben wurde, ist ein Gewinner des 27. IOCCC.[10]

Programmiersprachen mit printf

Sprachen, die Formatketten verwenden, die vom Stil in diesem Artikel abweichen (wie z. Ampl und Elixier), Sprachen, die ihre Implementierung von der erben JVM oder eine andere Umgebung (wie z. Clojure und Scala) und Sprachen, die keine Standard -native printf -Implementierung haben, aber externe Bibliotheken haben, die das Printf -Verhalten nachahmen (wie z. JavaScript) sind nicht in dieser Liste enthalten.

Siehe auch

Verweise

  1. ^ "BCPL". Cl.Cam.ac.uk. Abgerufen 19. März 2018.
  2. ^ McIlroy, M. D. (1987). A Research Unix Reader: Annotierte Auszüge aus dem Programmierhandbuch, 1971–1986 (PDF) (Technischer Bericht). Cstr. Bell Labs. 139.
  3. ^ "Printf - C ++ Referenz". cplusplus.com. Abgerufen 10. Juni 2020.
  4. ^ ISO/IEC (1999). ISO/IEC 9899: 1999 (E): Programmiersprachen - C. §7.19.6.1 Abs. 7
  5. ^ ""Die GNU C -Bibliotheksreferenzhandbuch", "12.12.3 Tabelle der Ausgangsumrechnungen"". Gnu.org. Abgerufen 17. März 2014.
  6. ^ "printf" ( %a hinzugefügt in C99)
  7. ^ "Numerische Druckausgabe formatieren". Die Java -Tutorials. Oracle Inc. Abgerufen 19. März 2018.
  8. ^ "Linux-Kernel-Dokumentation/printk-formats.txt". Git.kernel.org. Abgerufen 17. März 2014.
  9. ^ https://www.exploit-db.com/docs/english/28476-linux-format-string-exploitation.pdf[Bare URL PDF]
  10. ^ "Best Of Show - Missbrauch von libc". Ioccc.org. Abgerufen 5. Mai 2022.
  11. ^ ""Die offenen Gruppenbasis -Spezifikationen Ausgabe 7, 2018 Ausgabe", "Possix awk", "Ausgabeanweisungen"". Pubs.opengroup.org. Abgerufen 29. Mai 2022.
  12. ^ "Printf Standard Library". Das Julia -Sprachhandbuch. Abgerufen 22. Februar 2021.
  13. ^ "Eingebaute Typen: printf-Stil -String -Formatierung ", Die Python Standard Library, Python Software Foundation, abgerufen 24. Februar 2021

Externe Links