printf-formatstreng - printf format string

Image
Et eksempel på printf-funktionen

printf-formatstreng henviser til en kontrolparameter, der bruges af en klasse af funktioner i input / output-bibliotekerne i C og mange andre programmeringssprog . Strengen er skrevet på et simpelt skabelonsprog : tegn kopieres normalt bogstaveligt til funktionens output, men formatspecifikatorer , der starter med et %tegn, angiver placeringen og metoden til at oversætte et stykke data (såsom et tal) til tegn.

"printf" er navnet på en af ​​de vigtigste C-outputfunktioner og står for " print f ormatted". printf-formatstrenge er komplementære til scanf-formatstrenge , som giver formateret input ( parsing ). I begge tilfælde giver disse enkel funktionalitet og fast format sammenlignet med mere sofistikerede og fleksible skabelonmotorer eller parsere, men er tilstrækkelige til mange formål.

Mange andre sprog end C kopierer syntaxen for printf-format tæt eller nøjagtigt i deres egne I / O-funktioner.

Misforhold mellem formatformaterne og datatypen kan forårsage nedbrud og andre sårbarheder. Formatstrengen i sig selv er meget ofte en strenglitteral , som muliggør statisk analyse af funktionsopkaldet. Det kan dog også være værdien af ​​en variabel, der giver mulighed for dynamisk formatering, men også en sikkerhedssårbarhed kendt som en ukontrolleret formatstreng- udnyttelse.

Historie

Tidlige programmeringssprog som Fortran brugte specielle udsagn med helt anden syntaks fra andre beregninger til at oprette formateringsbeskrivelser. I dette eksempel er formatet specificeret på linje 601, og WRITE-kommandoen refererer til det med linjenummer:

      WRITE OUTPUT TAPE 6, 601, IA, IB, IC, AREA
 601  FORMAT (4H A= ,I5,5H  B= ,I5,5H  C= ,I5,
     &        8H  AREA= ,F10.2, 13H SQUARE UNITS)

ALGOL 68 havde mere funktionslignende API , men brugte stadig speciel syntaks ( $afgrænserne omgiver speciel formateringssyntaks):

printf(($"Color "g", number1 "6d,", number2 "4zd,", hex "16r2d,", float "-d.2d,", unsigned value"-3d"."l$,
         "red", 123456, 89, BIN 255, 3.14, 250));

Men ved hjælp af de normale funktionsopkald og datatyper forenkles sproget og compileren og gør det muligt at skrive implementeringen af ​​input / output på det samme sprog. Disse fordele opvejer ulemperne (såsom en fuldstændig mangel på typesikkerhed i mange tilfælde) og i de fleste nyere sprog er I / O ikke en del af syntaksen.

C'er printfhar sin oprindelse i BCPL 's writeffunktion (1966). I forhold til Cog printf, *Ner en BCPL sprog escapesekvens repræsenterer en ny linje (som C anvender escape-sekvensen \n) og rækkefølgen af formatet specifikationen felt bredde og type er vendt i writef:

WRITEF("%I2-QUEENS PROBLEM HAS %I5 SOLUTIONS*N", NUMQUEENS, COUNT)

Sandsynligvis var den første kopiering af syntaksen uden for C-sproget Unix printfshell-kommandoen, som først dukkede op i version 4 , som en del af porten til C.

Formater specifikation af pladsholder

Formatering finder sted via pladsholdere inden for formatstrengen. For eksempel, hvis et program ønskede at udskrive en persons alder, kunne det præsentere output ved at præfikse det med "Your age is" og bruge det underskrevne decimaltegn dtil at angive, at vi ønsker, at heltal for alderen skal vises med det samme efter denne meddelelse kan vi bruge formatstrengen:

printf("Your age is %d", age);

Syntaks

Syntaksen for et format pladsholder er

% [ parameter ] [ flag ] [ bredde ] [. præcision ] [ længde ] type

Parameterfelt

Dette er en POSIX- udvidelse og ikke i C99 . Parameterfeltet kan udelades eller kan være:

Karakter Beskrivelse
n $ n er nummeret på den parameter, der skal vises ved hjælp af denne formatspecifikator, så de angivne parametre kan udlæses flere gange ved hjælp af forskellige formatspecifikatorer eller i forskellige ordrer. Hvis en enkelt pladsholder angiver en parameter, SKAL alle resten af ​​pladsholderne også angive en parameter.
For eksempel printf("%2$d %2$#x; %1$d %1$#x",16,17)producerer 17 0x11; 16 0x10.

