řetězec formátu printf - printf format string

Příklad funkce printf

řetězec formátu printf odkazuje na kontrolní parametr používaný třídou funkcí ve vstupních / výstupních knihovnách jazyka C a mnoha dalších programovacích jazyků . Řetězec je napsán v jednoduchém jazyce šablony : znaky se obvykle kopírují doslova do výstupu funkce, ale specifikátory formátu , které začínají %znakem, označují umístění a způsob převodu části dat (například čísla) na znaky.

„printf“ je název jedné z hlavních výstupních funkcí jazyka C a znamená „ print f ormatted“. řetězce formátu printf doplňují řetězce formátu scanf , které poskytují formátovaný vstup ( syntaktickou analýzu ). V obou případech poskytují jednoduchou funkčnost a pevný formát ve srovnání se sofistikovanějšími a flexibilnějšími šablonami nebo analyzátory, ale jsou dostatečné pro mnoho účelů.

Mnoho jazyků jiných než C kopíruje syntaxi řetězce printf formátu přesně nebo přesně ve svých vlastních I / O funkcích.

Neshody mezi specifikátory formátu a typem dat mohou způsobit selhání a další chyby zabezpečení. Samotný formátovací řetězec je velmi často řetězcový literál , který umožňuje statickou analýzu volání funkce. Může to však být také hodnota proměnné, která umožňuje dynamické formátování, ale také chybu zabezpečení známou jako zneužití nekontrolovaného řetězce formátu .

Dějiny

Rané programovací jazyky, jako je Fortran, používaly k vytváření popisů formátování speciální příkazy se zcela odlišnou syntaxí od jiných výpočtů. V tomto příkladu je formát uveden na řádku 601 a příkaz WRITE na něj odkazuje číslem řádku:

      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 měl více funkčních API , ale stále používal speciální syntaxi ( $oddělovače obklopují speciální syntaxi formátování):

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

Ale použití volání normální funkce a datových typů zjednodušuje jazyk a kompilátor a umožňuje, aby implementace vstupu / výstupu byla zapsána ve stejném jazyce. Tyto výhody převažují nad nevýhodami (jako je naprostý nedostatek bezpečnosti typu v mnoha případech) a ve většině novějších jazyků není I / O součástí syntaxe.

C se printfmá svůj původ v BCPL ‚s writeffunkcí (1966). Ve srovnání s Ca printf, *Nje úniková sekvence jazyka BCPL představující znak nového řádku (pro který C používá únikovou sekvenci \n) a pořadí šířky a typu pole specifikace formátu je obráceno v writef:

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

Pravděpodobně první kopírování syntaxe mimo jazyk C byl příkaz Unix printfshell, který se poprvé objevil ve verzi 4 , jako součást portu do C.

Specifikace zástupného formátu

Formátování probíhá prostřednictvím zástupných symbolů v řetězci formátu. Pokud by například program chtěl vytisknout věk osoby, mohl by výstup představit předponou „Váš věk je“ a pomocí podepsaného znaku desítkového specifikátoru doznačit, že chceme, aby se celé číslo pro věk zobrazilo okamžitě po této zprávě můžeme použít formátovací řetězec:

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

Syntax

Syntaxe zástupného symbolu formátu je

% [ parametr ] [ příznaky ] [ šířka ] [. přesnost ] [ délka ] typ

Pole parametru

Toto je rozšíření POSIX a ne v C99 . Pole Parametr lze vynechat nebo může být:

Charakter Popis
n $ n je číslo parametru, který se má zobrazit pomocí tohoto specifikátoru formátu, což umožňuje výstup poskytovaných parametrů vícekrát, s použitím různých specifikátorů formátu nebo v různých objednávkách. Pokud některý jediný zástupný symbol určuje parametr, MUSÍ také určit všechny ostatní zástupné symboly.
Například printf("%2$d %2$#x; %1$d %1$#x",16,17)produkuje 17 0x11; 16 0x10.

Tato vlastnost vidí hlavně jeho použití v lokalizaci, kde se pořadí výskytu parametrů liší kvůli jazykově závislé konvenci.

V systému Microsoft Windows, který není POSIX, je podpora této funkce umístěna do samostatné funkce printf_p.

Pole příznaků

