Linux 系统应用编程——标准I/O

标准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
  1. typedef struct  {  
  2.         short           level;          /* fill/empty level of buffer */  
  3.         unsigned        flags;          /* File status flags    */  
  4.         char            fd;             /* File descriptor      */  
  5.         unsigned char   hold;           /* Ungetc char if no buffer */  
  6.         short           bsize;          /* Buffer size          */  
  7.         unsigned char   *buffer;        /* Data transfer buffer */  
  8.         unsigned char   *curp;          /* Current active pointer */  
  9.         unsigned        istemp;         /* Temporary file indicator */  
  10.         short           token;          /* Used for validity checking */  
  11. }       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则不能实现为宏。这意味着:

1)getc 的参数不应当是具有副作用的表达式。

2)因为fgetc一定是一个函数,所以可以得到其地址。这就允许将fgetc的地址作为一个参数传给另一个参数;

3)调用fgetc所需时间很可能长于调用getc,因为调用函数通常所需的时间长于调用宏。 

    这三个函数在返回下一个字符时,会将其unsigned char 类型转换为int类型。说明为什么不带符号的理由是,如果是最高位为1也不会使返回值为负。要求整数返回值的理由是,这样就可以返回所有可能的字符值再加上一个已出错或已达到文件尾端的指示值。在<stdio.h>中的常量EOF被要求是一个负值,其值经常是-1。这就意味着不能将这三个函数的返回值存放在一个字符变量中,以后还要将这些函数的返回值与常量EOF相比较。

    注意,不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror或feof。

