标签:
标准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_FILENO
stdout
,它内部的文件描述符就是STDOUT_FILENO
stderr
,它内部的文件描述符就是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
长度),然后设定该流为指定的mode
mode
:指定缓冲类型。可以为: _IOFBF
:全缓冲。_IOLBF
:行缓冲_IONBF
:不带缓冲。此时忽略buf
和size
参数size
:缓冲的长度注意:
size
fflush
函数:手动冲洗一个流
#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
:打开的文件对象指针EOF
EOF
注意:
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
:打开的文件对象指针c
EOF
注意:
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
:打开的文件对象指针c
EOF
注意:
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
:打开的文件对象指针buf
EOF
EOF
注意:
fgets
函数,必须指定缓冲区的长度n
。该函数一直读到下一个换行符为止,但是不超过n-1
个字符。 null
字节结尾n-1
个字节,则fgets
只返回一个不完整的行;下次调用fgets
会继续读该行gets
函数,从标准输入总读取字符。由于无法指定缓冲区的长度,因此很可能造成缓冲区溢出漏洞。故该函数不推荐使用EOF
fputs/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
:偏移量。其解释依赖于whence
whence
:偏移量的解释方式: 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
:偏移量。其解释依赖于whence
whence
:偏移量的解释方式: 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_t
z
:size_t
t
:ptrdiff_t
L
:long double
convtype
:控制如何解释参数 d
或者i
:有符号十进制o
:无符号八进制u
:无符号十进制x
或者X
:无符号十六进制f
或者F
:双精度浮点数e
或者E
:指数格式双精度浮点数g
或者G
:根据转换后的值解释为f、F、e、E
a
或者A
:十六进制指数格式双精度浮点数c
:字符(若带上长度修饰符l
,则为宽字符)s
:字符串(若带上长度修饰符l
,则为宽字符)p
:指向void
的指针n
:到目前位置,此printf
调用输出的字符的数目将被写入到指针所指向的带符号整型中%
:一个%
字符C
:宽字符,等效于lc
S
:宽字符串,等效于ls
printf
族的变体:将可变参数(...)
替换成了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
:一个缓冲区指针。从该缓冲区中读取输入EOF
EOF
scanf
族用于分析输入字符串,将字符序列转换成指定类型的变量。在格式之后的各参数中包含了变量的地址,用转换结果对这些变量赋值。
%[*][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
返回值: NULL
tmpnam
函数产生一个与现有文件名不同的有效路径名字符串。每次调用它时,都产生一个不同路径名。最多调用次数是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
返回值: NULL
mkstemp
返回值: mkdtemp
函数创建了一个目录,该目录有一个唯一的名字;mkstemp
函数创建了一个文件,该文件有一个唯一的名字。名字是通过template
字符串进程构建的。
mkdtemp
函数创建的目录具有权限位集: S_IRUSR|S_IWUSR|S_IXUSR
。调用进程的文件模式创建屏蔽字可以进一步限制这些权限mkstemp
函数返回的文件描述符以读写方式打开。它创建的文件用访问权限位:S_IRUSR|S_IWUSR
mkstemp
创建的临时文件并不会自动删除示例:
#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