格式化字串

格式化字串(英語:format string)是一些程式語言的輸入/輸出中能將字串參數轉換為另一種形式輸出的函數。例如C、C++等程式語言的printf類函數,其中的轉換說明(conversion specification)用於把隨後對應的0個或多個函數參數轉換為相應的格式輸出;格式化字串中轉換說明以外的其它字元原樣輸出。[1]

一個printf函數的例子

C語言printf函數為代表的一類

格式化預留位置

格式化字串中的預留位置用於指明輸出的參數值如何格式化。

格式化預留位置(format placeholder),語法是:

%[parameter][flags][field width][.precision][length]type

Parameter可以忽略或者是:

字元 描述
n$ n是用這個格式說明符(specifier)顯示第幾個參數;這使得參數可以輸出多次,使用多個格式說明符,以不同的順序輸出。 如果任意一個預留位置使用了parameter,則其他所有預留位置必須也使用parameter。這是POSIX擴充,不屬於ISO C。例:printf("%2$d %2$#x; %1$d %1$#x",16,17) 產生"17 0x11; 16 0x10"

Flags可為0個或多個:

字元 描述
+ 總是表示有符號數值的'+'或'-'號,預設情況是忽略正數的符號。僅適用於數值類型。
空格 使得有符號數的輸出如果沒有正負號或者輸出0個字元,則字首1個空格。如果空格與'+'同時出現,則空格說明符被忽略。
- 左對齊。預設情況是右對齊。
# 對於'g'與'G',不刪除尾部0以表示精度。對於'f', 'F', 'e', 'E', 'g', 'G', 總是輸出小數點。對於'o', 'x', 'X', 在非0數值前分別輸出字首0, 0x, and 0X表示數制。
0 如果width選項字首以0,則在左側用0填充直至達到寬度要求。例如printf("%2d", 3)輸出" 3",而printf("%02d", 3)輸出"03"。如果0-均出現,則0被忽略,即左對齊依然用空格填充。

Field Width給出顯示數值的最小寬度,典型用於制表輸出時填充固定寬度的表目。實際輸出字元的個數不足域寬,則根據左對齊或右對齊進行填充。實際輸出字元的個數超過域寬並不引起數值截斷,而是顯示全部。寬度值的前導0被解釋為0填充標誌,如上述;前導的負值被解釋為其絕對值,負號解釋為左對齊標誌。如果域寬值為*,則由對應的函數參數的值為當前域寬。

Precision通常指明輸出的最大長度,依賴於特定的格式化類型。對於d、i、u、x、o的整型數值,是指最小數字位數,不足的位要在左側補0,如果超過也不截斷,預設值為1。對於a,A,e,E,f,F的浮點數值,是指小數點右邊顯示的數字位數,必要時四捨五入或補0;預設值為6。對於g,G的浮點數值,是指有效數字的最大位數;預設值為6。對於s的字串類型,是指輸出的位元組的上限,超出限制的其它字元將被截斷。如果域寬為*,則由對應的函數參數的值為當前域寬。如果僅給出了小數點,則域寬為0。

Length指出浮點型參數或整型參數的長度。此項Microsoft稱為「Size」。可以忽略,或者是下述:

字元 描述
hh 對於整數類型,printf期待一個從char提升的int尺寸的整型參數。
h 對於整數類型,printf期待一個從short提升的int尺寸的整型參數。
l 對於整數類型,printf期待一個long尺寸的整型參數。

對於浮點類型,printf期待一個double尺寸的整型參數。對於字串s類型,printf期待一個wchar_t指標參數。對於字元c類型,printf期待一個wint_t型的參數。

ll 對於整數類型,printf期待一個long long尺寸的整型參數。Microsoft也可以使用I64
L 對於浮點類型,printf期待一個long double尺寸的整型參數。
z 對於整數類型,printf期待一個size_t尺寸的整型參數。
j 對於整數類型,printf期待一個intmax_t尺寸的整型參數。
t 對於整數類型,printf期待一個ptrdiff_t尺寸的整型參數。

此外,在ISO C99廣泛接受前,還有幾個平台相關的length選項:

字元 描述
I 對於有符號整數類型,printf期待一個ptrdiff_t尺寸的整型參數。對於無符號整數類型,printf期待一個size_t尺寸的整型參數。常見於Win32/Win64平台。
I32 對於整數類型,printf期待一個32位元(雙字)的整型參數。常見於Win32/Win64平台。
I64 對於整數類型,printf期待一個64位元(四字)的整型參數。常見於Win32/Win64平台。
q 對於整數類型,printf期待一個64位元(四字)的整型參數。常見於BSD平台。

ISO C99的標頭檔inttypes.h包含了許多巨集,用於平台獨立的printf編碼。例如:

