标准IO 文件和流

文件的概念和类型

Linux系统一切皆文件

概念

一组相关数据的有序集合

文件类型

  1. 常规文件 r
  2. 目录文件 d
  3. 字符设备文件 c
  4. 块设备文件 b
  5. 管道文件 p
  6. 套接字文件 s
  7. 符号链接文件 l

如何理解标准IO

标准I/O由ANSI C标准定义

主流操作系统上都实现了C库

标准I/O通过缓冲机制减少系统调用,实现更高的效率

IO的概念

  • I input 输入设备 比如键盘鼠标都是Input设备
  • O output 输出设备 比如显示器
  • 优盘,网口,既是输入也是输出

系统调用和库函数

  • 系统调用就是操作系统提供的接口函数
  • 如果我们把系统调用封装成库函数就可以起到隔离的作用,提供程序的可移植性
  • printf就是库函数然后调用了系统调用才在显示器上显示字符

流(FILE)的含义

就是数据的流,在程序中就是一个结构体

FILE

标准IO用一个结构体类型来存放打开的文件的相关信息

标准I/O的所有操作都是围绕FILE来进行

流(stream)

FILE又被称为流(stream)

文本流/二进制流

标准I/O预定义3个流

标准I/O预定义3个流,程序运行时自动打开

标准输入流(键盘)0STDIN_FILENOstdin
标准输出流(显示器)1STDOUT_FILENOstdout
标准错误流2STDERR_FILENOstderr

流的缓冲类型(重点)

全缓冲

当流的缓冲区无数据或无空间时才执行实际I/O操作

全缓冲的空间为 1 k 1k 1k

C程序结束或者缓冲区满,缓冲区的内容才会被打印,如下:

#include <stdio.h>
#include <unistd.h>
int main(int argc,char*argv[]){

    int i=0;
    for(i=0;i<1025;i++){   //i大于1024才会被打印
       printf("a");

    }
    //    printf("hello world\n");
    while(1){
    sleep(1);
    }
}

行缓冲

当在输入和输出中遇到换行符(‘\n’)时,进行I/O操作

当流和一个终端关联时,典型的行缓冲

Windows 和linux的换行符区别

  1. Windows是\r\n
  2. Linux 是\n

无缓冲

数据直接写入文件,流不进行缓冲

文件的打开和关闭

打开就是占用资源

关闭就是释放资源

标准I/O – 打开文件

下列函数可用于打开一个标准I/O流:

FILE *fopen (const char *path, const char *mode);
  • 成功时返回流指针;出错时返回NULL; FILE是文件类型

mode参数:

“r” 或 “rb”以只读方式打开文件,文件必须存在。
“r+” 或 ”r+b”以读写方式打开文件,文件必须存在。
“w” 或 “wb”以只写方式打开文件,若文件存在则文件长度清为0。若文件不存在则创建。
“w+” 或 “w+b”以读写方式打开文件,其他同”w”。
“a” 或 “ab”以只写方式打开文件,若文件不存在则创建;向文件写入的数 据被追加到文件末尾。
“a+” 或 “a+b”以读写方式打开文件。其他同”a”

fopen-示例

#include <stdio.h>

int main(int argc,char *argv[]){
	FILE *fp;
	fp = fopen("1.txt","r");
	if(fp == NULL){
        printf("Open filr failed\n");
		//perror("(perror)fopen");
		//printf("(strerror)fopen:%s\n",strerror(errno));
	}
	else{
		printf("Open filr success\n");
	}
	return 0;
}

处理错误信息

errno 存放错误号,由系统生成

perror先输出字符串s,再输出错误号对应的错误信息

strerror根据错误号返回对应的错误信息

extern int errno;

void perror(const char *s);

char *strerror(int errno);

错误信息处理-示例

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc,char *argv[]){
	FILE *fp;
	fp = fopen("1.txt","r");
	if(fp == NULL){
		perror("(perror)fopen");
		printf("(strerror)fopen:%s\n",strerror(errno));
	}
	else{
		printf("Open filr success\n");
	}
	return 0;
}

标准I/O – 关闭文件