[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int ferror (FILE *fp);  
  4. 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
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <unistd.h>  
  4. #include <fcntl.h>  
  5. #define maxsize 5  
  6.   
  7. int main(int argc, char *argv[])  
  8. {  
  9.     FILE *fp1 ,*fp2;  
  10.     char buffer[maxsize];  
  11.     char *p,*q;  
  12.   
  13.     if(argc < 3)  
  14.     {  
  15.         printf("Usage:%s <srcfile> <desfile>\n",argv[0]);  
  16.         return -1;  
  17.     }  
  18.   
  19.     if((fp1 = fopen(argv[1],"r")) == NULL)  
  20.     {  
  21.         perror("fopen argv[1] fails");  
  22.         return -1;  
  23.     }  
  24.   
  25.     if((fp2 = fopen(argv[2],"w+")) == NULL)  
  26.     {  
  27.         perror("fopen argv[2] fails");  
  28.         return -1;  
  29.     }  
  30.   
  31.   
  32.     while((p = fgets(buffer,maxsize,fp1)) != NULL)  
  33.     {  
  34.          fputs(buffer,fp2);  
  35.     }  
  36.   
  37.     if(p == NULL)  
  38.     {  
  39.         if(ferror(fp1))  
  40.             perror("fgets failed");  
  41.         if(feof(fp1))  
  42.             printf("cp over!\n");  
  43.     }  
  44.   
  45.     fclose(fp1);  
  46.     fclose(fp2);  
  47.           
  48.     return 0;  
  49. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/stdio/cp$ ls -l  
  2. total 16  
  3. -rwxrwxr-x 1 fs fs 7503 Jan  5 15:49 cp  
  4. -rw-rw-r-- 1 fs fs  736 Jan  5 15:50 cp.c  
  5. -rw-rw-r-- 1 fs fs  437 Jan  5 15:15 time.c  
  6. fs@ubuntu:~/qiang/stdio/cp$ ./cp time.c 1.c  
  7. cp over!  
  8. fs@ubuntu:~/qiang/stdio/cp$ ls -l  
  9. total 20  
  10. -rw-rw-r-- 1 fs fs  437 Jan  5 21:09 1.c  
  11. -rwxrwxr-x 1 fs fs 7503 Jan  5 15:49 cp  
  12. -rw-rw-r-- 1 fs fs  736 Jan  5 15:50 cp.c  
  13. -rw-rw-r-- 1 fs fs  437 Jan  5 15:15 time.c  
  14. fs@ubuntu:~/qiang/stdio/cp$   

我们可以看到,这里将time.c拷贝给1.c ,1.c和time.c大小一样,都是437个字节;

3、以指定大小为单位读写文件


三、流的定位


四、格式化输入输出


这里举个相关应用例子:循环记录系统时间

实验内容:程序每秒一次读取依次系统时间并写入文件

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <time.h>  
  4. #define N 64  
  5.   
  6. int main(int argc, char *argv[])  
  7. {  
  8.     int n;  
  9.     char buf[N];  
  10.     FILE *fp;  
  11.     time_t t;  
  12.   
  13.     if(argc < 2)  
  14.     {  
  15.         printf("Usage : %s <file >\n",argv[0]);  
  16.         return -1;  
  17.     }  
  18.   
  19.     if((fp = fopen(argv[1],"a+")) == NULL)  
  20.     {  
  21.         perror("open fails");  
  22.         return -1;  
  23.     }  
  24.   
  25.     while(1)  
  26.     {  
  27.         time(&t);  
  28.         fprintf(fp,"%s",ctime(&t));  
  29.         fflush(fp);  
  30.         sleep(1);  
  31.     }  
  32.   
  33.     fclose(fp);  
  34.   
  35.     return 0;  
  36. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/stdio/timepri$ ls -l  
  2. total 12  
  3. -rwxrwxr-x 1 fs fs 7468 Jan  5 16:06 time  
  4. -rw-rw-r-- 1 fs fs  451 Jan  5 17:40 time.c  
  5. fs@ubuntu:~/qiang/stdio/timepri$ ./time 1.txt  
  6. ^C  
  7. fs@ubuntu:~/qiang/stdio/timepri$ ls -l  
  8. total 16  
  9. -rw-rw-r-- 1 fs fs  175 Jan  5 21:14 1.txt  
  10. -rwxrwxr-x 1 fs fs 7468 Jan  5 16:06 time  
  11. -rw-rw-r-- 1 fs fs  451 Jan  5 17:40 time.c  
  12. fs@ubuntu:~/qiang/stdio/timepri$ cat 1.txt  
  13. Tue Jan  5 21:14:11 2016  
  14. Tue Jan  5 21:14:12 2016  
  15. Tue Jan  5 21:14:13 2016  
  16. Tue Jan  5 21:14:14 2016  
  17. Tue Jan  5 21:14:15 2016  
  18. Tue Jan  5 21:14:16 2016  
  19. Tue Jan  5 21:14:17 2016  
  20. fs@ubuntu:~/qiang/stdio/timepri$   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402362.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GitLab五种权限

From: https://blog.csdn.net/chenguanghan123/article/details/100523824

Winfrom实现圆角设计

主要代码 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Paint(object sender, PaintEventArgs e) { Type(this, 25, 0.1); } private void…

Linux 系统应用编程——进程基础

一、Linux下多任务机制的介绍 Linux有一特性是多任务&#xff0c;多任务处理是指用户可以在同一时间内运行多个应用程序&#xff0c;每个正在执行的应用程序被称为一个任务。 多任务操作系统使用某种调度(shedule)策略&#xff08;由内核来执行&#xff09;支持多个任务并发执行…

[正则] - 学习过程1

1. 判断是否以xxxx开头: 以数字. 开头&#xff0c;如“2. ” if re.match(^\d\. , content):return <h3>%s</h3> %(content) 2. 将内容中以[ dsfda789 df ] 的内容替换成tpl index 0 input {w: 60 # input宽度 }def replaceValue(matched):global i…

【Python文件处理】递归批处理文件夹子目录内所有txt数据

因为有个需求&#xff0c;需要处理文件夹内所有txt文件&#xff0c;将txt里面的数据筛选&#xff0c;重新存储。 虽然手工可以做&#xff0c;但想到了python一直主张的是自动化测试&#xff0c;就想试着写一个自动化处理数据的程序。 一.分析数据格式 需要处理的数据是txt格式存…

Windows Azure 之服务总线中继服务

Windows Azure的服务总线允许在Web服务内部与外部之间做成一个公共的连接点&#xff0c;在无需更改企业防火墙或者其他安全配置的情况下连接内部和外部的服务 而使用Azure云服务的时候由于缩放的原因通过IP来制定连接也是不科学的&#xff0c;而中继服务则可以充当很好的公共连…

C#对char[]的处理

先来看一段简单的C#代码&#xff1a; private void button3_Click(object sender, EventArgs e){char[] a new char[6] { h, e, L, O, \0, \0 }; // 少赋值一个元素都会报错string b new string(a);string result String.Format("b {0}, b.Length {1}",…

Centos7 关闭防火墙(Firewalld ),使用防火墙(iptables)

1、直接关闭防火墙 systemctl stop firewalld.service&#xff1b; #停止firewall systemctl disable firewalld.service&#xff1b; #禁止firewall开机启动 2、安装并启动 iptables service&#xff0c;以及设置开机自启 yum -y install iptables-services&#xff1b;#安装i…

【qt】QT 的信号与槽机制

QT 是一个跨平台的 C GUI 应用构架&#xff0c;它提供了丰富的窗口部件集&#xff0c;具有面向对象、易于扩展、真正的组件编程等特点&#xff0c;更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT 库的基础之上。 QT 支持下列平台&#xff1a;MS/WINDOWS-9…

C# String 前面不足位数补零的方法

using System; using System.Collections.Generic; using System.Linq;namespace ConsoleApp1 {class Program{static void Main(string[] args){//var a 5;var a 24;// 整数前面补N个0以保存对齐Console.WriteLine("{0:D4}", a);Console.WriteLine(a.ToString(&qu…

Linux 系统应用编程——进程间通信(上)

现在再Linux应用较多的进程间通信方式主要有以下几种&#xff1a; 1&#xff09;无名管道&#xff08;pipe&#xff09;及有名管道&#xff08;fifo&#xff09;&#xff1a;无名管道可用于具有亲缘关系进程间的通信&#xff1b;有名管道除具有管道相似的功能外&#xff0c;它还…

通过JDBK操作数据库

一、配置程序——让我们程序能找到数据库的驱动jar包1.把.jar文件复制到项目中去,整合的时候方便。2.在eclipse项目右击“构建路径”--“配置构建路径”--“库”--“添加外部jar”--找到数据库的驱动jar包--点击确定。会在左侧包资源管理器中出现“引用的库”&#xff0c;在里面…

scanf函数详解(下)

问题一如何让scanf()函数正确接受有空格的字符串&#xff1f;如: I love you!#include <stdio.h>int main(){char str[80];scanf("%s",str);printf("%s",str);return 0;}输入&#xff1a;I love you!上述程序并不能达到预期目的&#xff0c;scanf()扫…

77 大道理

1. 鲶鱼效应 以前&#xff0c;沙丁鱼在运输过程中成活率很低。后有人发现&#xff0c;若在沙丁鱼中放一条鲶鱼&#xff0c;情况却有所改观&#xff0c;成活率会大大提高。这是何故呢&#xff1f;原来鲶鱼在到了一个陌生的环境后&#xff0c;就会“性情急燥”&#xff0c;四处乱…

Linux 系统应用编程——网络编程(常用命令解析)

1、telnet Telnet协议是TCP/IP协议族中的一员&#xff0c;是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序&#xff0c;用它连接到服务器。终端使用者可以在telnet程序中输入命令&#…

灾难 BZOJ 2815

灾难 【样例输入】 5 0 1 0 1 0 2 3 0 2 0 【样例输出】 4 1 0 0 0 题解&#xff1a; 先跑出拓扑序 我们按拓扑序建立一棵“灭绝树” 灭绝树含义是当一个点灭绝时&#xff0c;它的子树将会全部灭绝 所以答案就是点在灭绝树中的子树大小 一个点如果灭绝&#xff0c;那么需要所有…

centos关于”running yum-complete-transaction first...

2019独角兽企业重金招聘Python工程师标准>>> 今天在用yum安装软件出错几次户&#xff0c;总是有提示信息&#xff1a; There are unfinished transactions remaining. You might consider running yum-complete-transaction first to finish them. The program yum…

js身份证号、电话脱敏处理(用*替换中间数据)

数字类型 certificatecodecopy certificatecode.replace(/^(.{6})(?:\d)(.{4})$/, "\$1****\$2"); 所有类型 enginenocopy engineno.replace(/^(.{2})(?:\w)(.{1})$/, "\$1****\$2"); enginenocopy engineno.replace(/^(.{4})(?:\w)(.{4})$/, &…

Linux 系统应用编程——网络编程(I/O模型)

Unix下可用的5种I/O模型&#xff1a;阻塞I/O非阻塞I/OI/O复用(select和poll)信号驱动I/O(SIGIO)异步I/O(POSIX的aio_系列函数)一个输入操作通常包括两个不同的阶段&#xff1a;1&#xff09;等待数据准备好&#xff1b;2&#xff09;从内核向进程复制数据&#xff1b;对于一个套…

Windows 7 OpenGL配置

Windows 7 OpenGL配置&#xff0c;解决“无法启动此程序,因为计算机中丢失glut32.dll。”转载于:https://www.cnblogs.com/yangai/p/6253332.html