C语言格式化输出

一、格式化输出

格式化输出的函数有printf、sprintf和snprintf等,功能略有不同,使用方法大同小异,本章节我们先以printf为例。

对于 printf 函数,相信大家并不陌生。之所以称它为格式化输出函数,该函数的声名如下:

int printf(const char *format, ...)

大家看到printf函数的声明就会有点懵,它参数的写法与我们之前学到的函数知识不一样,printf函数是一个“可变参数函数”(即函数参数的个数是可变的),可变参数函数的知识以后再介绍,现在只要知道怎么使用就行了。

printf函数的参数的个数和类型都是可变的,每一个参数的输出格式都有对应的格式说明符与之对应,从格式串的左端第1 个格式说明符对应第 1 个输出参数,第 2 个格式说明符对应第 2 个输出参数,第 3个格式说明符对应第 3 个输出参数,以此类推。

其中,格式说明符的形式如下(方括号 [] 中的项为可选项):

%[flags][width][.prec] type

1、类型符(type)

它用以表示输出数据的类型,以下是常用类型的汇总,不常用的就不列了。

%hd、%d、%ld 以十进制、有符号的形式输出 short、int、long 类型的整数。

%hu、%u、%lu 以十进制、无符号的形式输出 short、int、long 类型的整数

%c 输出字符。

%lf 以普通方式输出double(float弃用,long doube无用)。

%e 以科学计数法输出double。

%s 输出字符串。

以上输出数据的知识在之前介绍数据类型的时候已演示过,这里就不举例了。

2、宽度(width)

它用于控制输出内容的宽度。

printf("=%12s=\n","abc");     // 输出=         abc=
printf("=%12d=\n",123);       // 输出=         123=
printf("=%12lf=\n",123.5);    // 输出=  123.500000=

3、对齐标志(flags)

flags它用于控制输出内容的对齐方式。

不填或+:输出的内容右对齐,这是缺省的方式,上一小节就是右对齐的示例。

-:输出的内容左对齐。

printf("=%-12s=\n","abc");    // 输出=abc         =
printf("=%-12d=\n",123);     // 输出=123         =
printf("=%-12f=\n",123.5);    // 输出=123.500000  =

如果输出的内容是整数或浮点数,并且对齐的方式是右对齐,可以加0填充,例如:

printf("=%012s=\n","abc");  // 输出=         abc=
printf("=%012d=\n",123);   // 输出=000000000123=
printf("=%012f=\n",123.5);  // 输出=00123.500000= 

从上面第一行代码的结果看出,输出的内容不是整数或浮点数,是字符串,不能在前面填0。

左对齐的时候,能在整数或浮点数的后面补0吗?浮点数最多可以补到6位,整数不行,为什么?您的存款能在后面补0吗?

4、精度(prec)

如果输出的内容是浮点数,它用于控制输出内容的精度,也就是说小数点后面保留多少位,后面的数四舍五入。

printf("=%12.2lf=\n",123.5);   // 输出=      123.50=
printf("=%.2lf=\n",123.5);     // 输出=123.50=
printf("=%12.2e=\n",123500000000.0);  // 输出=    1.24e+11=
printf("=%.2e=\n",123500000000.0);    // 输出=1.24e+11=

二、格式化输出到字符串