int fclose(FILE *stream)
  • fclose()调用成功返回0,失败返回EOF,并设置errno
  • 流关闭时自动刷新缓冲中的数据并释放缓冲区
  • 当一个程序正常终止时,所有打开的流都会被关闭
  • 流一旦关闭后就不能执行任何操作

标准I/O的读写

流支持不同的读写方式:

  • 读写一个字符:fgetc()/fputc()一次读/写一个字符
  • 读写一行:fgets()和fputs()一次读/写一行
  • 读写若干个对象:fread()/fwrite() 每次读/写若干个对象,而每个对象具有相同的长度

打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!

按字符输入

下列函数用来输入一个字符:

函数返回值设置成int型既能处理有符号数又能处理无符号数

函数返回值是int类型不是char类型,主要是为了扩展返回值的范围。

 #include <stdio.h>
int fgetc(FILE \*stream); 
int getc(FILE \*stream); //宏
int getchar(void);
  • 成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),
  • getchar()等同于fgetc(stdin)
  • getc()和fgetc()区别是一个是宏一个是函数
  • 调用getchar会阻塞,等待你的键盘输入

示例:

 FILE *fp;
 int ch, count = 0;

 if ((fp = fopen(argv[1], “r”)) == NULL) { 
  perror(“fopen”); return -1;
 }

 while ((ch = fgetc(fp)) != EOF) { 
  count++; 
 }
 
 printf(“total %d bytes\n”, count);

按字符输出

下列函数用来输出一个字符:

#include <stdio.h>
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
  • 成功时返回写入的字符;出错时返回EOF
  • putchar©等同于fputc(c, stdout)

示例:

 FILE *fp;
 int ch;

 if ((fp = fopen(argv[1], “w”)) == NULL) { 
   perror(“fopen”); return -1;
 }

 for(ch = ‘a’; ch <=‘z’; ch++) { 
   fputc(ch, fp); 
 }

按行输入

下列函数用来输入一行:

#include <stdio.h>
char *gets(char *s);
char *fgets(char *s, int size, FILE *stream);
  • 成功时返回s,到文件末尾或出错时返回NULL
  • gets不推荐使用,没有设置长度容易造成缓冲区溢出
  • 遇到’\n’或已输入size-1个字符时返回,总是包含’\0’
  • fgets 函数第二个参数,输入的数据超出size,size-1个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于size-1 后面会添加换行符。

示例:

 #define N 6
 
 char buf[N];
 fgets(buf, N, stdin);
 printf(%s”, buf);

假设键盘输入分别是:

  • abcd<回车> buf中的内容是abcd
  • abcdef<回车> buf中的内容是abcde

按行输出

下列函数用来输出字符串:

#include <stdio.h>
int puts(const char *s);
int fputs(const char *s, FILE *stream);
  • 成功时返回非负整数;出错时返回EOF
  • puts将缓冲区s中的字符串输出到stdout, 并追加’\n’
  • fputs将缓冲区s中的字符串输出到stream,不追加 ‘\n’

示例:

 puts(“hello world”);

 FILE *fp;
 char buf[] = “hello world”;

 if ((fp = fopen(argv[1], “a”)) == NULL) { 
   perror(“fopen”); 
   return -1;
 }

 fputs(buf, fp);

注意:输出的字符串中可以包含’\n’,也可以不包含

按对象读写

下列函数用来从流中读写若干个对象:

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t n, FILE *fp); (size_t = unsigned int)
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);

void *ptr 读写内容放的位置指针

size_t size 读写的块大小

size_t n 读写的个数

FILE *fp 读写的文件指针

  • 成功返回读写的对象个数;出错时返回EOF
  • 既可以读写文本文件,也可以读写数据文件
  • 效率高

注意事项:

文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。

解决办法:

  • 移动指针(后面讲解)到文件头
  • 关闭文件,重新打开

流的刷新和定位

刷新流

#include <stdio.h>
int fflush(FILE *fp);
  • 成功时返回0;出错时返回EOF
  • 将流缓冲区中的数据写入实际的文件
  • Linux下只能刷新输出缓冲区,输入缓冲区丢弃

定位流

(long 32位返回2个G,这三个函数只适用2G以下的文件)