Pole Příznaky může být nula nebo více (v libovolném pořadí) z:

Charakter Popis
-
(minus)
Zarovnejte výstup tohoto zástupného symbolu doleva. (Výchozí nastavení je zarovnání výstupu doprava.)
+
(plus)
Prepends plus pro kladné znaménko-číselné typy. pozitivní = + , negativní = - .
(Výchozí hodnota nic před kladnými čísly nepřepíše.)

(prostor)
Prepends prostor pro kladné znaménko-číselné typy. pozitivní = , negativní = - . Tento příznak je ignorován, pokud existuje příznak + .
(Výchozí hodnota nic před kladnými čísly nepřepíše.)
0
(nula)
Když je zadána možnost 'šířka', předloží nula pro číselné typy. (Výchozí předřazuje mezery.)
Například printf("%4X",3)produkuje 3, zatímco printf("%04X",3)produkuje 0003.
'
(apostrof)
Celé číslo nebo exponent desetinného místa má použit oddělovač seskupení tisíců.
#
(hash)
Alternativní forma:
U typů g a G nejsou odstraněny koncové nuly.
U typů f , F , e , E , g , G výstup vždy obsahuje desetinnou čárku.
U typů o , x , X je text 0 , 0x , 0X předřazen nenulovým číslům.

Šířkové pole

Pole Šířka určuje minimální počet znaků pro výstup a obvykle se používá k vyplnění polí s pevnou šířkou v tabulkovém výstupu, kde by pole byla jinak menší, i když nezpůsobí zkrácení nadměrných polí.

Pole šířky může být vynecháno, nebo číselná celočíselná hodnota nebo dynamická hodnota, když je předána jako další argument, když je označena hvězdičkou * . printf("%*d", 5, 10)Výsledkem bude například 10tisk s celkovou šířkou 5 znaků.

Ačkoli není součástí pole šířky, úvodní nula je interpretována jako příznak nulového výplně uvedený výše a se zápornou hodnotou se zachází jako s kladnou hodnotou ve spojení s příznakem zarovnání doleva - také zmíněným výše.

Přesné pole

Pole Přesnost obvykle určuje maximální limit výstupu v závislosti na konkrétním typu formátování. U číselných typů s plovoucí desetinnou čárkou určuje počet číslic napravo od desetinné čárky, které mají být zaokrouhleny. U typu řetězce omezuje počet znaků, které by měly být na výstupu, po kterém je řetězec zkrácen.

Pole přesnosti může být vynecháno, nebo číselná celočíselná hodnota nebo dynamická hodnota, když je předána jako další argument, když je označena hvězdičkou * . Například printf("%.*s", 3, "abcdef")bude mít za následek abctisk.

Pole délky

Pole Délka lze vynechat nebo být libovolné z:

Charakter Popis
hh U celočíselných typů způsobí printf očekávání celočíselného argumentu velikosti int, který byl povýšen z char .
h Pro typy celočíselných způsobuje printf očekávat int -sized argument celého čísla, která byla podporována ze zkratu .
l Pro celočíselné typy způsobí, že printf očekává celočíselný argument dlouhé velikosti.

U typů s plovoucí desetinnou čárkou je toto ignorováno. float argumenty jsou vždy povýšeny na dvojnásobek, pokud jsou použity ve volání varargs.

ll U celočíselných typů způsobí, že printf očekává dlouhý celočíselný argument dlouhé .
L U typů s plovoucí desetinnou čárkou způsobí, že printf očekává dlouhý dvojitý argument.
z U celočíselných typů způsobí, že printf očekává celočíselný argument size_t -ized.
j U celočíselných typů způsobí, že printf očekává celočíselný argument intmax_t -ized.
t U celočíselných typů způsobí, že printf očekává celočíselný argument ptrdiff_t - velikost.

Před rozšířeným používáním rozšíření ISO C99 navíc existovalo několik možností délky pro konkrétní platformu:

