标签:
标准IO库与文件IO区别:
标准IO库的函数很多都是以
f开头,如fopen、fclose
对于ASCII字符集,一个字符用一个字节表示;对于国际字符集,一个字符可以用多个字节表示。
freopen函数清除一个流的定向fwide函数设置流的定向fwide函数:设置流的定向
#include<stdio.h>
#include<wchar.h>
int fwide(FILE *fp,int mode);
fp:FILE文件对象的指针mode:流的定向模式。 mode是负数,则函数试图使指定的流为字节定向(并不保证修改成功,因为fwide并不改变已定向流的定向)mode是正数,则函数试图使指定的流为宽定向的(并不保证修改成功,因为fwide并不改变已定向流的定向)mode为0,则函数不试图设置流的定向,而直接返回该流定向的值这里并没有函数失败的情况
fwide并不改变已定向流的定向。fp是无效流,由于fwide从返回值无法得知函数执行成功还是失败。那么我们必须采用这个方法:首先在调用fwide之前清除errno。然后在fwide之后检查errno的值。通过errno来检测fwide执行成功还是失败。FILE指针:当使用fopen函数打开一个流时,它返回一个执行FILE对象的指针。该对象通常是一个结构,包含了标准IO库为管理该流所需要的所有信息,包括:
应用程序没必要检验FILE对象,只需要将FILE指针作为参数传递给每个标准IO函数。
操作系统对每个进程与定义了3个流,并且这3个流可以自动地被进程使用,他们都是定义在<stdio.h>中:
stdin,它内部的文件描述符就是STDIN_FILENOstdout,它内部的文件描述符就是STDOUT_FILENOstderr,它内部的文件描述符就是STDERR_FILENO标准IO库提供缓冲的目的是:尽量减少使用read和write调用的次数。标准IO库对每个IO流自动地进行缓冲管理,从而避免了程序员需要手动管理这一点带来的麻烦。
标准IO库提供了三种类型的缓冲:
另外:
malloc获取使用的缓冲区fflush函数冲洗一个流。ISO C 要求下来缓冲特征:
很多操作系统默认使用下列类型的缓冲:
stderr时不带缓冲的stdin和输出stdout:若是指向终端设备的流,则是行缓冲的;否则是全缓冲的setbuf/setvbuf函数:设置流的缓冲类型
#include<stdio.h>
void setbuf(FILE *restrict fp,char *restrict buf);
int setvbuf(FILE *restrict fp,char* restrict buf,int mode,size_t size);
参数:
fp:被打开的文件对象的指针buf:一个缓冲区的指针。缓冲区长度必须为BUFSIZ常量(该常量定义在<stdio.h>中)。 buf为NULL,则是关闭缓冲buf非NULL,则通常设定该流为全缓冲的。但若该流与一个设备终端相关,则设为行缓冲的对于setvbuf函数:
buf:一个缓冲区的指针。缓冲区长度为size。 buf为NULL,且mode为_IONBF:则该流为不带缓冲的。因为此时忽略buf和size参数buf为NULL,且mode不是_IONBF:则标准IO库将自动为该流分片合适长度的缓冲区(即BUFSIZE长度),然后设定该流为指定的modemode:指定缓冲类型。可以为: _IOFBF:全缓冲。_IOLBF:行缓冲_IONBF:不带缓冲。此时忽略buf和size参数size:缓冲的长度注意:
sizefflush函数:手动冲洗一个流
#include<stdio.h>
int fflush(FILE *fp);
参数:
fp:被打开的文件对象的指针返回值:
EOF (并不是-1)该函数会使得该流所有未写的数据都被传送至内核。当fp为NULL时,此函数将导致所有输出流被冲洗。
- 冲洗是双向的:输出流 —> 内核 —> 磁盘或者终端; 输入流—> 用户缓冲区
- 冲洗并不是立即写到磁盘文件中。冲洗只是负责数据传到内核
fopen/freopen/fdopen函数:打开标准IO流
#include<stdio.h>
FILE *fopen(const char*restrict pathname,const char*restrict type);
FILE *freopen(const char*restrict pathname,const char*restrict type, FILE *restrict fp);
FILE *fdopen(int fd,const char*type);
参数:
type:指定对该IO流的读写方式: "r"或者"rb":为读打开"w"或者"wb":写打开。若文件存在则把文件截断为0长;若文件不存在则创建然后写"a"或者"ab":追加写打开;若文件存在每次都定位到文件末尾;若文件不存在则创建然后写"r+"或者"r+b"或者"rb+":为读和写打开"w+"或者"w+b"或者"wb+":若文件存在则文件截断为0然后读写;若文件不存在则创建然后读写"a+"或者"a+b"或者"ab+":若文件存在则每次都定位到文件末尾然后读写;若文件不存在则创建然后读写
- 其中
b用于区分二进制文件和文本文件。但是由于UNIX内核并不区分这两种文件,所以在UNIX环境中指定b并没有什么卵用- 创建文件时,无法指定文件访问权限位。POSIX默认要求为:
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
fopen函数: pathname:待打开文件的路径名 freopen函数:pathname:待打开文件的路径名fp:在指定的流上打开文件。若fp已经打开,则先关闭该流;若fp已经定向,则清除该定向。fdopen函数: fd:打开文件的文件描述符fopen,type意义稍微有点区别。因为该描述符已经被打开,所以fdopen为写而打开并不截断该文件。另外该文件既然被打开并返回一个文件描述符,则它一定存在。因此标准 IO追加写方式也不能创建文件返回值:
NULL这几个函数的常见用途:
fopen常用于打开一个指定的文件,返回一个文件指针freopen常用于将一个指定的文件打开为一个预定义的流(标准输入、标准输出或者标准错误)fdopen常用于将文件描述符包装成一个标准IO流。因为某些特殊类型的文件(如管道、socket文件)不能用fopen打开,必须先获取文件描述符,然后对文件描述符调用fdopen。注意:当以读和写类型打开一个文件时(type中带+号的类型),有下列限制:
fflush,fseek,fsetpos,rewind操作之一,则写操作后面不能紧跟读操作fseek,fsetpos,rewind操作之一,也没有到达文件末尾,则在读操作之后不能紧跟写操作注意:按照系统默认,流被打开时是全缓冲的。但是如果流引用的是终端设备,则安装系统默认,流被打开时是行缓冲的。
fclose:关闭一个打开的流
#include<stdio.h>
int fclose(FILE *fp);
fp:待关闭的文件指针在该文件被关闭之前:
fclose会自动冲洗缓冲中的输出数据当一个进程正常终止时(直接调用exit函数,或者从main函数返回):
示例
#include <stdio.h>
#include<string.h>
#include<errno.h>
#include<wchar.h>
void print_FILE_wide(FILE* fp)
{
errno=0;
int mode;
mode=fwide(fp,0);
if(errno!=0)
{
printf("\tprint_FILE_wide error,because %s\n",strerror(errno));
}else
{
if(mode>0) printf("\twide type is wide byte!\n");
else if(mode<0) printf("\twide type is byte!\n");
else printf("\twide type is undecided!\n");
}
}
void set_FILE_wide(FILE *fp,int mode)
{
errno=0;
fwide(fp,mode);
if(errno!=0)
{
printf("\tset_FILE_wide error,because %s\n",strerror(errno));
}else
{
printf("\tset_FILE_wide sucess\n");
}
}
void print_FILE(FILE *fp)
{
printf("\t\tfile descriptor is :%d\n",fp->_fileno);
printf("\t\tbuffer address is :0x%x\n",fp->_IO_buf_base);
printf("\t\tbuffer length is :%d\n",fp->_IO_buf_end-fp->_IO_buf_base);
printf("\t\tFILE status is :0x%x\n",fp->_flags);
}
void print_stdin_out_err()
{
printf("\t stdin wide type:");
print_FILE_wide(stdin);
printf("\t stdout wide type:");
print_FILE_wide(stdout);
printf("\t stderr wide type:");
print_FILE_wide(stderr);
printf("\tstdin FILE struct:");
print_FILE(stdin);
printf("\tstdout FILE struct:");
print_FILE(stdout);
printf("\tstderr FILE struct:");
print_FILE(stderr);
}
void set_FILE_buf(FILE *fp,int mode)
{
if(-1==setvbuf(fp,NULL,mode,0))
{
printf("setvbuf failed,because %s!\n",strerror(errno));
}else
{
printf("setvbuf sucess!\n");
}
}
int main(int argc, char *argv[])
{
FILE *fp;
printf("Test stdin,stdout,stderr:\n");
print_stdin_out_err();
printf("Test a custom FILE:\n");
fp=fopen("/home/huaxz1986/test","wb");
if(fp==NULL)
return;
printf("\t wide type is:");
print_FILE_wide(fp);
printf("\t expected set wide type to wide byte:");
set_FILE_wide(fp,1);
printf("\t wide type is:");
print_FILE_wide(fp);
printf("\t expected set wide type to byte:");
set_FILE_wide(fp,-1);
printf("\t wide type is:");
print_FILE_wide(fp);
printf("FILE struct is :");
print_FILE(fp);
printf("set_FILE_buf to unbuffered:");
set_FILE_buf(fp,_IONBF);
printf("FILE struct is :");
print_FILE(fp);
fclose(fp);
return 0;
}
可以看到:
stdin、stderr的缓冲区地址是 NULL。刚被创建的流的缓冲区地址也是NULL。stdin、stderr是未定向的。刚被创建的流的也是未定向的一旦打开了流,可以在3中不同类型的非格式化IO中选择,对流进行读、写操作:
格式化IO由
printf族函数完成
getc/fgetc/getchar函数:一次读一个字符:
#include<stdio.h>
int getc(FILE*fp);
int fgetc(FILE*fp);
int getchar(void);
fp:打开的文件对象指针EOFEOF注意:
getchar()等价于getc(stdin)。它从标准输入中读取一个字符getc和fgetc的区别在于:getc可能通过宏定义来实现,而fgetc不能实现为宏。因此: getc的参数不应该是具有副作用的表达式,因为它可能被计算多次fgetc可以得到其地址,这就允许将fgetc的地址作为参数传递。而getc不行fgetc所需的时间可能比调用getc长,因为函数调用所需时间通常比调用宏长unsigned char类型转换成了int类型。 因为需要通过返回
EOF来标记到达末尾或者出错。而EOF通常是常量 -1 。所以需要返回int
ferror/feof函数:查看是读文件出错,还是到达读文件遇到尾端
#include<stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
fp:打开的文件对象指针当读流返回EOF时,我们可能不清楚到底是遇到错误,还是读到了文件尾端。此时必须调用ferror或者feof来区别这两种情况。
clearerr函数:清除文件出错标志和文件结束标志
#include<stdio.h>
void clearerr(FILE *fp)
fp:打开的文件对象指针在大多数操作系统中,每个流在FILE对象中维护了两个标志:
调用clearerr函数可以清楚这两个标志
ungetc函数:将字符压回流中
#include<stdio.h>
int ungetc(int c,FILE *fp);
c:待压入字符转换成的整数值fp:打开的文件对象指针cEOF注意:
EOF。但是当已经读到文件尾端时,支持压入一个字符,此时ungetc会清除该流的文件结束标志ungetc通常用于这样的情形:正在读取一个输入流,然后需要根据某个字符串(标记字符串)来对输入进行切分。那么我们就需要先看一看下一个字符,来决定如何处理当前字符。此时需要方便的将刚查看的字符回送。
ungetc只是将字符压入流缓冲区中,并没有压入底层的磁盘文件或者操作系统内核中
putc/fputc/putchar函数:一次写一个字符
#include<stdio.h>
int putc(int c,FILE*fp);
int fputc(int c,FILE*fp);
int putchar(int c);
c:待写字符转换成的整数值fp:打开的文件对象指针cEOF注意:
putchar(c)等价于putc(c,stdout)。它向标准输出中写一个字符putc和fputc的区别在于:putc可能通过宏定义来实现,而fputc不能实现为宏fgets/gets函数:一次读一行字符:
#include<stdio.h>
char *fgets(char *restrict buf,int n, FILE* restrict fp);
char *gets(char *buf);
参数:
buf:存放读取到的字符的缓冲区地址对于 fgets函数:
n:缓冲区长度fp:打开的文件对象指针bufEOFEOF注意:
fgets函数,必须指定缓冲区的长度n。该函数一直读到下一个换行符为止,但是不超过n-1个字符。 null字节结尾n-1个字节,则fgets只返回一个不完整的行;下次调用fgets会继续读该行gets函数,从标准输入总读取字符。由于无法指定缓冲区的长度,因此很可能造成缓冲区溢出漏洞。故该函数不推荐使用EOFfputs/puts函数:一次写一行字符:
#include<stdio.h>
int fputs(const char* restrict str,FILE*restrict fp);
int puts(const char*str);
str:待写的字符串fp:打开的文件对象指针EOF注意:
fputs和puts都是将一个以null字节终止的字符串写到流中,末尾的null字符不写出!。字符串不要求以换行符结尾!puts将字符串写道标准输出,末尾的null字符不写出!但是puts随后又将一个换行符写到标准输出中!。而fputs不会自动添加换行符。 虽然
puts是安全的,但是我们也是要避免使用它,以免要记住它在最后是否添加了一个换行符。
fread/fwrite函数:执行二进制读写IO
#include<stdio.h>
size_t fread(void *restrict ptr,size_t size,size_t nobj,FILE *restrict fp);
size_t fwrite(const void*restrict ptr,size_t size,size_t nobj,FILE *restrict fp);
ptr:存放二进制数据对象的缓冲区地址size:单个二进制数据对象的字节数(比如一个struct的大小)nobj:二进制数据对象的数量fp:打开的文件对象指针nobj。此时应调用ferror或者feof来判断究竟是那种情况nobj,则出错使用二进制IO的基本问题是:它只能用在读取同一个操作系统上已写的数据。如果跨操作系统读写,则很可能工作异常。因为:
struct,可能在不同操作系统或者不同编译系统中,成员的偏移量不同示例:
#include <stdio.h>
#include<string.h>
#include<errno.h>
#define N 5
//################# binary io ##############
struct my_struct{
int i1;
double i2;
};
struct my_struct datas[N];
void initital_datas()
{
int i=0;
for(i=0;i<N;i++)
{
datas[i].i1=i;
datas[i].i2=0.666;
}
}
void clear_datas()
{
int i=0;
for(i=0;i<N;i++)
{
datas[i].i1=0;
datas[i].i2=0;
}
}
void print_datas()
{
printf("Data is:\n");
int i=0;
for(int i=0;i<N;i++)
{
printf("data[%d]: i1:%d, i2:%f\n",i,datas[i].i1,datas[2]);
}
printf("\n");
}
void test_fread(FILE*file)
{
size_t len;
len=fread(datas,sizeof(struct my_struct),N,file);
if(len!=N)
{
printf("\tRead binary data failed:");
test_error_eof(file);
}else
{
printf("\tRead binary data success!\n");
}
}
void test_fwrite(FILE*file)
{
size_t len;
len=fwrite(datas,sizeof(struct my_struct),N,file);
if(len!=N)
{
printf("\tWrite binary data failed,because %s!\n",strerror(errno));
}else
{
printf("\tWrite binary data success!\n");
}
}
//########## char io ################
void test_error_eof(FILE*file)
{
if(ferror(file)) printf("\tRead file error,because %s\n",strerror(errno));
else if(feof(file)) printf("\tAt the end of file\n");
else printf("\tUnkown error!\n");
clearerr(file);
}
void test_get_char(FILE*file)
{
int c;
if((c=fgetc(file))!=EOF)
{
printf("\tRead char<%c> success\n",c);
}else
{
printf("\tRead char failed:",c);
test_error_eof(file);
}
}
void test_put_char(int c,FILE*file)
{
int ok;
if((ok=fputc(c,file))!=EOF)
{
printf("\tWrite char<%c> sucess\n",ok);
}else
{
printf("\tWrite char<%c> error,beause %s \n",strerror(errno));
}
}
void test_ungetc(int c,FILE*file)
{
int ok;
if((ok=ungetc(c,file))!=EOF)
{
printf("\tUngetc char<%c> sucess\n",ok);
}else
{
printf("\tUngetc char<%c> failed:",ok);
test_error_eof(file);
}
}
//########### line io #############
char buffer[2048];
void print_buffer(int len)
{
int i=0;
printf("buffer:\n");
for (i=0;i<len;i++)
{
printf("\t0x%x;",buffer[i]);
}
printf("\n");
}
void test_fgets(FILE *file)
{
char* buf;
buf=fgets(buffer,2028,file);
if(buf==NULL)
{
printf("fpgets failed:");
test_error_eof(file);
}else
{
printf("fpgets success\n");
}
}
void tet_fputs(const char*str,FILE *file)
{
int len;
len=fputs(str,file);
if(len==EOF)
{
printf("fputs <%s> failed,beause %s\n",str,strerror(errno));
}else
{
printf("fputs <%s> success\n",str);
}
}
int main(int argc, char *argv[])
{
FILE *fp_text,*fp_binary;
fp_text=fopen("/home/huaxz1986/test","w+");
if(fp_text==NULL) return;
//###### test char io #############
printf("\n-----------------Test char IO ----------------------\n");
printf("Test put char:\n");
test_put_char(‘A‘,fp_text);
rewind(fp_text);
printf("Test get char:\n ");
test_get_char(fp_text);
printf("Test ungetc:\n");
test_ungetc(‘B‘,fp_text);
printf("Test get char:\n ");
test_get_char(fp_text);
printf("Test get char:\n ");
test_get_char(fp_text);
//######### Test line io ##############
printf("\n-----------------Test line IO ----------------------\n");
rewind(fp_text);
tet_fputs("abcdefghijklmnopq",fp_text);
rewind(fp_text);
test_fgets(fp_text);
print_buffer(26);
//####### Test binary io #############
printf("\n-----------------Test binary IO ----------------------\n");
fp_binary=fopen("/home/huaxz1986/test2","w+b");
if(fp_binary==NULL) return;
initital_datas();
print_datas();
printf("Test fwrite:\n");
test_fwrite(fp_binary);
rewind(fp_binary);
printf("After Clear data:\n");
clear_datas();
print_datas();
printf("Test fread:\n");
test_fread(fp_binary);
printf("After fread, the data is:\n");
print_datas();
return 0;
}
有三种方法定位标准IO流
通过 ftell/fseek函数:
#include<stdio.h>
long ftell(FILE *fp);
fp:打开的文件对象指针返回值:
若是二进制文件,则文件指示器是从文件开始位置度量的,并以字节为度量单位。ftell就是返回这种字节位置。
#include<stdio.h>
int fseek(FILE *fp,long offset,int whence);
fp:打开的文件对象指针offset:偏移量。其解释依赖于whencewhence:偏移量的解释方式: SEEK_SET常量:表示从文件的起始位置开始SEEK_CUR常量:表示从文件的当前位置开始SEEK_END常量:表示从文件的尾端开始返回值:
原书说,对文本文件和二进制文件,
fseek定位有某些限制。但是经过在ubuntu 16.04上测试,可以任意定位。并没有要求说不能定位到文件尾端,以及必须用SEEK_SET等诸多限制。
#include<stdio.h>
void rewind(FILE *fp);
参数:
fp:打开的文件对象指针rewind函数将一个流设置到文件的起始位置
通过 ftello/fseeko函数:除了偏移量类型为off_t而不是long以外,ftello/fseeko与ftell/fseek相同
#include<stdio.h>
off_t ftello(FILE *fp);
fp:打开的文件对象指针
#include<stdio.h>
int fseeko(FILE *fp,off_t offset,int whence);
fp:打开的文件对象指针offset:偏移量。其解释依赖于whencewhence:偏移量的解释方式: SEEK_SET常量:表示从文件的起始位置开始SEEK_CUR常量:表示从文件的当前位置开始SEEK_END常量:表示从文件的尾端开始fgetpos/fsetpos函数:由 ISO C 引入
#include<stdio.h>
int fgetpos(FILE *restrict fp,fpos_t *restrict pos);
int fsetpos(FILE * fp,const fpos_t * pos);
fp:打开的文件对象指针pos:存放偏移量的缓冲区示例
#include <stdio.h>
#include<string.h>
#include<errno.h>
#define N 5
//################# binary io ##############
struct my_struct{
int i1;
double i2;
char i3;
};
struct my_struct datas[N];
void test_fwrite(FILE*file)
{
size_t len;
len=fwrite(datas,sizeof(struct my_struct),N,file);
if(len!=N)
{
printf("\tWrite binary data failed,because %s!\n",strerror(errno));
clearerr(file);
}else
{
printf("\tWrite binary data success!\n");
}
}
//########## char io ################
void test_error_eof(FILE*file)
{
if(ferror(file)) printf("\tRead file error,because %s\n",strerror(errno));
else if(feof(file)) printf("\tAt the end of file\n");
else printf("\tUnkown error!\n");
clearerr(file);
}
void test_put_char(int c,FILE*file)
{
int ok;
if((ok=fputc(c,file))!=EOF)
{
printf("\tWrite char<%c> sucess\n",ok);
}else
{
printf("\tWrite char<%c> error,beause %s \n",strerror(errno));
}
}
//########### seek ###########
void test_tell(FILE *fp)
{
long ok=ftell(fp);
fpos_t pos;
if(ok==-1)
{
printf("\tftell error,because %s.",strerror(errno));
clearerr(fp);
}else
{
printf("\tftell :%d.",ok);
}
if(0==fgetpos(fp,&pos))
{
printf("\tfgetpos :%d\n",pos);
}else
{
printf("\tfgetpos error,because %s\n",strerror(errno));
clearerr(fp);
}
}
void test_seek(FILE *fp,int offset,int whence)
{
int ok;
ok=fseek(fp,offset,whence);
if(ok!=-1)
{
printf("\tfseek ok\n");
}else
{
printf("\tfseek error,because %s\n",strerror(errno));
clearerr(fp);
}
}
int main(int argc, char *argv[])
{
FILE *fp_text,*fp_binary;
//####### test text file seek ########
fp_text=fopen("/home/huaxz1986/test","w+");
if(fp_text==NULL) return;
printf("\n-----------------Test char IO ----------------------\n");
test_put_char(‘A‘,fp_text);
test_put_char(‘B‘,fp_text);
test_put_char(‘C‘,fp_text);
test_put_char(‘D‘,fp_text);
test_put_char(‘E‘,fp_text);
printf("Test tell:");
test_tell(fp_text);
printf("Test SEEK_SET:");
test_seek(fp_text,0,SEEK_SET);
printf(" current pos:");
test_tell(fp_text);
printf("Test SEEK_CUR:");
test_seek(fp_text,0,SEEK_CUR);
printf(" current pos:");
test_tell(fp_text);
printf("Test SEEK_END:");
test_seek(fp_text,0,SEEK_END);
printf(" current pos:");
test_tell(fp_text);
printf("\n-----------------Test binary IO ----------------------\n");
fp_binary=fopen("/home/huaxz1986/test2","w+b");
if(fp_binary==NULL) return;
test_fwrite(fp_binary);
printf("Test tell:");
test_tell(fp_binary);
printf("Test SEEK_SET:");
test_seek(fp_binary,0,SEEK_SET);
printf(" current pos:");
test_tell(fp_binary);
printf("Test SEEK_CUR:");
test_seek(fp_binary,0,SEEK_CUR);
printf(" current pos:");
test_tell(fp_binary);
printf("Test SEEK_END:");
test_seek(fp_binary,0,SEEK_END);
printf(" current pos:");
test_tell(fp_binary);
return 0;
}
格式化输出函数:
#include<stdio.h>
int printf(const char *restrict format,...);
int fprintf(FILE *restrict fp,const char*restrict format,...);
int dprintf(int fd,const char *restrict format,...);
int sprintf(char *restrict buf,const char*restrict format,...);
int snprintf(char *restrict buf,size_t n,const char *restrict format,...);
参数:
format,...:输出的格式化字符串对于fprintf:
fp:打开的文件对象指针。格式化输出到该文件中对于dprintf:
fd:打开文件的文件描述符。格式化输出到该文件中对于sprintf:
buf:一个缓冲区的指针。格式化输出到该缓冲区中对于snprintf:
buf:一个缓冲区的指针。格式化输出到该缓冲区中n:缓冲区的长度。格式化输出到该缓冲区中返回值:
null字节)printf将格式化输出写到标准输出;fprintf写到指定的流;dprintf写到指定的文件描述符;sprintf写到数组buf中;snprintf也是写到数组buf中,但是在该数组的尾端自动添加一个null字节(该字节不包含在返回值中)。
sprintf,因为它可能引起缓冲区溢出流动snprintf的数组缓冲区至少为s+1个字节,否则发生截断格式说明:%[flags][fldwidth][precision][lenmodifier]convtype
flags有: ‘ : 撇号,将整数按照千位分组字符- : 在字段内左对齐输出+: 总是显示带符号转换的正负号:空格。如果第一个字符不是正负号,则在其前面加一个空格#:指定另一种转换形式(如,对于十六进制格式,加 0x 前缀)0:添加前导0(而非空格) 进行填充fldwidth:说明最小字段宽度。转换后参数字符如果小于宽度,则多余字符位置用空格填充。 *precision:说明整型转换后最少输出数字位数、浮点数转换后小数点后的最少位数、字符串转换后最大字节数。 .后跟随一个可选的非负十进制数或者一个星号* 宽度和精度可以为
*,此时一个整型参数指定宽度或者精度的值。该整型参数正好位于被转换的参数之前
lenmodifier:说明参数长度。可以为: hh:将相应的参数按照signed char或者unsigned char类型输出h:将相应的参数按照signed short或者unsigned short类型输出l:将相应的参数按照signed long或者unsigned long或者宽字符类型输出ll:将相应的参数按照signed longlong或者unsigned longlong类型输出j:intmax_t或者uintmax_tz:size_tt:ptrdiff_tL:long doubleconvtype:控制如何解释参数 d或者i:有符号十进制o:无符号八进制u:无符号十进制x或者X:无符号十六进制f或者F:双精度浮点数e或者E:指数格式双精度浮点数g或者G:根据转换后的值解释为f、F、e、Ea或者A:十六进制指数格式双精度浮点数c:字符(若带上长度修饰符l,则为宽字符)s:字符串(若带上长度修饰符l,则为宽字符)p:指向void的指针n:到目前位置,此printf调用输出的字符的数目将被写入到指针所指向的带符号整型中%:一个%字符C:宽字符,等效于lcS:宽字符串,等效于lsprintf族的变体:将可变参数(...)替换成了va_list arg:
#include<stdarg.h>
#include<stdio.h>
int vprintf(const char *restrict format,va_list arg);
int vfprintf(FILE *restrict fp,const char*restrict format,va_list arg);
int vdprintf(int fd,const char *restrict format,va_list arg);
int vsprintf(char *restrict buf,const char*restrict format,va_list arg);
int vsnprintf(char *restrict buf,size_t n,const char *restrict format,va_list arg);
其参数与返回值与前面的printf族完全相同
格式化输入函数:
#include<stdio.h>
int scanf(const char*restrict format,...);
int fscanf(FILE *restrict fp,const char *restrict format,...);
int sscanf(const char *restrict buf,const char *restrict format,...);
参数:
format,...:格式化字符串对于fscanf:
fp:打开的文件对象指针。从流中读取输入对于sscanf:
buf:一个缓冲区指针。从该缓冲区中读取输入EOFEOFscanf族用于分析输入字符串,将字符序列转换成指定类型的变量。在格式之后的各参数中包含了变量的地址,用转换结果对这些变量赋值。
%[*][fldwidth][m][lenmodifier]convtype: *:用于抑制转换。按照转换说明的其余部分对输入进行转换,但是转换结果不存放在参数中而是抛弃fldwidth:说明最大宽度,即最大字符数lenmodifier:说明要转换结果赋值的参数大小。见前述说明convtype:类似前述说明。但是稍有区别:输入中的带符号的数值可以赋给无符号类型的变量m:用于强迫内存分配。当%c,%s时,如果指定了m,则会自动分配内存来容纳转换的字符串。同时该内存的地址会赋给指针类型的变量(即要求对应的参数必须是指针的地址)。同时要求程序员负责释放该缓冲区(通过free函数)scanf族也有一类变体:将可变参数(...)替换成了va_list arg:
#include<stdarg.h>
#include<stdio.h>
int vscanf(const char*restrict format,va_list arg);
int vfscanf(FILE *restrict fp,const char *restrict format,va_list arg);
int vsscanf(const char *restrict buf,const char *restrict format,va_list arg);示例:
#include <stdio.h>
#include<string.h>
#include<errno.h>
#include<memory.h>
#define LEN 32
char buffer[LEN];
void print_buffer()
{
int i;
printf("Buffer:\n");
for(i=0;i<LEN;i++)
printf("\t0x%x",buffer[i]);
printf("\n");
}
void test_snprintf_int(int i)
{
int ok;
ok=snprintf(buffer,LEN,"%d",i);
if(ok<0)
{
printf("\tsnprintf error,because:%s\n",strerror(errno));
print_buffer();
}else
{
printf("\tsnprintf success, write %d chars\n",ok);
print_buffer();
}
}
void test_snprintf_char(char c)
{
int ok;
ok=snprintf(buffer,LEN,"%c",c);
if(ok<0)
{
printf("\tsnprintf error,because:%s\n",strerror(errno));
print_buffer();
}else
{
printf("\tsnprintf success, write %d chars\n",ok);
print_buffer();
}
}
void test_snprintf_string(const char*s)
{
int ok;
ok=snprintf(buffer,LEN,"%s",s);
if(ok<0)
{
printf("\tsnprintf error,because:%s\n",strerror(errno));
print_buffer();
}else
{
printf("\tsnprintf success, write %d chars\n",ok);
print_buffer();
}
}
void test_sscanf_int()
{
int ok;
int i;
ok=sscanf(buffer,"%d",&i);
if(ok==EOF)
{
printf("\tsscanf error,because:%s\n",strerror(errno));
}else
{
printf("\tsscanf success, read %d items:i=%08d\n",ok,i);
}
}
void test_sscanf_char()
{
int ok;
char c;
ok=sscanf(buffer,"%c",&c);
if(ok==EOF)
{
printf("\tsscanf error,because:%s\n",strerror(errno));
}else
{
printf("\tsscanf success, read %d items:c=%c\n",ok,c);
}
}
void test_sscanf_string()
{
int ok;
char *s;
ok=sscanf(buffer,"%ms",&s);
if(ok==EOF)
{
printf("\tsscanf error,because:%s\n",strerror(errno));
}else
{
printf("\tsscanf success, read %d items:string=%s\n",ok,s);
free(s);
}
}
int main(int argc, char *argv[])
{
print_buffer();
test_snprintf_int(99);
test_sscanf_int();
test_snprintf_char(‘A‘);
test_sscanf_char();
test_snprintf_string("abcdefghijklmn");
test_sscanf_string();
return 0;
}
fileno函数:获取文件对象的文件描述符
#include<stdio.h>
int fileno(FILE *fp);
fp:打开的文件对象的指针tmpnam/tmpfile函数:创建临时文件
#include<stdio.h>
char *tmpnam(char *ptr);
FILE *tmpfile(void);
tmpnam参数: ptr:指向存放临时文件名的缓冲区的指针 NULL,则产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回 下次再调用
tmpnam时,会重写该静态区
NULL,则认为它指向长度至少为L_tmpnam个字符的数组,产生的路径名存放在该缓冲区中,返回ptr。L_tmpnam常量定义在<stdio.h>头文件中tmpnam返回值:返回指向唯一路径名的指针tmpfile返回值: NULLtmpnam函数产生一个与现有文件名不同的有效路径名字符串。每次调用它时,都产生一个不同路径名。最多调用次数是TMP_MAX次(定义在<stdio.h>中)
它只创建独一无二的文件名,但是并不创建临时文件
tmpfile是创建一个临时二进制文件(类型wb+),在关闭该文件或者程序结束时将自动删除这种文件
UNIX对二进制文件、文本文件并不进行特殊区分
mkdtemp/mkstemp函数:创建临时文件(由SUS 标准给出)
#include<stdlib.h>
char *mkdtemp(char *template);
int mkstemp(char *template);
template:一个字符串。这个字符是最末6个字符设置为XXXXXX的路径名。函数将这些占位符替代成不同的字符来构建一个唯一的路径名。若成功的话,这两个函数将修改template字符串来反映临时文件的名字 因为函数会修改
template,因此一定不能用常量字符串来赋值!
mkdtemp返回值: NULLmkstemp返回值: mkdtemp函数创建了一个目录,该目录有一个唯一的名字;mkstemp函数创建了一个文件,该文件有一个唯一的名字。名字是通过template字符串进程构建的。
mkdtemp函数创建的目录具有权限位集: S_IRUSR|S_IWUSR|S_IXUSR。调用进程的文件模式创建屏蔽字可以进一步限制这些权限mkstemp函数返回的文件描述符以读写方式打开。它创建的文件用访问权限位:S_IRUSR|S_IWUSRmkstemp创建的临时文件并不会自动删除示例:
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
char buffer[L_tmpnam];
void print_buffer()
{
int i;
printf("\tBuffer:\n");
for(i=0;i<L_tmpnam;i++)
printf("\t0x%x",buffer[i]);
printf("\n");
}
void test_tmpname()
{
char *p;
p=tmpnam(buffer);
if(NULL==p)
{
printf("\ttmpnam failed,because of %s\n",strerror(errno));
}else
{
printf("\ttmpnam success,file name is: %s\n",buffer);
}
}
void test_tmpfile()
{
FILE *fp;
fp=tmpfile();
if(fp==NULL)
{
printf("\ttmpfile failed,because of %s\n",strerror(errno));
}else
{
printf("\ttmpfile success,file descriptor is: %d\n",fp->_fileno);
}
}
void test_mkdtemp()
{
char * name;
char old_name[128]="/home/huaxz1986/test/abc123XXXXXX";
name=mkdtemp(old_name);
if(NULL==name)
{
printf("\ttest_mkdtemp failed,because of %s\n",strerror(errno));
}else
{
printf("\ttest_mkdtemp success,the new name is %s\n",name);
}
}
void test_mkstemp()
{
int ok;
char old_name[128]="/home/huaxz1986/test/abc123XXXXXX";
ok=mkstemp(old_name);
if(-1==ok)
{
printf("\ttest_mkstemp failed,because of %s\n",strerror(errno));
}else
{
printf("\ttest_mkstemp success, file descriptor is: %d\n",ok);
}
}
int main(int argc, char *argv[])
{
print_buffer();
printf("Test tmpname:\n");
test_tmpname();
print_buffer();
printf("Test tmpfile:\n");
test_tmpfile();
printf("Test mkdtemp:\n");
test_mkdtemp();
printf("Test mkstemp:\n");
test_mkstemp();
return 0;
}
内存流:一种标准IO流,虽然它通过 FILE指针来访问,但是并没有底层的文件 。所有的IO都是通过在缓冲区和主存之间来回传送字节来完成。
虽然它看起来像是文件流,但是更适用于字符串操作
创建内存流:
#include<stdio.h>
FILE *fmemopen(void *restrict buf,size_t size,const char *restrict type);
buf:内存流缓冲区的起始地址size:内存流缓冲区的大小(字节数) buf为NULL时,则函数负责分配size字节的缓冲区,并在流关闭时自动释放分配的缓冲区type:控制如何使用流(即打开内存流的方式): r或者rb:读打开w或者wb:写打开a或者ab:追加打开;为在第一个null字节处写打开r+或者r+b或rb+:读写打开w+或者w+b或wb+:把文件截断为0,然后读写打开a+或者a+b或ab+:追加;为在第一个null字节处读写打开NULL注意:
a方式打开内存流时,当前文件位置设为缓冲区中第一个null字节处。 null字节,则当前位置设为缓冲结尾的后一个字节a方式打开时,当前位置设置为缓冲区的开始位置buf是null,则打开流进行读或者写都没有任何意义。因为此时缓冲区是通过fmemopen分配的,没办法找到缓冲区的地址。fclose、fflush、fseek、fseeko、fsetpos时都会在当前位置写入一个null字节创建内存流的其他两个函数:
#include<stdio.h>
FILE *open_memstream(char **bufp,size_t *sizep);
#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp,size_t *sizep);
bufp:指向缓冲区地址的指针(用于返回缓冲区地址)sizep:指向缓冲区大小的指针(用于返回缓冲区大小)NULL这两个函数创建的流:
在缓冲区地址和大小使用上要遵守规则:
fclose或者fflush后才有效fclose之前才有效。因为缓冲区可能增长,也可能需要重新分配示例:
#include <stdio.h>
#include<string.h>
#include<errno.h>
#define LEN 32
char buffer[LEN];
void print_buffer()
{
int i;
printf("\tBuffer:\n");
for(i=0;i<LEN;i++)
printf("\t0x%x",buffer[i]);
printf("\n");
}
FILE * test_fmemopen()
{
FILE *fp;
fp=fmemopen(buffer,LEN,"r+");
if(NULL==fp)
{
printf("\tfmemopen failed,because:%s\n",strerror(errno));
return NULL;
}
else
{
printf("\tfmemopen success\n");
return fp;
}
}
int main(int argc, char *argv[])
{
FILE *fp;
char read_buffer[LEN];
print_buffer();
fp=test_fmemopen();
if(fp==NULL) return;
printf("Write to mem FILE\n");
fputs("abcedfghijklmn",fp);
fflush(fp);# 必须冲洗才能在 buffer 中体现出来
print_buffer();
printf("Seek to begin\n");
fseek(fp,0,SEEK_SET);
printf("Write to mem FILE\n");
fputs("ABCDEFG",fp);
fflush(fp);
print_buffer();
printf("Read from mem FILE\n");
fgets(read_buffer,LEN,fp);
printf("\tread:%s\n",read_buffer);
return 0;
}
标准IO库的缺点:效率不高。这与它需要复制的数据量有关。当使用每次一行的函数fgets/fputs时,通常需要复制两次数据:
read/write时)标签:
原文地址:http://blog.csdn.net/taoyanqi8932/article/details/51944995