文件 I/O

一、标准IO介绍及缓冲区

Linux系统中一切皆文件 

1)IO的概念及介绍:IO就是输入输出,标准I/O由ANSI C标准定义,主流操作系统上都实现了C库,标准I/O通过缓冲机制减少系统调用,实现更高的效率

   I input    输入设备  比如键盘鼠标都是Input设备

   O output    输出设备  比如显示器

   优盘,网口,既是输入也是输出

 2)标准IO - 流:

FILE:标准IO用一个结构体类型来存放打开的文件的相关信息,标准I/O的所有操作都是围绕FILE来进行  

(stream):FILE又被称为流(stream) ,文本流/二进制流

3)流的缓冲类型:

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

缓冲区的概念:为了减少操作IO设备的次数,提高运行效率,在内存里面设置的缓冲区,全缓冲:缓冲区满才输出,行缓冲:遇到换行符输出

 全缓冲

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

行缓冲

当在输入和输出中遇到换行符(‘\n’)时,进行I/O操作  当流和一个终端关联时,典型的行缓冲

无缓冲

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

 4)标准I/O –stdin,stdout,stderr:

 标准IO预定义3个流程序运行时自动打开

标准输入流0STDIN_FILENOstdin
标准输出流1STDOUT_FILENOstdout
标准错误流2STDERR_FILENOstderr

 stdin/stdout默认行缓冲,stderr没有缓冲

二、文件的打开和关闭 

概念:打开就是占用资源,关闭就是释放资源 

文件的打开 

1)文件的打开函数

FILE *fopen (const char *path, const char *mode);

Path: 普通文件当前路径不需要加目录,其他要使用完整的路径

Mode:打开的模式

返回值:出错返回NULL,所以使用fopen函数必须判断是否为空

 2)打开模式

“r” 或 “rb”以只读的方式打开文件,文件必须存在。
“r+” 或 “r+b”

以读写的方式打开文件,文件必须存在。

“w” 或 “wb”以只写的方式打开文件,文件若存在则文件长度清为 0 ,若文件不存在则创建文件
“w+” 或 “w+b”以读写的方式打开文件,其他于“w”相同
“a” 或 “ab”

以只写的方式打开文件,若文件不存在则创建文件,向文件写入的数据追加到文件末尾

“a+” 或 “a+b”

以读写的方式打开文件,其他于“a”相同

1)编译错误:

f_open.c:9:38: error: ‘errno’ undeclared (first use in this function)

         printf("fopen:%s\n",strerror(errno));  

error: ‘errno’ undeclared  表示errno变量没有定义

解决方法:如果是系统变量用include 头文件,如果是你自己的,自己手动定义。

f_open.c:10:29: warning: implicit declaration of function ‘strerror’ [-Wimplicit-function-declaration]

         printf("fopen:%s\n",strerror(errno));

warning: implicit declaration of function ‘strerror’  表示strerror函数隐示的声明

解决方法:include 添加对应的头文件。

2) perror 库函数    头文件stdio.h

     strerror 库函数  头文件 errno.h  string.h

   perror和strerror 功能:打印系统的错误描述(注意:是系统错误,不是你自己代码错误)

文件的关闭  

函数原型:int fclose(FILE *stream)

  1. fclose()调用成功返回0,失败返回EOF(-1),并设置errno
  2. 流关闭时自动刷新缓冲中的数据并释放缓冲区,比如:常规文件把缓冲区内容写入磁盘
  3. 当一个程序正常终止时,所有打开的流都会被关闭
  4. fclose()函数的入参stream必须保证为非空,否则出现断错误。
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	FILE * fp;
	int fpret;

	fp = fopen("2.txt", "r+");//fopen打开文件函数

	if (fp == NULL){
	//	printf("file open failed\n");
		perror("fopen");
		printf("fopen:%s\n",strerror(errno));//报错信息打印
	} else {
		printf("file open success\n");
	//	perror("fopen");	通常使用在报错语句上
		
		fpret = fclose(fp);//fclose关闭文件函数
		if (fpret == 0) {
			printf("file close success\n");//返回0表示成功打印
		} else {
			perror("fclose");//关闭文件
		}
	}

	return 0;
}

 三、标准IO的字符的输入输出