Postavy Popis
U podepsaných celočíselných typů způsobí printf očekávání ptrdiff_t -sized integer argument; pro nepodepsané celočíselné typy způsobí, že printf bude očekávat argument size_t -sized integer argument. Běžně se vyskytuje na platformách Win32 / Win64.
I32 U celočíselných typů způsobí, že printf očekává 32bitový (dvojslovný) celočíselný argument. Běžně se vyskytuje na platformách Win32 / Win64.
I64 U celočíselných typů způsobí, že printf očekává 64bitový (čtyřslovný) celočíselný argument. Běžně se vyskytuje na platformách Win32 / Win64.
q U celočíselných typů způsobí, že printf očekává 64bitový (čtyřslovný) celočíselný argument. Běžně se vyskytuje na platformách BSD.

ISO C99 obsahuje inttypes.hsoubor záhlaví, který obsahuje řadu maker pro použití v printfkódování nezávislém na platformě . Musí to být mimo uvozovky, napřprintf("%" PRId64 "\n", t);

Mezi příklady maker patří:

Makro Popis
PRId32 Obvykle ekvivalentní I32d ( Win32 / Win64 ) nebo d
PRId64 Typicky ekvivalentní I64d ( Win32 / Win64 ), lld ( 32bitové platformy ) nebo ld ( 64bitové platformy )
PRIi32 Obvykle ekvivalentní I32i ( Win32 / Win64 ) nebo i
PRIi64 Typicky ekvivalentní I64i ( Win32 / Win64 ), lli ( 32bitové platformy ) nebo li ( 64bitové platformy )
PRIu32 Typicky ekvivalentní I32u ( Win32 / Win64 ) nebo u
PRIu64 Obvykle ekvivalentní I64u ( Win32 / Win64 ), llu ( 32bitové platformy ) nebo lu ( 64bitové platformy )
PRIx32 Obvykle ekvivalentní I32x ( Win32 / Win64 ) nebo x
PRIx64 Obvykle ekvivalentní I64x ( Win32 / Win64 ), llx ( 32bitové platformy ) nebo lx ( 64bitové platformy )

Zadejte pole

Pole Typ může být kterékoli z:

Charakter Popis
% Vytiskne doslovný znak % (tento typ nepřijímá žádné příznaky, pole šířky, přesnosti, délky).
d , i int jako celé číslo se znaménkem . % d a % i jsou synonymem pro výstup, ale liší se při použití scanfpro vstup (kde použití % i bude interpretovat číslo jako hexadecimální, pokud mu předchází 0x , a osmičkové, pokud mu předchází 0. )
u Tisk desítkové nepodepsané int .
f , F zdvojnásobit v normální ( fixní ) notaci. f a F se liší pouze v tom, jak jsou vytištěny řetězce pro nekonečné číslo nebo NaN ( inf , nekonečno a nan pro f ; INF , INFINITY a NAN pro F ).
e , E dvojnásobná hodnota ve standardní formě ( d . ddd e ± dd ). E konverze používá dopis E (spíše než e ) zavést exponent. Exponent vždy obsahuje alespoň dvě číslice; pokud je hodnota nula, je exponent 00 . Ve Windows obsahuje exponent ve výchozím nastavení tři číslice, např. 1.5e002 , ale toto může být změněno _set_output_formatfunkcí specifickou pro Microsoft .
g , G zdvojnásobte buď normální nebo exponenciální notaci, podle toho, co je pro její velikost vhodnější. g používá malá písmena, G používá velká písmena. Tento typ se mírně liší od notace s pevnou čárkou v tom, že nejsou zahrnuty zanedbatelné nuly napravo od desetinné čárky. Desetinná čárka také není uvedena na celých číslech.
x , X unsigned int jako šestnáctkové číslo. x používá malá písmena a X velká písmena.
Ó unsigned int v osmičkovém formátu.
s řetězec zakončený nulou .
C char (znak).
p void * (ukazatel na void) ve formátu definovaném implementací.
a , A zdvojnásobte hexadecimální zápis, počínaje 0x nebo 0X . a používá malá písmena, A používá velká písmena. (C ++ 11 iostreams mají hexfloat, který funguje stejně).
n Netiskněte nic, ale zapište počet dosud zapsaných znaků do parametru celočíselného ukazatele.
V Javě se vytiskne nový řádek.

Zástupné symboly vlastního formátu