int printf(const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

功能:printf是把结果输出到屏幕,sprintf把格式化输出的内容(就是双引号里面的内容)保存到字符串str中,snprintf的n类似于strncpy中的n,意思是只获取输出结果的前n-1个字符,不是n个字符。

在之前的章节中,介绍过把字符串转换为整数和浮点数据的库函数,C语言没有提供把整数和浮点数据转换为字符串的库函数,而是采用sprintf和snprintf函数格式化输出到字符串。

示例(book98.c)

/*
 * 程序名:book98.c,此程序演示格式化输出sprintf和snprintf函数。
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>

int main()
{
   char str[301];

  // 格式化输出到str中
  sprintf(str,"%d,%c,%f,%s",10,'A',25.97,"一共输入了三个数。");
  printf("%s\n",str);

  // 格式化输出到str中,只截取前7个字符
  snprintf(str,8,"%d,%c,%f,%s",10,'A',25.97,"一共输入了三个数。");
  printf("%s\n",str);
}

运行结果

在这里插入图片描述
snprintf是获取双引号里字符串("%d,%c,%f,%s",10,‘A’,25.97,")的前n-1个字符,包括逗号
双引号的个数,所以snprintf(str,8,"%d,%c,%f,%s",10,‘A’,25.97,“一共输入了三个数。”)的结果是
10,A,25
sprintf函数是一个字节存放一字符的。25.97就花费5个字节

程序运行第二行只输出了6个字符,注意,snprintf函数在unix和windows平台下的表现略有不同,在windows平台下,第二行会输出7个字符。

三、C语言代码的多行书写

在我们之前学习的过程中,编写的程序的功能很简单,一句代码很短,但是在实际开发中,参数往往很长很多,一句代码可能会很长,需要用多行才能书写。

如果我们在一行代码的行尾放置一个反斜杠,c语言编译器会忽略行尾的换行符,而把下一行的内容也算作是本行的内容。这里反斜杠起到了续行的作用。

strcpy(str,"aaaaaaaaaa\
bbbbbbbbb);

如果在第二行插入空格那么,空格也属于输入的内容。
在这里插入图片描述

如果我们不使用反斜杠,当我们试图初始化一个跨多行的字符串时,c语言编译器可能会发出警告或错误,如下面的语句是不正确的。

strcpy(str,"aaaaaaaaaa
bbbbbbbbb);

C语言中还有字符串多行书写的方法,那就是将它写个多个字符串,C语言编译器会自动将这些字符串连接起来,如下:

 strcpy(str,"aaabbbccc");
  printf("str=%s=\n",str);   // 输出str=aaabbbccc=
 
  strcpy(str,"aaa""bbb""ccc");
  printf("str=%s=\n",str);   // 输出str=aaabbbccc=

  strcpy(str,"aaa"\
             "bbb"\
             "ccc");
  printf("str=%s=\n",str);   // 输出str=aaabbbccc=


  sprintf(str,"aaabbbccc");
  printf("str=%s=\n",str);   // 输出str=aaabbbccc=

  sprintf(str,"aaa""bbb""ccc");
  printf("str=%s=\n",str);   // 输出str=aaabbbccc=

  sprintf(str,"aaa"\
              "bbb"\
              "ccc");
  printf("str=%s=\n",str);   // 输出str=aaabbbccc=

以上每段代码的输出结果完全一样,表达式:“aaa” “bbb” “ccc” 实际上相当于"aaabbbccc"。

把字符串很长,参数很多的代码用多行书写,可以使程序代码结构更清晰,以下代码是我实际开发中用到的一句代码,这还不算长的。

在这里插入图片描述

四、课后作业

1)编写示例程序,把本章节介绍的知识点全部演示一遍,用程序演示可以加深您的理解和映象。

2)编写一个解析XML字符串的函数。

函数声明:

int  GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,char *out_Value);

in_XMLBuffer,XML格式的字符串,如下:

<name>西施</name><age>18</age><sc>火辣</sc><yz>漂亮</yz>

in_FieldName,字段的标签名。

out_Value,获取内容存放的变量的指针。

返回值,0-成功,-1-失败。

调用示例:

char strXMLBuffer[1024];
strcpy(strXMLBuffer," <name>西施</name><age>18</age><sc>火辣</sc><yz>漂亮</yz>");
char strvalue[51];
GetXMLBuffer(strXMLBuffer,"name",strvalue);   // strvalue的内容将是"西施"
GetXMLBuffer(strXMLBuffer,"age",strvalue);     // strvalue的内容将是"18"
GetXMLBuffer(strXMLBuffer,"sc",strvalue);       // strvalue的内容将是"火辣"
GetXMLBuffer(strXMLBuffer,"yz",strvalue);       // strvalue的内容将是"漂亮"
/*
 * 程序名:task.c,此程序演示XML格式字符串的生成和解析。
 * 作者:C语言技术网(www.freecplus.net) 日期:20200510
*/
#include <stdio.h>
#include <string.h>

struct st_girl
{
  char name[51];   // 姓名
  int  age;        // 年龄
  int  height;     // 身高,单位:cm
  double weight;     // 体重,单位:kg
  char sc[31];     // 身材,火辣;普通;飞机场
};

// 解析XML字符串的函数族,支持int、char *和double三种类型。
// 返回值:0-成功,-1-失败。
int GetXMLBuffer_Int(const char *in_XMLBuffer,const char *in_FieldName,int *out_Value);
int GetXMLBuffer_Str(const char *in_XMLBuffer,const char *in_FieldName,char *out_Value);
int GetXMLBuffer_Double(const char *in_XMLBuffer,const char *in_FieldName,double *out_Value);
 
int main()
{
  struct st_girl stgirl;
  memset(&stgirl,0,sizeof(struct st_girl));

  strcpy(stgirl.name,"西施");
  stgirl.age=18;
  stgirl.height=168;
  stgirl.weight=45.5;
  strcpy(stgirl.sc,"火辣");

  char str[301];
  memset(str,0,sizeof(str));

  sprintf(str,\
         "<name>%s</name><age>%d</age><height>%d</height><weight>%.2lf</weight><sc>%s</sc>",\
          stgirl.name,stgirl.age,stgirl.height,stgirl.weight,stgirl.sc);
  
  printf("=%s=\n",str);

  memset(&stgirl,0,sizeof(struct st_girl));
  GetXMLBuffer_Str(str,"name", stgirl.name);
  GetXMLBuffer_Int(str,"age" ,&stgirl.age);
  printf("name=%s,age=%d\n",stgirl.name,stgirl.age);
}

// <name>西施</name><age>18</age><height>168</height><weight>45.50</weight><sc>火辣</sc>
int GetXMLBuffer_Str(const char *in_XMLBuffer,const char *in_FieldName,char *out_Value)
{
  if (out_Value==0) return -1;  // 如果out_Value是空地址,返回失败。

  char *start=0,*end=0;
  char m_SFieldName[51],m_EFieldName[51];  // 字段的开始和结束标签。

  int m_NameLen = strlen(in_FieldName);  // 字段名长度。
  memset(m_SFieldName,0,sizeof(m_SFieldName));
  memset(m_EFieldName,0,sizeof(m_EFieldName));

  snprintf(m_SFieldName,50,"<%s>",in_FieldName);
  snprintf(m_EFieldName,50,"</%s>",in_FieldName);

  start=0; end=0;

  start = (char *)strstr(in_XMLBuffer,m_SFieldName);  // 字段开始标签的位置

  if (start != 0)
  {
    end   = (char *)strstr(start,m_EFieldName);  // 字段结束标签的位置。
  }

  if ((start==0) || (end == 0)) return -1;

  int   m_ValueLen = end - start - m_NameLen - 2;  // 字段值的长度。

  strncpy(out_Value,start+m_NameLen+2,m_ValueLen);  // 获取字段的值。

  out_Value[m_ValueLen]=0;

  return 0;
}


int GetXMLBuffer_Int(const char *in_XMLBuffer,const char *in_FieldName,int *out_Value)
{
  char strvalue[51];
  memset(strvalue,0,sizeof(strvalue));

  if (GetXMLBuffer_Str(in_XMLBuffer,in_FieldName,strvalue)!=0) return -1;

  (*out_Value)=atoi(strvalue);

  return 0;
}
  现在我描述一下我的思路
 解析XML字符串,其实就是在一个字符串中找到目标字段。在一般的字符串中我们只要知道目标字符串的地址就找到它了。那么XML的也一样。但是问题是如何找。
 你会发现 XML字符串有一个特点<>x</>是这样的形式的,
 例如<name>西施</name>,现在就是要西施解析出来,不过我们还不知道x是西施,解x是什么。
 现在我们只要把<name>,</name>分别作为起始结束标签,在<name>西施</name>这个字符串里面找到他们的地址,用strstr函数。然后将他们的地址做减法那么就可以得到x的长度,知道了<name>的地址,又知道了x的长度,地址偏移就可以得到x的值。

五、版权声明

C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。
来源:C语言技术网(www.freecplus.net)
作者:码农有道

如果这篇文章对您有帮助,请点赞支持,或在您的博客中转发我的文章,谢谢!!!
如果文章有错别字,或者内容有错误,或其他的建议和意见,请您留言指正,非常感谢!!!

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值