网站备案ip,外贸网站建设网,济南网站建设推荐q479185700强涵,网站建设方案拓扑图标准I/O的由来 标准I/O指的是ANSI C 中定义的用于I/O操作的一系列函数。 只要操作系统安装了C库#xff0c;标准I/O函数就可以调用。换句话说#xff0c;如果程序中使用的是标准I/O函数#xff0c;那么源代码不需要任何修改就可以在其他操作系统下编译运行#xff0c;具有更… 标准I/O的由来 标准I/O指的是ANSI C 中定义的用于I/O操作的一系列函数。 只要操作系统安装了C库标准I/O函数就可以调用。换句话说如果程序中使用的是标准I/O函数那么源代码不需要任何修改就可以在其他操作系统下编译运行具有更好的可移植性。 除此之外使用标准I/O可以减少系统调用的次数提高系统效率。标准I/O函数在执行时也会用到系统调用。在执行系统调用时Linux必须从用户态切换到内核态处理相应的请求然后再返回到用户态。如果频繁的执行系统调用会增加系统的开销。为避免这种情况标准I/O在使用时为用户控件创建缓冲区读写时先操作缓冲区在合适的时机再通过系统调用访问实际的文件从而减少了使用系统调用的次数。 流的含义 标准I/O的核心对象就是流。当用标准I/O打开一个文件时就会创建一个FILE结构体描述该文件或者理解为创建一个FILE结构体和实际打开的文件关联起来。我们把这个FILE结构体形象的称为流我们在stdio.h里可以看到这个FILE结构体。 [cpp] view plaincopy typedef struct { short level; /* fill/empty level of buffer */ unsigned flags; /* File status flags */ char fd; /* File descriptor */ unsigned char hold; /* Ungetc char if no buffer */ short bsize; /* Buffer size */ unsigned char *buffer; /* Data transfer buffer */ unsigned char *curp; /* Current active pointer */ unsigned istemp; /* Temporary file indicator */ short token; /* Used for validity checking */ } FILE; /* This is the FILE object */ 这个结构体1对 fd 进行了封装2对缓存进行了封装 unsigned char *buffer; 这而指向了buffer 的地址实际这块buffer是cache,我们要将其与用户控件的buffer分开。 标准I/O函数都是基于流的各种操作标准I/O中的流的缓冲类型有下面三种 1、全缓冲。 在这种情况下,实际的I/O操作只有在缓冲区被填满了之后才会进行。对驻留在磁盘上的文件的操作一般是有标准I/O库提供全缓冲。缓冲区一般是在第一次对流进行I/O操作时,由标准I/O函数调用malloc函数分配得到的。 术语flush描述了标准I/O缓冲的写操作。缓冲区可以由标准I/O函数自动flush(例如缓冲区满的时候);或者我们对流调用fflush函数。 2、行缓冲 在这种情况下,只有在输入/输出中遇到换行符的时候,才会执行实际的I/O操作。这允许我们一次写一个字符,但是只有在写完一行之后才做I/O操作。一般的,涉及到终端的流--例如标注输入(stdin)和标准输出(stdout)--是行缓冲的。 3、无缓冲 标准I/O库不缓存字符。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。 标准I/O函数时库函数是对系统调用的封装所以我们的标准I/O函数其实都是基于文件I/O函数的是对文件I/O函数的封装下面具体介绍·标准I/O最常用的函数 一、流的打开与关闭 使用标准I/O打开文件的函数有fopen() 、fdopen() 、freopen()。他们可以以不同的模式打开文件都返回一个指向FILE的指针该指针指向对应的I/O流。此后对文件的读写都是通过这个FILE指针来进行。 fopen函数描述如下 所需头文件#include stdio.h函数原型FILE *fopen(const char *path, const char *mode);函数参数 path: 包含要打开的文件路径及文件名 mode:文件打开方式 函数返回值 成功指向FILE的指针 失败NULL mode用于指定打开文件的方式。 关闭流的函数为fclose()该函数将流的缓冲区内的数据全部写入文件中并释放相关资源。 fclose()函数描述如下 所需头文件#include stdio.h函数原型int fclose(FILE *stram);函数参数 stream:已打开的流指针 函数返回值 成功0 失败EOF 二、流的读写 1、按字符字节输入/输出 字符输入/输出函数一次仅读写一个字符。 字符输入函数原型如下 所需头文件#include stdio.h函数原型 int getc(FILE *stream); int fgetc(FILE *stream); int getchar (void); 函数参数 stream:要输入的文件流 函数返回值 成功读取的字符 失败EOF 函数getchar等价于get(stdin)。前两个函数的区别在于getc可被实现为宏而fgetc则不能实现为宏。这意味着 1getc 的参数不应当是具有副作用的表达式。 2因为fgetc一定是一个函数所以可以得到其地址。这就允许将fgetc的地址作为一个参数传给另一个参数 3调用fgetc所需时间很可能长于调用getc因为调用函数通常所需的时间长于调用宏。 这三个函数在返回下一个字符时会将其unsigned char 类型转换为int类型。说明为什么不带符号的理由是如果是最高位为1也不会使返回值为负。要求整数返回值的理由是这样就可以返回所有可能的字符值再加上一个已出错或已达到文件尾端的指示值。在stdio.h中的常量EOF被要求是一个负值其值经常是-1。这就意味着不能将这三个函数的返回值存放在一个字符变量中以后还要将这些函数的返回值与常量EOF相比较。 注意不管是出错还是到达文件尾端这三个函数都返回同样的值。为了区分这两种不同的情况必须调用ferror或feof。 [cpp] view plaincopy #include stdio.h int ferror (FILE *fp); int feof (FILE *fp); 两个函数返回值若条件为真则返回非0值真否则返回0假 在大多数实现中为每个流在FILE对象中维持了两个标志 出错标志。 文件结束标志。 字符输出-函数原型如下 所需头文件#include stdio.h函数原型 int putc (int c ,FILE *stream); int fputc (int c, FILE *stream); int putchar(int c); 函数返回值 成功输出的字符c 失败EOF putc()和fputc()向指定的流输出一个字符节putchar()向stdout输出一个字符节。 2、按行输入、输出 行输入/输出函数一次操作一行。 行输入函数原型如下 所需头文件#include stdio.h函数原型 char *gets(char *s); char *fgets(char *s,int size,FILE *stream); 函数参数 s存放输入字符串的缓冲区首地址 size输入的字符串长度 stream对应的流 函数返回值 成功s 失败或到达文件末尾NULL 这两个函数都指定了缓冲区的地址读入的行将送入其中。gets从标准输入读而fgets则从指定的流读。 gets函数容易造成缓冲区溢出不推荐使用 fgets从指定的流中读取一个字符串当遇到 \n 或读取了 size - 1个字符串后返回。注意fgets不能保证每次都能读出一行。 如若该行包括最后一个换行符的字符数超过size -1 则fgets只返回一个不完整的行但是缓冲区总是以null字符结尾。对fgets的下一次调用会继续执行。 行输出函数原型如下 所需头文件#include stdio.h函数原型 int puts(const char *s); int fgets(const char *s,FILE *stream); 函数参数 s存放输入字符串的缓冲区首地址 stream对应的流 函数返回值 成功非负值 失败或到达文件末尾NULL 函数fputs将一个以null符终止的字符串写到指定的流尾端的终止符null不写出。注意这并不一定是每次输出一行因为它并不要求在null符之前一定是换行符。通常在null符之前是一个换行符但并不要求总是如此。 下面举个例子模拟文件的复制过程 [cpp] view plaincopy #include stdio.h #include string.h #include unistd.h #include fcntl.h #define maxsize 5 int main(int argc, char *argv[]) { FILE *fp1 ,*fp2; char buffer[maxsize]; char *p,*q; if(argc 3) { printf(Usage:%s srcfile desfile\n,argv[0]); return -1; } if((fp1 fopen(argv[1],r)) NULL) { perror(fopen argv[1] fails); return -1; } if((fp2 fopen(argv[2],w)) NULL) { perror(fopen argv[2] fails); return -1; } while((p fgets(buffer,maxsize,fp1)) ! NULL) { fputs(buffer,fp2); } if(p NULL) { if(ferror(fp1)) perror(fgets failed); if(feof(fp1)) printf(cp over!\n); } fclose(fp1); fclose(fp2); return 0; } 执行结果如下 [cpp] view plaincopy fsubuntu:~/qiang/stdio/cp$ ls -l total 16 -rwxrwxr-x 1 fs fs 7503 Jan 5 15:49 cp -rw-rw-r-- 1 fs fs 736 Jan 5 15:50 cp.c -rw-rw-r-- 1 fs fs 437 Jan 5 15:15 time.c fsubuntu:~/qiang/stdio/cp$ ./cp time.c 1.c cp over! fsubuntu:~/qiang/stdio/cp$ ls -l total 20 -rw-rw-r-- 1 fs fs 437 Jan 5 21:09 1.c -rwxrwxr-x 1 fs fs 7503 Jan 5 15:49 cp -rw-rw-r-- 1 fs fs 736 Jan 5 15:50 cp.c -rw-rw-r-- 1 fs fs 437 Jan 5 15:15 time.c fsubuntu:~/qiang/stdio/cp$ 我们可以看到这里将time.c拷贝给1.c 1.c和time.c大小一样都是437个字节 3、以指定大小为单位读写文件 三、流的定位 四、格式化输入输出 这里举个相关应用例子循环记录系统时间 实验内容程序每秒一次读取依次系统时间并写入文件 [cpp] view plaincopy #include stdio.h #include unistd.h #include time.h #define N 64 int main(int argc, char *argv[]) { int n; char buf[N]; FILE *fp; time_t t; if(argc 2) { printf(Usage : %s file \n,argv[0]); return -1; } if((fp fopen(argv[1],a)) NULL) { perror(open fails); return -1; } while(1) { time(t); fprintf(fp,%s,ctime(t)); fflush(fp); sleep(1); } fclose(fp); return 0; } 执行结果如下 [cpp] view plaincopy fsubuntu:~/qiang/stdio/timepri$ ls -l total 12 -rwxrwxr-x 1 fs fs 7468 Jan 5 16:06 time -rw-rw-r-- 1 fs fs 451 Jan 5 17:40 time.c fsubuntu:~/qiang/stdio/timepri$ ./time 1.txt ^C fsubuntu:~/qiang/stdio/timepri$ ls -l total 16 -rw-rw-r-- 1 fs fs 175 Jan 5 21:14 1.txt -rwxrwxr-x 1 fs fs 7468 Jan 5 16:06 time -rw-rw-r-- 1 fs fs 451 Jan 5 17:40 time.c fsubuntu:~/qiang/stdio/timepri$ cat 1.txt Tue Jan 5 21:14:11 2016 Tue Jan 5 21:14:12 2016 Tue Jan 5 21:14:13 2016 Tue Jan 5 21:14:14 2016 Tue Jan 5 21:14:15 2016 Tue Jan 5 21:14:16 2016 Tue Jan 5 21:14:17 2016 fsubuntu:~/qiang/stdio/timepri$