Existuje několik implementací printfpodobných funkcí, které umožňují rozšíření mini jazyka založeného na únikových znacích , což umožňuje programátorovi mít specifickou funkci formátování pro nepředstavené typy. Jedním z nejznámějších je (nyní však) glibc ‚s . Je však zřídka používán kvůli skutečnosti, že je v konfliktu s kontrolou řetězce statického formátu. Dalším je vlastní formátovač Vstr , který umožňuje přidávat názvy víceznakových formátů. register_printf_function()

Některé aplikace (například Apache HTTP Server ) obsahují své vlastní printffunkce a vkládají do nich rozšíření. Všechny tyto problémy však mají stejné problémy register_printf_function().

Funkce jádra Linux printk podporuje řadu způsobů, jak zobrazit struktury jádra pomocí generické %pspecifikace, připojením dalších znaků formátu. Například %pI4vytiskne adresu IPv4 v desítkovém formátu s tečkami. To umožňuje kontrolu řetězce statického formátu ( %pčásti) na úkor plné kompatibility s běžným printf.

Většina jazyků, které mají printffunkci podobnou, obchází nedostatek této funkce pouhým použitím %sformátu a převedením objektu na řetězcovou reprezentaci.

Zranitelnosti

Neplatné specifikace převodu

Pokud je poskytnuto příliš málo argumentů funkcí k zadání hodnot pro všechny specifikace převodu v řetězci šablony, nebo pokud argumenty nejsou správných typů, výsledky jsou nedefinované, může dojít k chybě. Implementace jsou nekonzistentní ohledně toho, zda syntaktické chyby v řetězci spotřebovávají argument a jaký typ argumentu konzumují. Přebytečné argumenty jsou ignorovány. V řadě případů vedlo nedefinované chování k chybám zabezpečení typu „ Formátovat útok řetězce “ . Ve většině C nebo C ++ konvence volání mohou být předány argumenty na zásobníku, což znamená, že v případě příliš malého počtu argumentů bude printf číst za konec aktuálního stackframe, což umožňuje útočníkovi číst zásobník.

Některé překladače, jako je GNU Compiler Collection , staticky zkontrolují formátovací řetězce funkcí podobných printf a upozorní na problémy (při použití příznaků -Wallnebo -Wformat). GCC také varuje před uživatelem definovanými funkcemi stylu printf, pokud __attribute__je na funkci použit nestandardní „formát“ .

Šířka pole versus explicitní oddělovače v tabulkovém výstupu

Použití pouze šířky pole k zajištění tabelace, stejně jako u formátu jako %8d%8d%8dpro tři celá čísla ve třech 8místných sloupcích, nezaručí, že bude zachováno oddělení polí, pokud se v datech vyskytne velké množství. Ztráta oddělení polí může snadno vést k poškození výstupu. V systémech, které podporují použití programů jako stavebních kamenů ve skriptech, mohou být taková poškozená data často předávána a poškozovat další zpracování, bez ohledu na to, zda původní programátor očekával, že výstup bude čten pouze lidskýma očima. Takové problémy lze odstranit zahrnutím explicitních oddělovačů, dokonce mezer, do všech tabulkových výstupních formátů. Jednoduše změna nebezpečného příkladu z dříve na to %7d %7d %7dřeší toto, formátování identicky, dokud se čísla nezvětší, ale poté jim explicitně zabráníte ve sloučení na výstupu kvůli explicitně zahrnutým mezerám. Podobné strategie platí i pro řetězcová data.

Zápis do paměti

Ačkoli je výstupní funkcí na povrchu, printfumožňuje zápis do paměťového umístění určeného argumentem prostřednictvím %n. Tato funkce se příležitostně používá jako součást komplikovanějších formátovacích řetězcových útoků.

Díky této %nfunkci je Turingprintf omylem kompletní i s dobře sestavenou sadou argumentů. Hra tic-tac-toe napsaná ve formátu řetězce je vítězem 27. IOCCC .

Programovací jazyky s printf

Jazyky, které používají formátovací řetězce, které se odchylují od stylu v tomto článku (například AMPL a Elixir ), jazyky, které dědí jejich implementaci z JVM nebo jiného prostředí (například Clojure a Scala ), a jazyky, které nemají standardní nativní tisk implementace, ale mají externí knihovny, které emulují chování tisku (například JavaScript ), nejsou v tomto seznamu zahrnuty.

Viz také

Reference

externí odkazy