一、基本介紹
1、printf()函數的語法
關於格式化字符串,我們從最基本的一個函數入手,這個函數是printf()。它的的聲明如下:
int printf(
const char *format [,
argument]...
);
參數format
它用以格式控制,通過含%的字符串來實現,如”%d”。參數里的%可以不止1個。
其實,該參數中,還可以使用普通字符和轉義字符,如”My%d\n”。
該參數內除了輸出控制符和轉義字符外,其余字符原樣輸出。
該參數的完整語法如下:
%[flags] [width] [.precision] [{h | l | ll | I | I32 | I64}]type
后面講詳細介紹這些語法。
參數argument
它是可選參數,不是必須的,可能是1個,2個……,這跟前邊一個參數中%的數量有關。
返回值:若成功,返回打印字符串的數量;若失敗,返回負數。
2、典型格式化字符串函數的基本用法
關於格式化字符串的C運行庫函數較多,它們的格式化用法,跟printf()的用法大同小異。
典型的格式化字符串函數有如下:
printf, _printf_l, wprintf, _wprintf_l
sprintf, _sprintf_l, swprintf, _swprintf_l, __swprintf_l
scanf, _scanf_l, wscanf, _wscanf_l
sscanf, _sscanf_l, swscanf, _swscanf_l
(1)、第一組簡介
它們輸出類型,將字符串顯示在屏幕。
示例
int i = 35;
printf("%d",i); //顯示35
(2)、第二組簡介
它們為輸出類型,將格式化后的字符串保存在變量,而不顯示在屏幕。
示例
char buffer[200], s[] = "computer";
sprintf(buffer, "My %s\n", s);
printf("%s", buffer); //顯示My computer
(3)、第三組簡介
它們為輸入類型,從鍵盤輸入,保存在變量。
int i, result;
float fp;
char c, s[81];
wchar_t wc, ws[81];
result = scanf( "%d %f %c %C %80s %80S", &i, &fp, &c, &wc, s, ws );
printf("The number of fields input is %d\n", result);
printf("The contents are: %d %f %c %C %s %S\n", i, fp, c, wc, s, ws);
從鍵盤輸入如下內容(最后還要按回車鍵):
71 98.6 h z Byte characters
然后將顯示如下內容:
The number of fields input is 6
The contents are: 71 98.599998 h z Byte characters
(4)、第四組簡介
它們為輸入類型,不從鍵盤輸入,將給定的字符串按指定格式,保存在變量。
char tokenstring[] = "15 12 14...";
char s[81];
char c;
int i;
float fp;
sscanf(tokenstring, "%80s", s);
sscanf(tokenstring, "%c", &c);
sscanf(tokenstring, "%d", &i);
sscanf(tokenstring, "%f", &fp);
printf("String = %s\n", s);
printf("Character = %c\n", c);
printf("Integer: = %d\n", i);
printf("Real: = %f\n", fp);
顯示如下內容:
String = 15
Character = 1
Integer: = 15
Real: = 15.000000
(5)、sscanf()與sprintf()的區別
它們都不需要硬件輸入輸出,且最終結果都是保存在變量中,但兩者還是有區別的。Sscanf()的最大特點是要給定一字符串(第1個參數),在該值基礎上格式化輸出;而sprintf()則不需要給定一字符串,直接將格式化的部分的內容保存在變量。
二、printf()函數的語法詳解
該函數的第1個參數是輸入參數,是字符串類型,用於格式化字符串,它的完整語法如下:
%[flags] [width] [.precision] [{h | l | ll | I | I32 | I64}]type
其中%是格式標志符號,是必須的且固定不變; [……] 部分是可選的;type表示數據類型,和%一樣,也是必須的。
1、[flags]部分的用法
它為標志符號,共有6個:–、+、0、空格符(' ')、#、*
(1)、“–”標志符號
在缺省情況下,輸出結果采用右對齊;若使用了“-”標志,則在給定的字段寬度內采用左對齊。
(2)、“+”標志符號
如果輸出值是有符號整數類型,則在輸出值前面加上正負符號(+或-)。
(3)、“0”標志符號
如果width以“0”為前綴,則會添加0直到達到最小寬度為止。
注意:以下兩種情況,“0”標志符號功能將被忽略。
A、如果使用了“-”標志,則“0”標志符號功能不在起作用。
B、如果“0”是用整數格式指定的(i、u、x、X、o、d),並且還提供了精度規范(例如,%04.d), “0”標志符號功能也不在起作用。
(4)、blank(空格)標志符號
如果輸出值是帶符號且為正的正數,則在輸出值的前面加上一個空白;如果同時出現空白和+標志,空白將被忽略。
這里的blank標志符號,是針對帶符號的正整數而言。對於字符串的空格,則直接使用’ ’字符。
(5)、“#”標志符號
當與o、x或X格式一起使用時,分別輸出0、0x或0X作為任何非零輸出值的前綴,分別表示八進制數、十六進制數、十六進制數。前綴0x與0X都表示十六進制數。
(6)、“*”標志符號
“*”要占一個變量的位置,不過它被忽略,從“*”的下一個位置開始取值。整數類型支持這種功能,但是浮點數則不支持。
2、[width]部分的用法
這個可選字段是寬度規范。寬度參數是一個非負的十進制整數,控制打印的最小字符數。如果輸出值中的字符數小於指定的寬度,則會在值的左邊或右邊添加空格(若使用了“-”標志,則左對齊,右邊補空格;否則右邊對齊,左邊補空格),直到達到最小寬度。
一般情況下,寬度與“0”標志符號搭配使用,才有意義。如果width以“0”為前綴,則會添加0直到達到最小寬度為止(對於左對齊的數字沒有用處)。
寬度規范不會導致值被截斷。如果輸出值中的字符數大於指定的寬度,或者寬度未指定,則打印該值的所有字符(根據精度要求)。
3、[.precision]部分的用法
這個可選字段是精度規范。它指定一個非負的十進制整數,前面有一個句點,即“.”符號,它指定要打印的字符數、小數點位數或有效位數(請參閱精度值如何影響類型表)。
與寬度規范不同,精度規范可能導致輸出值的截斷或浮點值的舍入。
如果將精度指定為0,且要轉換的值為0,則結果為不輸出字符,參考如下:
printf( "%.0d", 0); //結果為空值
類型決定了精度的解釋,當省略精度時,默認值如下表所示。
| 類型 |
含義 |
缺省值 |
| a, A |
精度指定小數點后的位數。
|
默認精度為6。如果精度為0,則不打印小數點,除非使用#標志。 |
| c, C |
非數值型數據,與精度無關。 |
字符直接打印出來。 |
| d, i, u, o, x, X |
精度指定要打印的最小數字個數。若參數中的數字個數小於精度,輸出值將在左側用零填充。若參數中的數字個數大於精度時,輸出值為參數值。 |
默認精度為1。
|
| e, E |
精度指定小數點后的位數,最后打印的數字四舍五入。 |
默認精度為6;如果precision為0或句點(.)后面沒有數字,則不打印小數點。 |
| f |
精度指定小數點后的位數。如果出現小數點,則小數點前至少出現一位數字。該值被四舍五入到適當的位數。 |
默認精度為6;如果精度為0,或者句點(.)后面沒有數字,則不打印小數點。 |
| g, G |
精度指定打印的最大有效數字數。
|
將打印6個有效數字,末尾的零將被截斷。 |
| s, S |
精度指定要打印的最大字符數,不打印超過精度的字符。 |
字符一直打印到遇到空字符為止。 |
4、[{h | l | ll | I | I32 | I64}]部分的用法
type的可選前綴h、l、I、I32、I64和ll指定參數的“size”(長或短、32或64位、單字節字符或寬字符,取決於它們修改的類型說明符)。這些類型說明符前綴與printf函數或wprintf函數中的類型字符一起使用,以指定參數的解釋,如下表所示。
當h和l前綴作為字符使用時,是Microsoft擴展字符,而不是ansi的。
| 說明 |
前綴 |
類型說明 |
| long int |
l |
d, i, o, x或X |
| long unsigned int |
l |
o, u, x或X |
| long long |
l l |
d, i, o, x或X |
| short int |
h |
d, i, o, x或X |
| short unsigned int |
h |
o, u, x或X |
| __int32 |
I32 |
d, i, o, x或X |
| unsigned __int32 |
I32 |
o, u, x或X |
| __int64 |
I64 |
d, i, o, x或X |
| unsigned __int64 |
I64 |
o, u, x或X |
| ptrdiff_t (在32位系統,為__int32類型;在64位系統,為__int64類型) |
I |
d, i, o, x或X |
| size_t (在32位系統,為__int32類型;在64位系統,為__int64類型) |
I |
o, u, x或X |
| long double |
l |
f |
| 在printf函數中使用的單字節字符 |
h
|
c或C |
| 在wprintf函數中使用的單字節字符 |
h
|
c或C |
| 在printf函數中使用的寬字節字符 |
l |
c或C |
| 在wprintf函數中使用的寬字節字符 |
l
|
c或C |
| 在printf函數中使用的單字節字符串 |
h
|
s或S |
| 在wprintf函數中使用的單字節字符串 |
h
|
s或S |
| 在printf函數中使用的寬字節字符串 |
l
|
s或S |
| 在wprintf函數中使用的寬字節字符串 |
l
|
s或S |
| 寬字節字符 |
w |
c |
| 寬字節字符串 |
w |
s |
printf函數和wprintf函數打印單字節或寬字符,請使用如下格式說明符。
| 輸出字符類別 |
使用函數 |
格式控制符 |
| 單字節字符 |
printf |
c, hc或hC |
| 單字節字符 |
wprintf |
c, hc或hC |
| 寬字節字符 |
wprintf |
c, lc, lC或 wc |
| 寬字節字符 |
printf |
c, lc, lC或wc |
5、type部分的用法
類型字符是唯一必需的格式字段,它出現在任何可選的格式字段之后。類型字符確定關聯參數是被解釋為字符、字符串還是數字。類型C、n、p和S,以及C和S與printf函數的行為,都是Microsoft擴展,不是ANSI兼容的。
| 類型 |
實際類型 |
輸出格式 |
| c |
int 或 wint_t |
當使用printf函數時,指定一個單字節字符;當使用wprintf函數時,指定一個寬字符。 注:wint_t為unsigned short |
| C |
int 或 wint_t |
當使用printf函數一起時,指定一個寬字符;當使用wprintf函數時,指定一個單字節字符。 |
| d |
int |
有符號十進制整數。 |
| i |
int |
有符號十進制整數。 |
| o |
unsigned int |
無符號八進制整數。 |
| u |
unsigned int |
無符號十進制整數。 |
| x |
unsigned int |
無符號十六進制整數,使用abcdef。 |
| X |
unsigned int |
無符號十六進制整數,使用ABCDEF。 |
| e |
double |
采用科學計數法,形式為[-]d.dddd e [sign]dd[d]的帶符號值。其中d為單個十進制數,dddd為一個或多個十進制數,dd[d]為兩個或三個十進制數,符號為+或-。 |
| E |
double |
和e格式一樣,只是輸出結果用E替換e。 |
| f |
double |
形式為[-]dddd.dddd的帶符號值,其中dddd是一個或多個十進制數字。小數點前的位數取決於數字的大小,小數點后的位數取決於要求的精度。 |
| g |
double |
以f或e格式打印帶符號值,這取決於哪個更短。e格式僅在值的指數小於-4或大於或等於precision參數時使用。后面的零會被截斷,小數點只在后面有一個或多個數字時才會出現。 |
| G |
double |
與g格式相同,只是輸出結果用E替換e除了E。 |
| a |
double |
采用P計數法,形式為[−]0xh.hhhh P±dd的有符號十六進制雙精度浮點值,其中h.hhhh為尾數的十六進制數字(用小寫字母表示),dd為指數的一個或多個數字。精度指定點后的位數。 |
| A |
double |
采用P計數法,形式為[−]0Xh.hhhh P±dd的有符號十六進制雙精度浮點值,其中h.hhhh為尾數的十六進制數字(大寫字母),dd為指數的一個或多個數字。精度指定小數點后的位數。 |
| n |
指向整型的指針 |
到目前為止成功寫入流或緩沖區的字符數;該值存儲在整數中,其地址作為參數給出。 |
| p |
指向任意型的指針 |
將打印十六進制數表示的變量地址值。 |
| s |
字符串 |
當與printf函數一起使用時,指定一個單字節字符串;當與wprintf函數一起使用時,指定寬字符字符串。字符將一直打印到第一個空字符或直到達到精度值為止。 |
| S |
字符串 |
當與printf函數一起使用時,指定寬字符字符串;當與wprintf函數一起使用時,指定一個單字節字符串。字符將一直打印到第一個空字符或直到達到精度值為止。 |
注意:如果對應於%s或%S的參數是空指針,“(null)”將被打印。
注意:在所有指數格式中,默認顯示的指數位數是3。使用_set_output_format函數,可以將顯示的數字數設置為2,如果exponent的大小需要,則擴展為3。
安全注意:%n格式本質上是不安全的,默認情況下是禁用的;如果在格式字符串中遇到%n,將按照參數驗證中所述調用無效的參數處理程序。要啟用%n支持,請使用_set_printf_count_output函數。
三、printf()函數的用法
現在大多的程序是在MFC代碼,CString類的Format()成員的用法,跟printf()函數一致。因此,示例采用MFC程序。
可能要包含如下文件:#include <stdio.h>
1、[flags]部分的用法
(1)、“–”標志符號
CString strTemp1;
strTemp1.Format("|%15s|","1234567890");
//為了觀看效果直觀,需要加“參照物”,如這里的'|'
//給定15個字符長度,但這里只有10個字符,不夠15個;
//由於沒有加“–”,則缺省為右邊對齊,左邊補空格。
MessageBox(strTemp1); //結果為| 1234567890|
strTemp1.Format("|%-15s|","1234567890");
//給定15個字符長度,但這里只有10個字符,不夠15個,則左對齊,右邊補空格。
MessageBox(strTemp1); //結果為|1234567890
(2)、“+”標志符號
CString strTemp2;
strTemp2.Format("%+d",200);
MessageBox(strTemp2); //結果為+200
strTemp2.Format("%+d",-200);
MessageBox(strTemp2); //結果為-200
strTemp2.Format("%d",-200); //對於負數,可以不加“+”
MessageBox(strTemp2); //結果為-200
(3)、“0”標志符號
CString strTemp3;
strTemp3.Format("%03d",4);
//當前字符串的寬度為3
MessageBox(strTemp3); //結果為004
strTemp3.Format("%-03d",4);
//使用“-”標志后,“0”標志符號功能將被忽略
MessageBox(strTemp3); //結果為4
strTemp3.Format("%03.d",4);
//使用整數格式指定的(i、u、x、X、o、d),並且還提供了精度規范(例如,%04.d),
//“0”標志符號功能也不在起作用。
MessageBox(strTemp3); //結果為4
(4)、空格標志符號
CString strTemp4;
strTemp4.Format("%d",500);//正號用空格替代
MessageBox(strTemp2); //結果為500
(5)、“#”標志符號
CString strTemp5;
strTemp5.Format("%#o",35);
//輸出一個八進制數,將十進制數轉換為八進制數
MessageBox(strTemp5); //結果為043,前綴為零而非字母o
strTemp5.Format("%#x",35);
//輸出一個十六進制數,將十進制數轉換為十六進制數
MessageBox(strTemp5); //結果為0x23
strTemp5.Format("%#X",35); //前綴0x與0X都表示十六進制數
//輸出一個十六進制數,將十進制數轉換為十六進制數
MessageBox(strTemp5); //結果為0X23
strTemp5.Format("%#x",0x35);
//輸出一個16進制數,這里0x35本身就是十六進制數
MessageBox(strTemp5); //結果為0x35
(6)、“*”標志符號
CString strTemp6;
strTemp6.Format("%*d,%d",12,28,89);
//首先,“*”要占一個變量的位置,不過它被忽略;
//因此,從“*”的下一個位置開始取值;
//這里“*”對應的數是28,但被忽略;后邊還有兩個%d,則分別對應28和89
MessageBox(strTemp6); //結果為28,89
//strTemp6.Format("%*f,%f",28.45,67.26,83.02);
//float類型不支持“*”功能
//MessageBox(strTemp6);
2、[width]部分的介紹
CString strTemp;
strTemp.Format("%d",4);
//不指定寬度,輸出本身。
MessageBox(strTemp); //結果為4
strTemp.Format("%2d",4);
//指定寬度,但不指定前綴“0”,也輸出本身。
MessageBox(strTemp); //結果為4
strTemp.Format("%02d:%02d:%02d:%03d",4,52,6,21);
//即指定了寬度,又指定了指定前綴“0”,才右意義。
MessageBox(strTemp); //結果為04:52:06:021
//其中小時部分“4”,不足兩位,則補齊為“04”;
//其中分鍾部分“52”,本身就是兩位,則不變。
strTemp.Format("%-03d",4);
//使用“-”標志后,“0”標志符號功能將被忽略
MessageBox(strTemp); //結果為4
strTemp.Format("%8s","abcdefghijk");
//這里指定輸出值的字符數為8,而實際給定的字符數為11;
//這種輸出值中的字符數大於指定的寬度,輸出值不會被截斷。
MessageBox(strTemp); //結果為abcdefghijk,而不是abcdefgh
strTemp.Format("|%15s|","1234567890"); //未使用“–”標志符號
//為了觀看效果直觀,需要加“參照物”,如這里的'|'
//給定15個字符長度,但這里只有10個字符,不夠15個;
//由於沒有加“–”,則缺省為右邊對齊,左邊補空格。
MessageBox(strTemp); //結果為| 1234567890|
strTemp.Format("|%-15s|","1234567890"); //使用了“–”標志符號
//給定15個字符長度,但這里只有10個字符,不夠15個,則左對齊,右邊補空格。
MessageBox(strTemp); //結果為|1234567890
3、[.precision]部分的介紹
float fVal = 45.27;
CString strTemp;
strTemp.Format("%f",fVal);
//不指定精度,缺省情況下,float類型的變量會顯示6位小數。
MessageBox(strTemp); //結果為45.270000
strTemp.Format("%.3f",fVal);
//指定輸出3位小數
MessageBox(strTemp); //結果為45.270
strTemp.Format("%.2f",fVal);
//指定輸出2位小數
MessageBox(strTemp); //結果為45.27
strTemp.Format("%.1f",fVal);
//指定輸出1位小數,最后一位小數執行四舍五入
MessageBox(strTemp); //結果為45.3
strTemp.Format("%.0d",0);
//精度指定為0,且要轉換的值為0,則結果為不輸出字符
MessageBox(strTemp); //結果為空值
strTemp.Format("%.2d",452);
//參數452的數字個數為3,精度為2
//參數中的數字個數大於精度,輸出值為參數值。
MessageBox(strTemp); //結果為452
strTemp.Format("%.5d",452);
//參數452的數字個數為3,精度為5
//參數中的數字個數小於精度,輸出值將在左側用零填充。
MessageBox(strTemp); //結果為00452
4、[{h | l | ll | I | I32 | I64}]部分的介紹
int iVal = -269;
//int的范圍為-2147483648-2147483647
unsigned int uVal = 248;
//unsigned int的范圍為0-4294967295
long long llVal = -9223372036854775808;
//long long的范圍為-9223372036854775808-9223372036854775807
__int32 __iVal32 = 678;
//__int32等效為int
__int64 __iVal64 = 9223372036854775807;
//__int64等效為long long
unsigned __int64 u__iVal64 = 18446744073709551615;
//unsigned __int64等效為unsigned long long
//unsigned __int64的范圍為0-18446744073709551615
//18446744073709551615(0xffffffffffffffff)
long double dVal = 67.34576899;
char cChar = 'D';
WCHAR wChar = 'F';
char* sStr = "abcd";
WCHAR* wStr = L"efgh"; //寬字符串要以L為前綴
CString strTemp;
strTemp.Format("%d",iVal);
MessageBox(strTemp); //結果為-269
strTemp.Format("%ld",iVal);
MessageBox(strTemp); //結果為-269
strTemp.Format("%u",uVal);
MessageBox(strTemp); //結果為248
strTemp.Format("%u",iVal);//類型不一致,雖不報錯,但結果錯誤
MessageBox(strTemp); //結果為4294967027
//4294967027 = 4294967295+(-269)
strTemp.Format("%lld",llVal); //ll對應long long類型
MessageBox(strTemp); //結果為-9223372036854775808
strTemp.Format("%I32d",__iVal32); //I32對應int類型
MessageBox(strTemp); //結果為678
strTemp.Format("%I64d",__iVal64); //I64對應long long類型
MessageBox(strTemp); //結果為9223372036854775808
strTemp.Format("%I64u",u__iVal64); //I64也對應unsigned long long類型
MessageBox(strTemp); //結果為18446744073709551615
strTemp.Format("%f",dVal);
MessageBox(strTemp); //結果為67.345769
strTemp.Format("%lf",dVal);
MessageBox(strTemp); //結果為67.345769
strTemp.Format("%c",cChar); //“c”為單字節字符
MessageBox(strTemp); //結果為D
strTemp.Format("%hc",cChar); //“hc”為單字節字符
MessageBox(strTemp); //結果為D
strTemp.Format("%lc",wChar); //“lc”為寬字符
MessageBox(strTemp); //結果為F
strTemp.Format("%s",sStr); //“s”為單字節字符串
MessageBox(strTemp); //結果為abcd
strTemp.Format("%hs",sStr); //“hs”為單字節字符串
MessageBox(strTemp); //結果為abcd
strTemp.Format("%ls",wStr); //“ls”為寬字節字符串
MessageBox(strTemp); //結果為efgh
strTemp.Format("%wc",wChar); //“wc”為寬字符
MessageBox(strTemp); //結果為F
strTemp.Format("%ws",wStr); //“ws”為寬字節字符串
MessageBox(strTemp); //結果為efgh
5、type部分的介紹
char cChar = 'D';
WCHAR wChar = 'F';
char* sStr = "abcd";
WCHAR* wStr = L"efgh"; //寬字符串要以L為前綴
CString strTemp;
strTemp.Format("%c",cChar); //“c”為單字節字符
MessageBox(strTemp); //結果為D
strTemp.Format("%C",wChar); //“C”為寬字符
MessageBox(strTemp); //結果為F
strTemp.Format("%s",sStr); //“s”為單字節字符串
MessageBox(strTemp); //結果為abcd
strTemp.Format("%S",wStr); //“S”為寬字符串
MessageBox(strTemp); //結果為efgh
int iVal = -248;
//int的范圍為-2147483648-2147483647
unsigned int uVal = 248;
//unsigned int的范圍為0-4294967295
strTemp.Format("%d",iVal); //“d”為int
MessageBox(strTemp); //結果為-248
strTemp.Format("%u",uVal); //“u”為unsigned int
MessageBox(strTemp); //結果為248
strTemp.Format("%x",uVal); //“x”為無符號十六進制整數
MessageBox(strTemp); //結果為f8
strTemp.Format("%X",uVal); //“X”為無符號十六進制整數
MessageBox(strTemp); //結果為F8
double dVal = 41.25;
strTemp.Format("%A",dVal);
//采用P計數法,底數為2,p+5為2的5次方
MessageBox(strTemp); //結果為0X1.4A0000P+5
strTemp.Format("%a",dVal);
//采用P計數法,底數為2,p+5為2的5次方
MessageBox(strTemp); //結果為0x1.4a0000p+5
strTemp.Format("%E",dVal);
//采用科學計數法,底數為10,E+001為10的1次方
MessageBox(strTemp); //結果為4.125000E+001
strTemp.Format("%e",dVal);
//采用科學計數法,底數為10,E+001為10的1次方
MessageBox(strTemp); //結果為結果為4.125000e+001
strTemp.Format("%.3e",dVal);
//采用科學計數法,底數為10,E+001為10的1次方
MessageBox(strTemp); //結果為結果為4.125e+001
strTemp.Format("%f",dVal);
//缺省情況下,小數位保留6位
MessageBox(strTemp); //結果為結果為41.250000
strTemp.Format("%.2f",dVal);
//小數位保留兩位
MessageBox(strTemp); //結果為結果為41.25
strTemp.Format("%g",dVal);
//選取f與e輸出結果的簡潔值,41.25比4.125000e+001簡潔
MessageBox(strTemp); //結果為結果為41.25
strTemp.Format("%G",dVal);
//選取f與E輸出結果的簡潔值,41.25比4.125000E+001簡潔
MessageBox(strTemp); //結果為結果為41.25
void* pVoid = "ASD";
strTemp.Format("%p",pVoid);
//輸出變量的地址值。
MessageBox(strTemp); //結果為結果為012BE250
