一、基本介绍
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