格式化I/O函数

来源:互联网 发布:淘宝3d试衣技术 编辑:程序博客网 时间:2024/04/26 22:50
 

格式化I/O函数分为输出函数和输入函数两大类,输入和输出格式是编程应该掌握的细节,同时也是编程时经常需要使用到的知识。

1.1 格式化输出函数

1.1.1 输出函数原型

格式化I/O输出函数原型如下:

#include <stdio.h>

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

int fprintf(FILE *stream, const char *format, ...);

int sprintf(char *str, const char *format, ...);

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

 

#include <stdarg.h>

int vprintf(const char *format, va_list ap);

int vfprintf(FILE *stream, const char *format, va_list ap);

int vsprintf(char *str, const char *format, va_list ap);

int vsnprintf(char *str, size_t size, const char *format, va_list ap);

具体说明如下:

返回值:上述函数成功返回格式化输出的字节数(不包括字符串的结尾'\0'),出错返回一个负值,错误原因存在于error中。

printf函数会把格式化串打印到标准输出。

fprintf函数会把格式化串输出到指定的文件stream中。

sprintf函数会把格式化串输出到缓冲区str中,并在末尾加'\0',当str空间不够时,会造成缓冲区溢出。

snprintf函数会把格式化串输出到缓冲区str中,并在末尾加'\0',但格式化串长度超过size-1字节时,对格式化串按长度size-1进行截断,因此snprintf函数比sprintf函数使用起来更加安全。

上面列出的后四个函数在前四个函数名的前面多了个v,表示可变参数,不是以...的形式传进来,而是以va_list类型传进来。

1.1.2 输出函数格式说明

1.   format格式说明

%(flags)(width)(.prec)type

以上圆括号括起来的参数为选择性参数,而%与type则是必要的。下面是format各参数的详细说明。

                    type选项说明

 

 

选项

说明

整数

d

整数的参数会被转成一有符号的十进制数字

u

整数的参数会被转成一无符号的十进制数字

o

整数的参数会被转成一无符号的八进制数字

x

整数的参数会被转成一无符号的十六进制数字,并以小写abcdef表示

X

整数的参数会被转成一无符号的十六进制数字,并以大写ABCDEF表示浮点型数

f

double 型的参数会被转成十进制数字,并取到小数点以下六位,四舍五入

e

double型的参数以指数形式打印,有一个数字会在小数点前,六位数字在小数点后,而在指数部分会以小写的e来表示

E

与%e作用相同,唯一区别是指数部分将以大写的E 来表示

g

double 型的参数会自动选择以%f 或%e 的格式来打印,其标准是根据欲打印的数值及所设置的有效位数来决定

G

与%g 作用相同,唯一区别在以指数形态打印时会选择%E 格式

字符串

c

整型数的参数会被转成unsigned char型打印出

s

指向字符串的参数会被逐字输出,直到出现NULL字符为止

p

如果是参数是“void *”型指针则使用十六进制格式显示

 

                    pre选项说明

 

 

①   正整数的最小位数

②   在浮点型数中代表小数位数

③   在%g格式代表有效位数的最大值

④   在%s格式代表字符串的最大长度

⑤   若为×符号则代表下个参数值为最大长度

 

                    width选项说明

 

 

width为参数的最小长度,若此栏并非数值,而是*符号,则表示以下一个参数当做参数长度

 

                    flag选项说明

 

 

选项

说明

#