字符的输入(单个字符) 

int  fgetc(FILE *stream);

int  getc(FILE *stream);   //宏

int  getchar(void);

成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),

getchar()等同于fgetc(stdin)

getc和fgetc区别是一个是宏一个是函数

注意事项:

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

2 .tdin 也是FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)

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

4 .调用getchar会阻塞,等待你的键盘输入

字符的输出(单个字符) 

int  fputc(int c, FILE *stream);

int  putc(int c, FILE *stream);

int  putchar(int c);

成功时返回写入的字符;出错时返回EOF

putchar(c)等同于fputc(c, stdout)

注意事项:

1.返回和输入参数都是int类型

2.遇到这种错误:Bad file descriptor,  很可能是文件打开的模式错误(只读模式去写,只写模式去读)

行输入 (读取整个行)

char  *gets(char *s);  读取标准输入到缓冲区s

char *fgets(char *s, int size, FILE *stream);

成功时返回s,到文件末尾或出错时返回NULL

遇到’\n’或已输入size-1个字符时返回,总是包含’\0’

注意事项:

1. gets函数已经被淘汰,因为会导致缓冲区溢出

2.fgets 函数第二个参数,输入的数据超出size,size-1个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于size-1 后面会添加换行符。

 行输出(写整行)

int  puts(const char *s);

int fputs(const char *s,  FILE *stream);

成功时返回非负整数;出错时返回EOF

puts将缓冲区s中的字符串输出到stdout,并追加’\n’

fputs将缓冲区s中的字符串输出到stream,不追加  ‘\n’

四、二进制读写 

文本文件和二进制的区别:存储的格式不同:文本文件只能存储文本。

计算机内码概念:文本符号在计算机内部的编码(计算机内部只能存储数字0101001....,所以所有符号都要编码)

二进制读写函数格式:

size_t fread(void *ptr, size_t size, size_t n, FILE *fp);

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

size_t size :读取的块大小

size_t n :读取的个数

FILE *fp  :读取的文件指针

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);

void *ptr  :写文件的内容的位置指针

size_t size :写的块大小

size_t n :写的个数

FILE *fp  :要写的文件指针

注意事项:

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

 五、流的刷新及定位

 流的刷新

 int fflush(FILE *fp);

成功时返回0;出错时返回EOF

将流缓冲区中的数据写入实际的文件

Linux下只能刷新输出缓冲区,输入缓冲区丢弃

如果输出到屏幕使用fflush(stdout)

流的定位 

long ftell(FILE *stream);

long fseek(FILE *stream, long offset,  int whence);

void rewind(FILE *stream);

fseek 参数(whence位置参数):SEEK_SET / SEEK_CUR / SEEK_END

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

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

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

offset参数 :偏移量,可正可负

注意事项:

1.文件的打开使用a模式 fseek无效

2.rewind(fp) 相当于 fseek(fp,0,SEEK_SET);

3.这三个函数只适用2G以下的文件

六、格式化输入输出 

格式化输出

int fprintf(FILE *stream, const char *fmt, …);

int sprintf(char *s, const char *fmt, …);

以指定格式 “年-月-日” 分别写入文件和缓冲区
int  year, month, date;
FILE *fp;
char buf[64];
year = 2014; month = 10; date = 26;
fp = fopen(“test.txt”, “a+”);
fprintf(fp, “%d-%d-%d\n”, year, month, date);
sprintf(buf, “%d-%d-%d\n”, year, month, date);

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

格式化输入 

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

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

#include <stdio.h>

int main(int argc, const char *argv[])
{
	FILE * fp;
	int year ;
	int month;
	int day;

	fp = fopen("ftset.txt", "r");

	if(fp == NULL){
		perror("fopen");
		return 0;
	}

	fscanf(fp, "%d-%d-%d\n", &year, &month, &day);
	printf("%d-%d-%d\n", year, month, day);

	fclose(fp);

	return 0;
}