Denne funktion ser hovedsagelig brugen af ​​den i lokalisering, hvor rækkefølgen af ​​parametrernes forekomst varierer på grund af den sprogafhængige konvention.

På ikke-POSIX Microsoft Windows er understøttelse af denne funktion placeret i en separat printf_p-funktion.

Flagfelt

Feltet Flag kan være nul eller mere (i en hvilken som helst rækkefølge) af:

Karakter Beskrivelse
-
(minus)
Venstrejuster output for denne pladsholder. (Standard er at højrejustere output.)
+
(plus)
Forudsætter et plus for positive signatur-numeriske typer. positiv = + , negativ = - .
(Standarden afhænger ikke af noget foran positive tal.)

(plads)
Forbereder et mellemrum til positive signatur-numeriske typer. positiv = negativ = - . Dette flag ignoreres, hvis + -fanen findes.
(Standarden afhænger ikke af noget foran positive tal.)
0
(nul)
Når indstillingen 'bredde' er angivet, udfyldes nuller til numeriske typer. (Standard forudbestiller mellemrum.)
F.eks. printf("%4X",3)Producerer 3mens printf("%04X",3)producerer 0003.
'
(apostrof)
Heltallet eller eksponenten for et decimal har tusindvis grupperingsseparator anvendt.
#
(hash)
Alternativ form: Efterfølgende nuller fjernes ikke
for g- og G- typer.
For typerne f , F , e , E , g , G indeholder output altid et decimaltegn.
For o- , x- , X- typer er teksten 0 , 0x , 0X henholdsvis forudbestemt til ikke-nul tal.

Bredde felt

Feltet Bredde angiver et minimum antal tegn, der skal udføres, og bruges typisk til at blokere felter med fast bredde i output i tabeller, hvor felterne ellers ville være mindre, selvom det ikke forårsager trunkering af for store felter.

Breddefeltet kan udelades, eller en numerisk heltalsværdi eller en dynamisk værdi, når den sendes som et andet argument, når det er angivet med en stjerne * . For eksempel printf("%*d", 5, 10)vil det resultere i 10udskrivning med en samlet bredde på 5 tegn.

Selvom det ikke er en del af breddefeltet, fortolkes et førende nul som det ovennævnte nul-polstringsflag, og en negativ værdi behandles som den positive værdi i forbindelse med venstrejusteringen - flag også nævnt ovenfor.

Præcisionsfelt

Præcisionsfeltet angiver normalt en maksimumsgrænse for output afhængigt af den bestemte formateringstype. For numeriske typer med flydende punkt specificeres antallet af cifre til højre for decimaltegnet, som output skal afrundes. For strengtypen begrænser det antallet af tegn, der skal sendes, hvorefter strengen afkortes.

Præcisionsfeltet kan udelades, eller en numerisk heltalsværdi eller en dynamisk værdi, når den sendes som et andet argument, når det er angivet med en stjerne * . For eksempel printf("%.*s", 3, "abcdef")vil det resultere i abcudskrivning.

Længde felt

Feltet længde kan udelades eller være et af:

Karakter Beskrivelse
hh For heltalstyper forårsager printf at forvente et int- dimensioneret heltalargument, som blev promoveret fra en char .
h For heltalstyper forårsager printf at forvente et int- dimensioneret heltalargument, der blev promoveret fra en kort .
l For heltal typer, årsager printf forvente en lang -størrelse heltal argument.

For flydende punkttyper ignoreres dette. float argumenter er altid forfremmet til det dobbelte , når de anvendes i en varargs opkald.

ll For heltalstyper forårsager printf at forvente et langt langstor heltalargument.
L For flydende punkttyper forårsager printf at forvente et langt dobbeltargument .
z For heltalstyper forårsager printf at forvente et størrelse_t-størrelse heltalargument .
j For heltalstyper forårsager printf at forvente et intmax_t-dimensioneret heltalargument .
t For heltalstyper forårsager printf at forvente et ptrdiff_t-dimensioneret heltalargument .

Derudover eksisterede der flere platformsspecifikke længdemuligheder inden udbredt brug af ISO C99-udvidelser:

Tegn Beskrivelse
jeg For signerede heltalstyper forårsager printf at forvente ptrdiff_t-dimensioneret heltalargument ; for ikke-signerede heltal typer, årsager printf forvente size_t -størrelse heltal argument. Almindeligvis fundet på Win32 / Win64-platforme.
I32 For heltalstyper forårsager printf at forvente et 32-bit (dobbelt ord) heltalargument. Almindeligvis fundet på Win32 / Win64-platforme.
I64 For heltalstyper forårsager printf at forvente et 64-bit (kvadratord) heltalargument. Almindeligvis fundet på Win32 / Win64-platforme.
q For heltalstyper forårsager printf at forvente et 64-bit (kvadratord) heltalargument. Almindeligvis fundet på BSD-platforme.

ISO C99 inkluderer inttypes.hheaderfilen, der indeholder et antal makroer til brug i platformuafhængig printfkodning. Disse skal være uden for dobbelt anførselstegn, f.eksprintf("%" PRId64 "\n", t);

Eksempel på makroer inkluderer:

Makro Beskrivelse
PRId32 Svarer typisk til I32d ( Win32 / Win64 ) eller d
PRId64 Typisk svarende til I64d ( Win32 / Win64 ), lld ( 32-bit platforme ) eller ld ( 64-bit platforme )
PRIi32 Svarer typisk til I32i ( Win32 / Win64 ) eller i
PRIi64 Svarer typisk til I64i ( Win32 / Win64 ), lli ( 32-bit platforme ) eller li ( 64-bit platforme )
PRIu32 Svarer typisk til I32u ( Win32 / Win64 ) eller u
PRIu64 Svarer typisk til I64u ( Win32 / Win64 ), llu ( 32-bit platforme ) eller lu ( 64-bit platforme )
PRIx32 Svarer typisk til I32x ( Win32 / Win64 ) eller x
PRIx64 Svarer typisk til I64x ( Win32 / Win64 ), llx ( 32-bit platforme ) eller lx ( 64-bit platforme )

Skriv felt

Type-feltet kan være et af:

Karakter Beskrivelse
% Udskriver et bogstaveligt % tegn (denne type accepterer ikke noget flag, bredde, præcision, længde felter).
d , i int som et signeret heltal . % d og % i er synonyme for output, men er forskellige, når de bruges scanftil input (hvor brug af % fortolker et tal som hexadecimalt, hvis det er forud for 0x , og oktalt, hvis det er forud for 0. )
u Udskriv decimal usigneret int .
f , F dobbelt i normal ( fast punkt ) notation. f og F adskiller sig kun i, hvordan strengene for et uendeligt antal eller NaN udskrives ( inf , uendelig og nan for f ; INF , INFINITY og NAN for F ).
e , E dobbelt værdi i standardform ( d . ddd e ± dd ). En E- konvertering bruger bogstavet E (snarere end e ) til at introducere eksponenten. Eksponenten indeholder altid mindst to cifre; hvis værdien er nul, er eksponenten 00 . I Windows indeholder eksponenten som standard tre cifre, f.eks. 1.5e002 , men dette kan ændres af Microsoft-specifik _set_output_formatfunktion.
g , G dobbelt i enten normal eller eksponentiel notation, alt efter hvad der er mere passende for dens størrelse. g bruger små bogstaver, G bruger store bogstaver. Denne type adskiller sig lidt fra fast punktnotation, da ubetydelige nuller til højre for decimaltegnet ikke er inkluderet. Desuden er decimaltegnet ikke inkluderet i hele tal.
x , X usigneret int som et hexadecimalt tal. x bruger små bogstaver, og X bruger store bogstaver.
o usigneret int i oktal.
s null-termineret streng .
c char (karakter).
s void * (pointer to void) i et implementeringsdefineret format.
a , A dobbelt i hexadecimal notation, startende med 0x eller 0X . a bruger små bogstaver, A bruger store bogstaver. (C ++ 11 iostreams har en hexfloat, der fungerer det samme).
n Udskriv intet, men skriver antallet af tegn, der hidtil er skrevet i et heltalsmarkørparameter.
I Java udskriver dette en ny linje.

Pladsholdere i tilpasset format

