1. 标准IO介绍及缓冲区
Linux IO 进程课程目的:学习编写 linux 应用程序(APP)
Linux 文件的种类 :
常规文件 r
目录文件 d
字符文件 c
块文件 b
链接文件(相当于 windows 快捷方式)l
socket文件 s
管道文件 pIO 的概念
I input: 输入设备 比如键盘鼠标都是 Input 设备
O output 输出设备 比如显示器
优盘,网口,既是输入也是输出系统调用和库函数
系统调用就是操作系统提供的接口函数.
如果我们把系统调用封装成库函数就可以起到隔离的作用,提供程序的可移植性。
Printf 就是库函数然后调用了系统调用才在显示器上显示字符流的概念
就是数据的流,在程序中就是一个结构体。Windows 和 linux 的换行符区别
Windows 是\r\n
Linux 是\n缓冲区的概念
为了减少操作 IO 设备的次数,提高运行效率,在内存里面设置的缓冲区,
全缓冲:缓冲区满才输出
行缓冲:遇到换行符输出-
三种标准 IO :
缓冲区概念演示:
程序正常结束会刷新缓冲区
#include<stdio.h>
int main(int argc, char* argv[]){
printf("hello world");
return 0;
}
- Sleep 函数:是释放 cpu 给其他应用程序使用的库函数。使用的头文件是#include<unistd.h>
查看头文件方法:man 2 函数 ,或者 man 3 函数
#include<stdio.h>
#include<unistd.h>
int main(int argc, char* argv[]){
printf("hello world");
while(1){
sleep(1);
}
return 0;
}
结果无任何输出,因为缓冲区未满也没\n, 程序也没结束。
2. 标准IO: 文件的打开和关闭
文件的打开和关闭概念
打开就是占用资源
关闭就是释放资源
文件的打开
文件的打开函数
FILE *fopen (const char *path, const char *mode);
Path: 普通文件当前路径不需要加目录,其他要使用完整的路径
Mode:
返回值:出错返回 NULL,所以使用 fopen 函数必须判断是否为空-
文件打开的模式(非常重要)
编译错误:
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 添加对应的头文件。perror 库函数 头文件 stdio.h
strerror 库函数 头文件 errno.h string.h
perror 和 strerror 功能:打印系统的错误描述(注意:是系统错误,不是你自己代码错误)
文件的关闭:
函数原型:int fclose(FILE *stream)
- fclose()调用成功返回 0,失败返回 EOF(-1),并设置 errno
- 流关闭时自动刷新缓冲中的数据并释放缓冲区,比如:常规文件把缓冲区内容写入磁盘
- 当一个程序正常终止时,所有打开的流都会被关闭
- fclose()函数的入参 stream 必须保证为非空,否则出现断错误。
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(int argc, char * argv[]){
FILE *fp = fopen("1.txt","r");
int fret = EOF;
if(fp == NULL){
perror("fopen");
printf("fopen: %s\n",strerror(errno));
}else{
printf("fopen: open file success\n");
fret = fclose(fp);
if(fret == 1){
printf("file close success\n");
}else{
perror("fclose");
}
}
return 1;
}
3. 标准 IO 的字符输入和输出
字符的输入(读单个字符)
int fgetc(FILE *stream);
int getc(FILE *stream); //宏
int getchar(void);
成功时返回读取的字符;若到文件末尾或出错时返回 EOF(-1),
getchar()等同于 fgetc(stdin)
getc 和 fgetc 区别是一个是宏一个是函数
注意事项:
- 函数返回值是 int 类型不是 char 类型,主要是为了扩展返回值的范围。(char范围是-128 -- 127 unsigned char 0 - 255, int就都包含了)
- stdin 也是 FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)
- 打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!
- 调用 getchar 会阻塞,等待你的键盘输入
字符的输出(写单个字符):
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
成功时返回写入的字符;出错时返回 EOF
putchar(c)等同于 fputc(c, stdout)
行输入(读取整个行)
char *gets(char *s); 读取标准输入到缓冲区 s
char *fgets(char *s, int size, FILE *stream);
成功时返回 s,到文件末尾或出错时返回 NULL
遇到’\n’或已输入 size-1 个字符时返回,总是包含’\0’
注意事项:
- gets 函数已经被淘汰,因为会导致缓冲区溢出
- 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’
4. 标准IO读写:二进制方式
文本文件和二进制的区别:
存储的格式不同:文本文件只能存储文本。
计算机内码概念:文本符号在计算机内部的编码(计算机内部只能存储数字 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 要写的文件指针
fread: 从1.txt中读出10个字符
#include<stdio.h>
#include<stdlib.h>
int main(){
FILE *fp;
size_t ret;
char* buff;
fp = fopen("1.txt","r");
if(fp == NULL){
perror("fopen");
return -1;
}
buff = (char *)malloc(100);
if(buff == NULL){
perror("buff");
fclose(fp);
return -1;
}
ret = fread(buff,10,1,fp);
if(ret == EOF){
perror("fread");
goto end;
}
printf("ret = %s\n",buff);
end:
free(buff);
fclose(fp);
return 0;
}
fwrite: 往1.bin中写入1个struct Student对象的二进制
#include<stdio.h>
#include<string.h>
struct Student{
char name[16];
int age;
char sex[8];
};
int main(){
FILE *fp;
size_t ret;
fp = fopen("1.bin","w");
if(fp == NULL){
perror("fopen");
return -1;
}
struct Student stu;
strcpy(stu.name,"zhangsan");
stu.age = 18;
strcpy(stu.sex,"male");
ret = fwrite(&stu,sizeof(stu),1,fp);
if(ret == EOF){
perror("fwrite");
goto end;
}
end:
fclose(fp);
return 0;
}
#include<stdio.h>
#include<string.h>
struct Student{
char name[16];
int age;
char sex[8];
};
int main(){
FILE *fp;
size_t ret;
struct Student stu;
struct Student stu2;
fp = fopen("1.bin","w");
if(fp == NULL){
perror("fopen");
return -1;
}
strcpy(stu.name,"zhangsan");
stu.age = 18;
strcpy(stu.sex,"male");
ret = fwrite(&stu,sizeof(stu),1,fp);
if(ret == EOF){
perror("fwrite");
goto end;
}else{
printf("write student success\n");
}
ret = fread(&stu2,sizeof(stu2),1,fp);
if(ret == EOF){
perror("fread");
goto end;
}
printf("stu2.name=%s,stu2.age=%d,stu2.sex=%s\n",stu2.name,stu2.age,stu2.sex);
end:
fclose(fp);
return 0;
}
输出:stu2.name=,stu2.age=0,stu2.sex=
注意事项:
文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。
解决办法:移动指针(后面讲解)到文件头;或关闭文件,重新打开
5. 流刷新定位
流的刷新
int fflush(FILE *fp);
- 成功时返回 0;出错时返回 EOF
- 将流缓冲区中的数据写入实际的文件
- Linux 下只能刷新输出缓冲区,输入缓冲区丢弃
- 如果输出到屏幕使用 fflush(stdout)
#include<stdio.h>
#include<unistd.h>
int main(){
printf("abcdefghijklmn");
fflush(stdout); //不加这一行将无法输出 因为缓冲区未满也没\n, 程序也没结束。
while(1){
sleep(1);
}
return 0;
}
#include<stdio.h>
#include<unistd.h>
int main(){
FILE *fp;
size_t ret;
fp = fopen("fflush.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
ret = fwrite("abcdefghigklmn",14,1,fp);
if(ret == EOF){
perror("fwrite");
}
fflush(fp); //不加这一行 也无法将内容写入到文件
while(1){
sleep(1);
}
return 0;
}
流的定位
include <stdio.h>
long ftell(FILE *stream);
long fseek(FILE *stream, long offset, int whence);
void rewind(FILE *stream);
- ftell() 成功时返回流的当前读写位置,出错时返回 EOF
- fseek() 定位一个流,成功时返回 0 ,出错时返回 EOF
- whence 参数: SEEK_SET/SEEK_CUR/SEEK_END
- SEEK_SET 从距文件开头 offset 位移量为新的读写位置
- SEEK_CUR :以目前的读写位置往后增加 offset 个位移量
- SEEK_END :将读写位置指向文件尾后再增加 offset 个位移量
- offset 参数:偏移量,可正可负
- 打开 a 模式 fseek 无效
- rewind() 将流定位到文件开始位置
- 读写流时,当前读写位置自动后移
- 注意事项:
1.文件的打开使用 a 模式 fseek 无效
2.rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
3.这三个函数只适用 2G 以下的文件
#include<stdio.h>
int main(){
FILE *fp;
fp = fopen("1.txt","w");
if(fp == NULL){
perror("fopen");
return 0;
}
fwrite("abcdefg",7,1,fp);
printf("current fp=%d\n",(int)ftell(fp));
rewind(fp);
printf("after fp=%d\n",(int)ftell(fp));
return 0;
}
int ferror(FILE *stream);
int feof(FILE *stream);
- ferror() 返回 1 表示流出错;否则返回 0
- feof() 返回 1 表示文件已到末尾;否则返回 0
6. 格式化输入输出
格式化输出
int printf(const char *fmt, …);
int fprintf(FILE *stream, const char *fmt, …);
int sprintf(char *s, const char *fmt, …);
- 成功时返回输出的字符个数;出错时返回 EOF
#include<stdio.h>
int main(){
FILE *fp;
size_t ret;
int year = 2025;
int month = 8;
int day = 1;
fp = fopen("1.txt","w");
if(fp == NULL){
perror("fopen");
return 0;
}
ret = fprintf(fp,"%d-%d-%d",year,month,day);
if(ret == EOF){
perror("fprintf");
}
fclose(fp);
return 0;
#include<stdio.h>
int main(){
char buff[20]={0};
size_t ret;
int year = 2025;
int month = 8;
int day = 1;
ret = sprintf(buff,"%d-%d-%d",year,month,day);
if(ret == -1){
perror("sprintf");
}
printf("%ld %s",ret,buff);
return 0;
}
格式化输入
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
- 成功时返回输出的字符个数;出错时返回 EOF
#include<stdio.h>
int main(){
FILE *fp;
int year, month, day;
fp = fopen("1.txt","r");
if(fp == NULL){
perror("fopen");
return 0;
}
fscanf(fp,"%d-%d-%d",&year,&month,&day);
printf("%d-%d-%d\n",year, month, day);
fclose(fp);
return 0;
}
#include<stdio.h>
int main(){
char buff[20]={0};
size_t ret;
int year = 2025;
int month = 8;
int day = 1;
int syear, smonth, sday;
ret = sprintf(buff,"%d-%d-%d",year,month,day);
if(ret == -1){
perror("sprintf");
}
printf("%ld %s\n",ret,buff);
sscanf(buff,"%d-%d-%d",&syear,&smonth,&sday);
printf("%d-%d-%d\n",syear,smonth,sday);
return 0;
}