`
emowuyi
  • 浏览: 1477961 次
文章分类
社区版块
存档分类
最新评论

自己动手写C语言格式化输出函数(一)

 
阅读更多

printf系列函数,包括fprintf、sprintf函数等,其功能是将C语言的所有基本数据类型按用户要求进行格式化输出。

printf函数几乎是所有学习C语言的人接触到的第一个函数,是C语言标准中使用频率最高的函数。

printf函数是C语言标准函数中最著名的可变参数函数,看见printf这个函数名,就想起了C语言的说法一点也不过分,因此,可以说是C语言标准函数中的最具标志性的函数。

printf系列函数。在DOS环境下,这一系列输出函数涵盖了PC机所能用到的所有输出设备,所以printf系列函数也是C语言中最复杂的函数。

当然,随着DOS时代的结束,不仅printf系列函数的作用减弱了,就连C语言本身也被压缩到了最小的应用领域。

本文写的sprintfA函数,也是应一个小友要求写的几个函数之一,包括我昨天发布的《自己动手写C语言浮点数转换字符串函数》中的FloatToStr函数,是用来学习用的。之所以取名为sprintfA,不仅是区别系统本身的sprintf函数,同时也因为在Windows下,A表示的是传统的ANSI函数。因为在Windows下,printf系列函数也“与时俱进”了,如wprintf等就是在宽字符环境下的输出函数。由于我在sprintfA函数中使用了Windows的宽字符转换函数,因此该函数只适用于Windows环境。

由于sprintfA函数代码比较长,将分为多篇文章发布,《自己动手写C语言浮点数转换字符串函数》一文中的代码也应算作一篇:

一、数据定义:


FormatRec是一个数据格式化结构,它包含了sprintfA格式化各种数据所需的基本信息。

TYPE_XXXX是数据类型标记,对应于FormatRec.type字段

TS_XXXX是各种数据类型在sprintfA可变参数传递时所占的栈字节长度。除指针类型和INT类型长度直接用sizeof关键字确定栈字节长度外,其它数据类型所占栈长度则用TypeSize宏配合计算取得,这样就使得这些数据所占栈字节长度在各种环境下都是正确的,比如字符型长度为1字节,TypeSizesizeof(CHAR)),在32位编译环境时等于4,在64位编译环境时则等于8。

对于带任意类型可变参数的函数来说,实参数据类型的栈字节长度正确与否,完全取决于程序员。比如在sprintfA的格式参数中定义了%Ld,应该是个64位整数类型,而在对应的可变参数部分却给了一个int类型,在32位编译器环境下,就存在2个错误,一是数据类型不正确,二是栈字节长度不匹配,64位整数长度为8字节,而INT的长度却只有4字节,其结果就是这个数据以及其后的所有数据都会出现错误的显示结果,甚至还有可能造成程序崩溃。这也是一些C语言初学者在使用printf系列函数时最容易犯的错误,他们混淆了一般函数与带可变参数函数调用的区别,对于一般的C函数,形参的数据类型是固定的,在调用时,如果实参与形参数据类型不匹配,编译器视情况会作出错误、警告或者转换等处理,而对于不同精度的相同数据类型,编译器大都会自动进行扩展或截断;而调用带可变参数函数时,由于函数原型的形参说明部分为“...”,编译器就没法将int扩展为_int64了。

另外,还有有关浮点数部分的数据定义在《自己动手写C语言浮点数转换字符串函数》。

二、函数主体。


sprintfA函数的主体部分就是一个简单的解释器,通过一个主循环,对字符串参数format逐字符的作如下解析:

1)如果不是数据格式前缀字符'%',直接拷贝到输出缓冲区buffer;

2)如果'%'后接着一个'%'字符,则表示要输出后面这个'%';

3)紧接着'%'后面的,应该是数据格式前导字符。共有4个前导字符:

1、'0':前导零标志。如果数据被格式化后的长度小于规定的格式化宽度,则在被格式化后的数据前补0;

2、'-':左对齐标记。

3、'+':正数符号输出标记。正数在正常格式输出时,其符号是省略了的,'+'则表示要输出这个符号;

4、'#':对浮点数,这是强制小数点('.')输出标记。无论这个数有没有小数部分,都必须输出这个小数位符号;对整数的十六进制输出,则是十六进制前缀(0x或者0X)输出标记。

前导字符不是必须的,也可有多个前导符同时出现在'%'后面,但'0'必须排在最后一个,其余顺序可任意。

4)解析数据输出宽度和精度。宽度是指数据输出时必须达到的字节数,如果格式化后的数据长度小于宽度,应用空格或者零补齐;精度则是数据要求格式化的长度,视数据类型不同而有所区别,如浮点数是指小数部分的长度,而其它数据则是指全部数据格式化长度,大于精度的数据是保留还是截断,小于精度是忽略还是补齐(零或空格),后面涉及具体数据类型时再说明。

宽度和精度一般以'.'为分隔符,左边是宽度,右边是精度,如果只有宽度则'.'可忽略。宽度和精度可用固定数字表示,如“10.6”,也可用可变形式“*.*”表示。可变形式的宽度和精度必须在sprintf的可变参数部分有其对应的整数实参。

宽度和精度部分也不是必须的。

5)分析数据类型精度字符。在C语言中,相同类型的基本数据可能有不同的精度,如整数有长短之分,浮点数有精度之分,而字符有ANSI和UNICODE之分等等。在sprintfA中,是靠分析类型精度字符来取得的。字符'l'和'h'分别表示长数据和短数据,在16位编译器环境下,一个'l'或'h'就够了,而32位及以上编译器中,随着数据精度的提高,必须靠多个类型精度字符才能表示完整,为此,也可用字符'L'和'H'分别表示数据类型的最大精度和最小精度。sprintfA的数据类型精度分析有较高的容错处理,你可以输入任意多个类型精度字符。

类型精度字符也不是必须的,缺省情况下,按一般类型精度处理。

6)解析数据类型字符。数据类型字符的作用有2个,一是确定将要输出的数据类型,如x是整型数,e是浮点数等;二是确定要输出的形式,x是以小写十六进制输出整型数,e则是以指数形式输出浮点数。

数据类型字符是必须的。数据类型字符解析完毕,各种信息写入FormatRec结构,接着就是具体的各种数据的格式化过程了,其代码将在后面给出。

7)错误处理。如果在'%'字符后,出现上述各种字符以外的字符,或者上述各种字符排列顺序错误,就需要进行错误处理。printf系列函数的错误处理在不同的编译器中的处理方式是不一样的,主要有2种处理方式:一是忽略本次数据分析,format指针退回到'%'之后,继续循环('%'后的字符作一般字符处理);二是不再作分析,直接将'%'后的所有字符输出到buffer后退出函数。本文sprintfA函数采用了后一种处理方式,前一种处理方式在函数主体中也能找到,就是被注释了的语句。

如果没有错误,则回到1),继续下一数据分析。

未完待续......

声明:本文代码主要供学习使用,如作其它用途,出问题慨不负责。

水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

分享到:
评论

相关推荐

    C语言格式化读写函数

    C语言格式化读写函数,与另一个资源相关连,可同时下载看看。

    C语言格式化输出详细说明

    C语言格式化输出详细说明,介绍了printf 一类函数格式化输出的格式控制说明。

    C语言格式化读取函数

    格式化读取函数。C语言。 自定义格式。

    C语言格式化输入输出函数详解

    一:格式输出函数printf() 1、调用形式一般为:printf(“格式化控制字符串”,输出表列); 2、格式化控制字符串用于指定输出格式,它有三种形式: 1、格式说明符:规定了相应输出表列内容的输出格式,以%打头,如%...

    C语言输出格式总结.

    printf(格式控制,输出表列) 例如:printf("i=%d,ch=%c\n",i,ch); 说明: (1)“格式控制”是用双撇号括起来的字符串,也称“转换控制字符串”,它包括两种信息: ①格式说明:由“%”和格式字符组成,它的作用...

    C语言IO--printf右对齐补零

    C语言IO--printf右对齐补零,包括标准输入输出函数、格式化输入输出函数、格式化规定符、一些特殊规定字符等

    详解C语言中printf输出的相关函数

    C语言printf()函数:格式化输出函数 printf()函数是最常用的格式化输出函数,其原型为: int printf( char * format, ... ); printf()会根据参数 format 字符串来转换并格式化数据,然后将结果输出到标准输出...

    深入分析C语言中格式化输入函数scanf的使用.pdf

    深入分析C语言中格式化输入函数scanf的使用.pdf

    C语言 格式化读写文件详解

    fscanf() 和 fprintf() 函数与前面使用的 scanf() 和 printf() 功能相似,都是格式化读写函数,两者的区别在于 fscanf() 和 fprintf() 的读写对象不是键盘和显示器,而是磁盘文件。 这两个函数的原型为: int ...

    计算机专题20100311期——C语言格式化IO详解

    函数族 用途 可用于所有的流 只用于stdin和stdout 内存中的字符串 scanf 格式化输入 fscanf scanf sscanf printf 格式化输出 fprintf printf sprintf

    C语言入门基础,适用于初学者

    3.3 C语言格式化输出函数printf实例详解 15 3.4 C语言格式化输入函数scanf实例详解 17 4.1 C语言赋值运算符实例讲解 19 4.2 C语言算数运算符实例讲解 21 4.3 C语言sizeof运算符 23 4.4 C语言逻辑运算符实例讲解 23 ...

    C语言源代码格式化 完工 V1.04 20120226 1946.7z

    C语言源代码格式化。 处理文件夹。 PLT2KMLX14解决最后一行的问题 20110721 0931.7z WPT2KMLx4解决空行末行 20110721 0948.7z WPTPLT2KMLx12PLTWPT初步 20110721 1006.7z OZI格式的轨迹文件转KML格式的轨迹文件...

    C语言常用调试用函数和宏定义头文件,便捷打印参数信息、输出日志

    C语言常用的调试用函数,例如打印参数名和数值、记录日志文件、格式化打印一维和二维数组、二进制信息等。头文件包含:1、常用数据类型别名;2、重定向printf到日志文件,打印信息可选包含行号,便于调试信息追溯;3...

    C语言全套视频教程 视频.txt

    7) C语言之格式化输出 8) C语言之格式化输入 9) C语言之格式化输入输出整理 10) C语言之基本运算符 11) C语言之其他运算符 12) C语言之表达式和语句 循环语句 13) C语言之while循环语句 14) C语言之do-while循环...

    C语言参数个数可变函数详解

    *printf()/*scanf()系列函数,用于输入输出时格式化字符串;exec*()系列函数,用于在程序中执行外部文件(main(int argc,char*argv[])算不算呢,与其说main()也是一个可变参数函数,倒不如说它是exec*()经过封装后的...

    格式化输出

    C语言中关于某些特殊符号格式化的输出,打印函数的参数

    自学去c语言视频教程

    7) C语言之格式化输出 8) C语言之格式化输入 9) C语言之格式化输入输出整理 10) C语言之基本运算符 11) C语言之其他运算符 12) C语言之表达式和语句 循环语句 13) C语言之while循环语句 14) C语言之do-while循环...

Global site tag (gtag.js) - Google Analytics