巨集 定義
PRId32 典型地等效於I32d (Win32/Win64)或d
PRId64 典型地等效於I64d (Win32/Win64), lld (32位元平台)或ld (64位元平台)
PRIi32 典型地等效於I32i (Win32/Win64)或i
PRIi64 典型地等效於I64i (Win32/Win64), lli (32位元平台)或li (64位元平台)
PRIu32 典型地等效於I32u (Win32/Win64)或u
PRIu64 典型地等效於I64u (Win32/Win64), llu (32位元平台)或lu (64位元平台)
PRIx32 典型地等效於I32x (Win32/Win64)或x
PRIx64 典型地等效於I64x (Win32/Win64), llx (32位元平台)或lx (64位元平台)

類型

Type,也稱轉換說明(conversion specification/specifier),可以是:

字元 描述
d, i 有符號十進制數值int。'%d'與'%i'對於輸出是同義;但對於scanf()輸入二者不同,其中%i在輸入值有字首0x或0時,分別表示16進制或8進制的值。如果指定了精度,則輸出的數字不足時在左側補0。預設精度為1。精度為0且值為0,則輸出為空。
u 十進制unsigned int。如果指定了精度,則輸出的數字不足時在左側補0。預設精度為1。精度為0且值為0,則輸出為空。
f, F double型輸出10進制定點英語Fixed-point arithmetic表示。'f'與'F'差異是表示無窮與NaN時,'f'輸出'inf', 'infinity'與'nan';'F'輸出'INF', 'INFINITY'與'NAN'。小數點後的數字位數等於精度,最後一位數字四捨五入。精度預設為6。如果精度為0且沒有#標記,則不出現小數點。小數點左側至少一位數字。
e, E double值,輸出形式為10進制的([-]d.ddd e[+/-]ddd). E版本使用的指數符號為E(而不是e)。指數部分至少包含2位數字,如果值為0,則指數部分為00。Windows系統,指數部分至少為3位數字,例如1.5e002,也可用Microsoft版的執行時函數_set_output_format 修改。小數點前存在1位數字。小數點後的數字位數等於精度。精度預設為6。如果精度為0且沒有#標記,則不出現小數點。
g, G double型數值,精度定義為全部有效數字位數。當指數部分在閉區間 [-4,5] 內,輸出為定點形式;否則輸出為指數浮點形式。'g'使用小寫字母,'G'使用大寫字母。小數點右側的尾數0不被顯示;顯示小數點僅當輸出的小數部分不為0。
x, X 16進制unsigned int。'x'使用小寫字母;'X'使用大寫字母。如果指定了精度,則輸出的數字不足時在左側補0。預設精度為1。精度為0且值為0,則輸出為空。
o 8進制unsigned int。如果指定了精度,則輸出的數字不足時在左側補0。預設精度為1。精度為0且值為0,則輸出為空。
s 如果沒有用l標誌,輸出null結尾字串英語null-terminated string直到精度規定的上限;如果沒有指定精度,則輸出所有位元組。如果用了l標誌,則對應函數參數指向wchar_t型的陣列,輸出時把每個寬字元轉化為多位元組字元,相當於呼叫wcrtomb函數。
c 如果沒有用l標誌,把int參數轉為unsigned char型輸出;如果用了l標誌,把wint_t參數轉為包含兩個元素的wchart_t陣列,其中第一個元素包含要輸出的字元,第二個元素為null寬字元。
p void *
a, A double型的16進制表示,"[−]0xh.hhhh p±d"。其中指數部分為10進制表示的形式。例如:1025.010輸出為0x1.004000p+10。'a'使用小寫字母,'A'使用大寫字母。[2][3] (C++11流使用hexfloat輸出16進制浮點數)
n 不輸出字元,但是把已經成功輸出的字元個數寫入對應的整型指標參數所指的變數。
% '%'字面值,不接受任何flags, width, precision or length。

寬度與精度格式化參數可以忽略,或者直接指定,或者用星號"*"表示取對應函數參數的值。例如printf("%*d", 5, 10)輸出"   10";printf("%.*s", 3, "abcdef") 輸出"abc"。

如果函數參數太少,不能匹配所有的格式參數說明符,或者函數參數的類型不匹配,將導致未定義(undefined)行為。過多的函數參數被忽略。許多時候,未定義的行為將導致格式化字串攻擊英語Format string attack

某些編譯器,如GCC,會靜態檢查printf這一類函數的格式化參數並編譯警告存在的問題(當使用編譯標誌-Wall-Wformat)。GCC也會對用戶自訂的printf風格函數做靜態檢查,如果在函數定義時使用了非標準的 __attribute__((format(...)))

具有printf風格函數的程式語言

此外,Windows API的FormatMessage函數也使用格式化字串說明。

參考文獻

  1. ^ C11 standard n1570 draft. 7.21.6.1.3 pp309
  2. ^ "The GNU C Library Reference Manual", "12.12.3 Table of Output Conversions". Gnu.org. [2014-03-17]. (原始內容存檔於2021-03-27). 
  3. ^ "printf"頁面存檔備份,存於互聯網檔案館) ("%a" added in C99)

外部連結