重点掌握sprintf 和sscanf

七、标准IO练习 

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是正确的年份

获取文件内的所有行数量:

   while(fgets(buf,32,fp)!=NULL){

          if(buf[strlen(buf)-1] =='\n'){  //注意判断是否是一行结束

               linecount++;

          }

}

写完文件记得fflush ,写到磁盘里面去。

标准IO磁盘文件的缓冲区一般为4096

注意和标准输出的全缓冲区别,标准输出是1024

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

int main(int argc, const char *argv[])
{
	FILE * fp;
	time_t ctime;
	struct tm *ctimestr;
	int linecount = 1;
	char buf[32];

	fp = fopen("1.txt", "a+");
	if (fp == NULL){
		perror("fopen");
		return 0;
	}

	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("%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);
	fprintf(fp,"%d. %04d-%02d-%02d %2d:%02d:%02d\n",linecount, ctimestr->tm_year+1900, ctimestr->tm_mon+1, ctimestr->tm_mday,
			ctimestr->tm_hour, ctimestr->tm_min, ctimestr->tm_sec);
	linecount++;
	fflush(fp);
	sleep(1);
	}
	fclose(fp);
	return 0;
}

八、文件IO

文件IO的概念:

什么是文件IO,又称系统IO,系统调用

是操作系统提供的API接口函数。

POSIX接口 (了解)

注意:文件IO不提供缓冲机制

文件IO的API

open close read read

文件描述符概念:

英文:缩写fd(file descriptor)

是0-1023的数字,表示文件。

0, 1, 2 的含义 标准输入,标准输出,标准错误

文件IO 打开

open

int open(const char *pathname, int flags);   不创建文件

int open(const char *pathname, int flags, mode_t mode);  创建文件,不能创建设备文件

成功时返回文件描述符;出错时返回EOF

文件IO和标准的模式对应关系:

rO_RDONLY
r+O_RDWR
wO_WRONLY | O_CREAT | O_TRUNC, 0664
w+O_RDWR | O_CREAT | O_TRUNC, 0664
aO_WRONLY | O_CREAT | O_APPEND, 0664
a+O_RDWR | O_CREAT | O_APPEND, 0664

umask概念:

umask 用来设定文件或目录的初始权限

文件的关闭

int close(int fd)

关闭后文件描述符不能代表文件

文件IO的读写和定位

容易出错点:

求字符串长度使用sizeof,对二进制数据使用strlen

printf 的字符最后没有’\0’

文件的读

read函数用来从文件中读取数据:

#include  <unistd.h>  

ssize_t  read(int fd, void *buf, size_t count);  

成功时返回实际读取的字节数;出错时返回EOF

读到文件末尾时返回0

buf是接收数据的缓冲区 count不应超过buf大小

//从指定的文件中读取内容并统计大小
int  main(int argc, char *argv[]) {
{
    int  fd, n, total = 0;
    char  buf[64];
    if (argc < 2) {
       printf(“Usage : %s <file>\n”, argv[0]);  return -1;
    }
    if ((fd  = open(argv[1], O_RDONLY)) < 0) {
           perror(“open”);  return -1;
    }
    while ((n = read(fd, buf, 64)) > 0) {
        total += n;
    }
    ……

文件的写 

write函数用来向文件写入数据:  

#include  <unistd.h>  

ssize_t  write(int fd, void *buf, size_t count);  

成功时返回实际写入的字节数;出错时返回EOF

buf是发送数据的缓冲区 count不应超过buf大小

    int  fd;
    char  buf[20];

    if ((fd  = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
           perror(“open”);  return -1;
    }
    while (fgets(buf, 20, stdin) != NULL) {
        if (strcmp(buf, “quit\n”) == 0) break;
        write(fd, buf, strlen(buf));
    }
    ……

文件的定位 

lseek函数用来定位文件:  

#include  <unistd.h>  

off_t  lseek(int fd, off_t offset, intt whence);  

成功时返回当前的文件读写位置;出错时返回EOF

参数offset和参数whence同fseek完全一样

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一天干五顿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值