long ftell(FILE *stream); 
  • ftell() 成功时返回流的当前读写位置,出错时返回EOF
long fseek(FILE *stream, long offset, int whence);
  • fseek()定位一个流,成功时返回0,出错时返回EOF
  • whence参数:SEEK_SET/SEEK_CUR/SEEK_END

SEEK_SET:从距文件开头 offset 位移量为新的读写位置

SEEK_CUR:以目前的读写位置往后增加 offset 个位移量

SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量

  • offset参数:偏移量,可正可负
  • 打开a模式 fseek无效
void rewind(FILE *stream);
  • rewind()将流定位到文件开始位置
  • rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
示例一(在文件末尾追加字符’t’)
FILE *fp = fopen(“test.txt”, “r+);
fseek(fp, 0, SEEK_END);
fputc(‘t’, fp);
示例二(获取文件长度)
 FILE *fp;

 if ((fp = fopen(“test.txt”, “r+)) == NULL) {
   perror(“fopen”);
   return -1;
 }

 fseek(fp, 0, SEEK_END);
 printf(“length is %d\n”, ftell(fp));

判断流是否出错和结束

#include <stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);

ferror()返回1表示流出错;否则返回0

feof()返回1表示文件已到末尾;否则返回0

格式化输入和输出

sprintf和sscanf常用,重点掌握sprintf 和sscanf

格式化输出

int printf(const char *fmt,);
int fprintf(FILE *stream, const char *fmt,);
int sprintf(char *s, const char *fmt,);

成功时返回输出的字符个数;出错时返回EOF

格式化输入

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

练习题

题目

每隔1秒向文件test.txt中写入当前系统时间,格式如下:

1, 2014-10-15 15:16:42

2, 2014-10-15 15:16:43

该程序无限循环,直到按Ctrl-C中断程序

每次执行程序时,系统时间追加到文件末尾,序号递增

1, 2014-10-15 15:16:42

2, 2014-10-15 15:16:43

3, 2014-10-16 11:35:07

4, 2014-10-16 11:35:08

思路

time()用来获取系统时间(秒数)

time_t time(time_t *seconds) 返回值是从1970.1.1 0:0:0开始的秒数

localtime()将系统时间转换成本地时间

struct tm *localtime(const time_t *timer)

struct tm {

int tm_sec; /* 秒,范围从 0 到 59 */

int tm_min; /* 分,范围从 0 到 59 */

int tm_hour; /* 小时,范围从 0 到 23 */

int tm_mday; /* 一月中的第几天,范围从 1 到 31 */

int tm_mon; /* 月份,范围从 0 到 11 */

int tm_year; /* 自 1900 起的年数 */

int tm_wday; /* 一周中的第几天,范围从 0 到 6 */

int tm_yday; /* 一年中的第几天,范围从 0 到 365 */

int tm_isdst; /* 夏令时 */

};

注意:

int tm_mon; 获取的值要加1是正确的月份

int tm_year; 获取的值加1900是正确的年份

代码

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc,char *argv[]){
     FILE *fp;
     time_t ctime;
     struct tm *ctimestr;
     int linecount = 0; 
     char buf[32];
     fp=fopen("test.txt","a+");
     if(fp==NULL){
         perror("fopen");
         return 0;
 }
 //calculate test.txt  line    

 while(fgets(buf,32,fp)!=NULL){
      if(buf[strlen(buf)-1] =='\n'){
           linecount++;
      }
} 

 while(1){
     ctime = time(NULL);
     //printf("ctime=%d\n",(int)ctime);
     ctimestr = localtime(&ctime);
     printf("%04d-%02d-%02d %02d:%02d:%02d\n",ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
                                 ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
     fprintf(fp,"%d, %04d-%02d-%02d %02d:%02d:%02d\n",linecount,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
                                 ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
     fflush(fp);
     linecount++;
     sleep(1);
 }
 fclose(fp);
}

注意(写完文件记得fflush,\n也不会读出缓冲区)

  • 写完文件记得fflush ,写到磁盘里面去。
  • 标准IO磁盘文件的缓冲区一般为4096
  • 注意和标准输出的全缓冲区别,标准输出是1024
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深センのHZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值