此旗标会根据其后转换字符的不同而有不同含义。当在类型为o之前(如%#o),则会在打印八进制数值前多印一个o。而在类型为x 之前(%#x)则会在打印十六进制数前多印’0x’,在型态为e、E、f、g或G 之前则会强迫数值打印小数点。在类型为g 或G之前时则同时保留小数点及小数位数末尾的零

u

一般在打印负数时,printf()会加印一个负号,整数则不加任何负号。此旗标会使得在打印正数前多一个正号(+)

o

整数的参数会被转成一无符号的八进制数字

0

当有指定参数时,无数字的参数将补上0。默认是关闭此标记,所以一般会打印出空白字符

-

格式化后的内容居左,右边可以留空格

2.   输出字符串中字符类型说明

参数format字符串可包含下列3种字符类型:

①  一般文本,伴随直接输出。

②  ASCII控制字符,如\t、\n等。

③  格式转换字符。

格式转换为一个百分比符号(%)及其后的格式字符所组成。一般而言,每个%符号在其后都必需有一参数与之相呼应(只有当%%转换字符出现时会直接输出%字符)。

3.   常用格式化输出说明

格式化输出常用格式为:%(+|-|0)m.n。

对格式化输出常用格式解释如下:

①    m:输出数据域宽,数据长度<m,左补空格,否则按实际输出。

②    .n:对实数,指定小数点后位数(四舍五入);对字符串,指定字符串实际输出位数,超过指定长度则进行截断。

③    -:输出数据在域内左对齐(默认右对齐)。

④    +:指定在有符号数的正数前显示正号(+)。

⑤    0:输出数值时指定左边不使用的空位置自动填0。

⑥    输出百分号需要用两个%%。

4.   常用格式化输出使用举例

   在实际应用编程中,常用的格式化输出有下面6种。

①    数字前补0:sprintf(acStr,"%06dSECCTL ",30 )。

②    左对齐:sprintf( acStr,"%-6.6s","05023")。

③    右对齐:sprintf( acStr,"%6.6s","05023")。

④    两位小数点输入:sprintf(acStr,"%.2f",19.79)。

⑤    增加输出百分号:sprintf(acStr,"%%.2f",19.79)。

⑥    指定最长输出位数:sprintf(acStr,"%6.6s","testTEST")。

5.   输出函数应用举例

printf.c代码如下:

#include <stdio.h>

int main()

{

    int i = 150;

    int j = -100;

    double k = 3.14159;

    printf("%d %f %x\n",j,k,i);

    printf("%010d|%-6d|%4d|%*d\n",i,i,i,2,i); /*参数2 会代入格式*中,而与%2d同意义*/

    return 0 ;

}

编译 gcc printf.c –o printf。

执行 ./printf,执行结果如下:

-100 3.141590 96

0000000150|150   | 150|150

 

变参使用范例,了解即可。

vprintf.c代码如下:

#include <stdio.h>

#include <stdarg.h>

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

{

    va_list ap;

    int retval;

    va_start(ap,format);

    printf("my_printf( ):");

    retval = vprintf(format,ap);

    va_end(ap);

    return retval;

}

int main()

{

    int i = 150,j = -100;

    double k = 3.14159;

    my_printf("%d %f %x\n",j,k,i);

    my_printf("%2d %*d\n",i,2,i);

    return 0 ;

}

编译 gcc vprintf.c –o vprintf。

执行 ./vprintf,执行结果如下:

my_printf( ):-100 3.141590 96

my_printf( ):150 150

1.2 格式化输入函数

1.2.1 输入函数原型

格式化I/O输入函数原型如下:

#include <stdio.h>

int scanf(const char *format, ...);

int fscanf(FILE *stream, const char *format, ...);

int sscanf(const char *str, const char *format, ...);

具体说明如下:

返回值:上述函数返回成功匹配和赋值的参数个数,成功匹配的参数可能少于所提供的赋值参数,返回0表示一个都不匹配,出错返回-1并设置errno。

scanf函数会将输入的数据根据参数format字符串来转换并格式化数据。

fscanf函数从指定的文件stream中读字符。

sscanf函数从指定的字符串str中读字符。

1.2.2 输入函数格式说明

1.   format类型

%[*][size][l][h]type

以上圆括号括起来的参数为选择性参数,而%与type则是必要的。具体说明如下:

Ÿ  *代表该对应的参数数据忽略不保存。

Ÿ  size 为允许参数输入的数据长度。

Ÿ  l 输入的数据数值以long int 或double型保存。

Ÿ  h 输入的数据数值以short int 型保存。

                    type选项说明

 

 

选项

说明

d

输入的数据会被转换成一有符号的十进制数字(int)

i

输入的数据会被转换成一有符号的十进制数字,若输入数据以“0x”或“0X”开头代表转换十六进制数字,若以“0”开头则转换八进制数字,其他情况代表十进制

o

输入的数据会被转换成一无符号的八进制数字

u

输入的数据会被转换成一无符号的正整数

x

输入的数据为无符号的十六进制数字,转换后存于unsigned int型变量

X

同%x

f

输入的数据为有符号的浮点型数,转换后存于float型变量

e

同%f

E

同%f

g

同%f

s

输入数据为以空格字符为终止的字符串

c

输入数据为单一字符

[]

读取数据但只允许括号内的字符。如[a-z]

[^]

读取数据但不允许中括号的^符号后的字符出现,如[^0-9]

2.   输入字符串中字符类型说明

格式化输入字符串中字符类型包括如下3种:

①    空格或Tab,在处理过程中被忽略。

②    普通字符(不包括%),和输入字符中的非空白字符相匹配,输入字符中的空白字符是指空格、Tab、\r、\n、\v、\f。

③    格式转换是以%开头,以转换字符结尾,中间有若干个可选项。

3.   scanf函数特别说明

scanf函数使用格式为scanf("格式控制串", 地址表),输入变量默认间隔是空格,如果用“,”号做间隔,输入时也要输入“,”号。

4.   输入函数应用举例

scanf.c源代码如下:

#include <stdio.h>

int main()

{

    int i;

    unsigned int j;

    char s[5];

    scanf("%d %x %5[a-z] %*s %f",&i,&j,s,s);

    printf("%d %d %s\n",i,j,s);

    return 0 ;

}

编译 gcc scanf.c -o scanf。

执行 ./scanf,执行结果如下:

输入: 10 0x1b aaaaaaaaaa bbbbbbbbbb

10 27 aaaaa

5.   变参实用项目实例

下面是一个打印源代码文件名称、报错位置和信息的实用日志函数,具体说明如下:

程序中syslog函数打印信息长度不限,报错参数个数和信息不限,并打印该进程进程号。

日志位置存放在自定义的目录下,日志文件名称可自定义。

日志文件按天生成,打印函数自动检测日志文件是否存在,如不存在建立相应的日志文件。

变参一般都需使用va_start和va_end函数,编程模式类似,读者可模仿掌握。

syslog.c源代码如下:

#include <sys/stat.h>

#include <sys/types.h>

#include <unistd.h>

#include <stdarg.h>

#include <time.h>

#include <stdlib.h>

#include <stdio.h>

#include <fcntl.h>

#include <string.h>

/*------------------------------------------------------------------------

 * Function Name : syslog

 * Description   : 写日志

 * Input         : sysname    --  系统代号

 *               : modname    --  功能模块名

 *               : file_name  --  文件名

 *               : line_num   --  行号

 *               : format     --  消息

 * Output        :

 * Return        : success:0

 *               : fail   :-1

 *------------------------------------------------------------------------*/

int syslog( char *sysname, char *modname,char *file_name, int line_num, char *format, ...  )

{

    va_list ap ;

    struct tm *ts;

    time_t now;

    char acTimeStr[16] ;

    char filename[128] ;

    char syscmd[256] ;

    FILE *LogFileID ;

    memset( filename, 0x00, sizeof( filename ) );

    now = time(NULL);

    ts = localtime( &now );

    sprintf( filename, "%s/log/%s/%s.%02d%02d.log",

            getenv("HOME"), sysname, modname, ts->tm_mon+1, ts->tm_mday );

    LogFileID=fopen( filename, "a" );

    if ( LogFileID == NULL )

    {

        sprintf( syscmd, "/bin/mkdir -p %s/log/%s/", getenv("HOME"), sysname );

        if ( system( syscmd ) != 0 )

        {

            fprintf(stderr,"cread log folder fail [%s]", syscmd );

            return -1;

        }

        LogFileID=fopen( filename, "a" );

        if ( LogFileID == NULL )

        {

            fprintf(stderr,"open file fail [%s]", filename );

            return -1 ;

        }

    }

    sprintf( acTimeStr, "%02d-%02d %02d:%02d:%02d",

            ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);

    fprintf( LogFileID, "[%s %d %s:%d] ", acTimeStr, getpid(), file_name, line_num );

    va_start(ap, format );

    vfprintf( LogFileID, format, ap );

    va_end( ap );

    fputc( 0x0a, LogFileID );

    fflush( LogFileID );

    fclose( LogFileID );

    return 0 ;

}

int main()

{

    syslog( "newSys", "SOCK", __FILE__, __LINE__, "hello world! %s %d", "what who why where when how", 999 );

    syslog( "newSys", "SOCK", __FILE__, __LINE__, "every day is new day!");

    return 0;

}

编译 gcc syslog.c –o syslog。

执行 ./syslog,在$HOME/log/newSys下产生了SOCK.0116.log文件,文件内容如下:

[01-16 19:51:04 11921 syslog.c:66] hello world! what who why where when how 999

[01-16 19:51:04 11921 syslog.c:67] every day is new day!

 

摘录自《深入浅出Linux工具与编程》

原创粉丝点击