Der er et par implementeringer af printf-lignende funktioner, der tillader udvidelser til det escape-karakter- baserede mini-sprog , hvilket gør det muligt for programmøren at have en bestemt formateringsfunktion til ikke-indbyggede typer. En af de mest kendte er den (nu forældet) glibc 's register_printf_function(). Det bruges dog sjældent på grund af det faktum, at det er i konflikt med strengkontrol af strengformat. En anden er Vstr brugerdefinerede formateringsprogrammer , som tillader tilføjelse af formatnavne med flere tegn.

Nogle applikationer (som Apache HTTP Server ) inkluderer deres egen printffunktion og integrerer udvidelser i den. Imidlertid har disse alle en tendens til at have de samme problemer, som register_printf_function()har.

Den Linux-kernen printk funktion understøtter en række måder at vise kerne strukturer ved hjælp af den generiske %pspecifikation, ved at tilføje ekstra format tegn. %pI4Udskriver f.eks. En IPv4- adresse i punkteret decimal. Dette muliggør kontrol af streng i statisk format (af %pdelen) på bekostning af fuld kompatibilitet med normal printf.

De fleste sprog, der har en printflignende funktion, omgår manglen på denne funktion ved blot at bruge %sformatet og konvertere objektet til en strengrepræsentation.

Sårbarheder

Ugyldige konverteringsspecifikationer

Hvis der er for få funktionsargumenter til at levere værdier til alle konverteringsspecifikationerne i skabelonstrengen, eller hvis argumenterne ikke er af de rigtige typer, er resultaterne udefinerede, kan gå ned. Implementeringer er inkonsekvente med hensyn til, om syntaksfejl i strengen bruger et argument, og hvilken type argument de bruger. Overskydende argumenter ignoreres. I en række tilfælde har udefineret adfærd førte til " Format snor angreb " sikkerheds sårbarheder . I de fleste C- eller C ++ -opkaldskonventioner kan argumenter sendes på stakken, hvilket betyder, at i tilfælde af for få argumenter vil printf læse forbi slutningen af ​​den nuværende stackframe, hvorved angriberen kan læse stakken.

Nogle compilers, som GNU Compiler Collection , vil statisk kontrollere formatstrengene for printf-lignende funktioner og advare om problemer (når du bruger flag -Walleller -Wformat). GCC advarer også om brugerdefinerede printf-stilfunktioner, hvis det ikke-standardiserede "format" __attribute__anvendes til funktionen.

Feltbredde kontra eksplicit afgrænsning i tabelformat output

Brug kun feltbredder til at give tabellering, som med et format som %8d%8d%8dfor tre heltal i tre kolonner med 8 tegn, garanterer ikke, at feltadskillelse bevares, hvis der forekommer store tal i dataene. Tab af markadskillelse kan let føre til korrupt output. I systemer, der tilskynder til brug af programmer som byggesten i scripts, kan sådanne korrupte data ofte videresendes til og ødelægge videre behandling, uanset om den oprindelige programmør forventede, at output kun ville blive læst af menneskelige øjne. Sådanne problemer kan elimineres ved at inkludere eksplicitte afgrænsere, jævne mellemrum, i alle formatformater. Du skal blot ændre det farlige eksempel fra før til at %7d %7d %7dadressere dette, formatere identisk, indtil tallene bliver større, men derefter eksplicit forhindre dem i at blive flettet på output på grund af de eksplicit inkluderede mellemrum. Lignende strategier gælder for strengdata.

Hukommelse skriv

Selvom en outputfunktion på overfladen printftillader skrivning til en hukommelsesplacering specificeret af et argument via %n. Denne funktionalitet bruges lejlighedsvis som en del af mere detaljerede formatangreb.

Den %nfunktionalitet gør også printfet uheld Turing komplet selv med en velformet sæt af argumenter. Et spil tic-tac-toe skrevet i formatstrengen er vinder af den 27. IOCCC .

Programmeringssprog med printf

Sprog, der bruger formatstrenge, der afviger fra stilen i denne artikel (såsom AMPL og Elixir ), sprog, der arver deres implementering fra JVM eller andre omgivelser (såsom Clojure og Scala ), og sprog, der ikke har en standard native printf implementering, men har eksterne biblioteker, der efterligner printf-opførsel (såsom JavaScript ), er ikke inkluderet i denne liste.

Se også

Referencer

eksterne links