一、基本概念
文件:
一组相关数据的集合
文件名:
01.sh //文件名
流:
流是指数据在程序或系统中的流动方式。在Linux中,数据流通常通过文件描述符(File Descriptors)进行管理。文件描述符是一个非负整数,用于标识打开的文件或输入/输出资源。
二、linux下的文件类型
b block 块设备文件 eg: 硬盘
c character 字符设备文件 eg: 鼠标,键盘
d directory 目录文件 eg: 文件夹
- regular 常规文件 eg: 1.txt 01.sh xx.bmp
l symblink 软链接 eg: 快捷方式
s socket 套接字 eg: 网络通信用
p pipe 管道 eg: 进程间通信
三、思想
linux中一个核心设计思想
everything is file!
//一切皆文件
四、 linux操作文件的函数
两套函数 (两套操作路径
![](https://i-blog.csdnimg.cn/direct/f1bf3289b9144dcfa93015da7c701869.png)
(一)库函数
--- 第三方提供的函数
集成了很多功能函数
//标准输入输出
//数学函数
(二)系统调用
--- 内核提供的函数
(三)标准io的概念
1975 Dennis r IO库,
从C语言的标准,ANSI c
IO input output
I: 键盘是标准输入设备 ====》默认输入就是指键盘 /dev/input
O: 显示器是标准输出设备 ==》默认输出就是指显示器
Linux操作系统当中IO都是对文件的操作
C一部分,任何支持标准C的系统都可使用标准IO实现文件存储
标准IO在UNIX上是对文件IO的封装
一般都是对普通文件操作是一种有缓存的IO 在文件IO和用户程序之间,
加入缓冲区,可以有效减少系统调用的次数,节省系统IO调度资源
说明:
标准IO库,不单单是linux上有,在windows,Mac os上都有。
很多操作系统都实现了标准IO库。
都是依据IOS C标准实现的。
所以基本保证了可移植性。
但是因为标准和具体实现之间的差异,
未必敢保证所有的函数在都可以相互通用。
标准IO都干了些啥?
标准IO处理了很多细节:
(1).处理缓冲区分配 (缓存--提高效率 --- 慢速 快速)
(2).读写IO的块长度的优化
(3).对系统调用进行了封装,内部对应的"文件描述符"
好处:
用户使用方便,不必再担心如何选择正确的块长度。
//地位:
标准I/O库是由 Dennis Ritchie在1975年左右编写的。
它是Mike Lesk编写的可移植I/O库的主要修改版本。
令人惊讶的是,50年来,几乎没有对标准I/O库进行修改。
五、函数接口
(一)fopen
FILE * fopen(const char *pathname,const char *mode);
功能:
流打开函数
参数:
@pathname --- 要打开的文件名 //字符串形式的名字
@mode --- 打开的模式
r --- 只读
r+ --- 读写
说明: 带r的 要求,文件必须存在 ,如果不存在,就会报错
w --- 只写
w+ --- 读写
说明:
文件存在 ,截断成 0长度
不存在,则创建
a --- 追加(写)
a+ --- 读写
说明:
读 从头读
写 从末尾开始写
文件存在 则打开做写操作即可
文件不存在 创建文件
返回值:
成功 FILE*指针 //文件指针 流指针 --- 从程序上讲,FILE *指针就代表打开的这个文件
失败 NULL 同时 errno会被设置
#include<stdio.h>
#include<errno.h>
int main(int argc, const char *argv[])
{
if (argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"w+");
// FILE *fp = fopen(argv[1],"r+");
if(fp == NULL)
{
printf("fopen fail\n");
return -1;
}
// printf ("fopen success!\n");
return 0;
}
(二)perror
void perror(const char *s);
#include<stdio.h>
#include<errno.h>
int main(int argc, const char *argv[])
{
if (argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen fail");
return -1;
}
printf ("fopen success!\n");
return 0;
}
(三)fgetc 、fputc
1、 int fgetc(FILE * stream);
功能:
从文件中读取字符
参数:
@stream 表示要读取的文件对应的流指针
返回值:
成功 对应字符的ASCII码值
失败 EOF 到达文件结尾
出错
2、int fputc(int c,FILE *stream);
功能:
将一个字符输出到指定的流(文件)
参数:
@c //要输出的字符 ---ascii码值
@stream //指定的文件
返回值:
成功 被写入的字符的ASCII码值
失败 EOF
问题:
EOF
文件中有没有 EOF? //只是到达结尾时的一个标志
#include<stdio.h>
#include<errno.h>
//类似cat功能
int main(int argc, const char *argv[])
{
//处理命令行参数
if (argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
//1.打开文件
FILE *fp = fopen(argv[1],"r+");
if(fp == NULL)
{
perror("fopen fail");
return -1;
}
//2.读写数据
int ret;
while((ret = fgetc(fp)) != EOF)
{
fputc(ret,stdout);
//printf("%c",ret);
}
//3.关闭
fclose(fp);
return 0;
}
系统默认打开的流指针:
stdin //标准输入
stdout //标准输出
stderr //标准出错 --- 屏幕 --- 可以专门把错误信息输出到 stderr
#include<stdio.h>
int main(int argc, const char *argv[])
{
int ret;
while ( (ret = fgetc(stdin)) != EOF)
fputc(ret,stdout);
return 0;
}
(四)fclose
int fclose(FILE *stream);
功能:
关闭文件
刷新流
关闭了底层文件描述符
返回值:
成功 0
失败 EOF
注意,不要多次重复关闭
(五)fgets、fputs
1、char * fgets(char*s,int size,FILE*stream)
功能:
从stream中读取size-1个字符,到s指向的空间
参数:
@s //存放读取到的数据 对应的内存空间
@size //指定一次最多读取多少个字符
@stream //表示要读取的文件
返回值:
成功 返回s
失败 NULL
读到文件结尾 也 返回NULL
注意:
fgets读取结束:
1. EOF
2. '\n'
3. size-1
练习:
统计文件行数
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r+");
if(fp == NULL)
{
perror("fopen fail");
return -1;
}
char buf[1024];
int n = 0;
while (fgets(buf,sizeof(buf),fp))
{
if(buf[strlen(buf) - 1] == '\n')
{
n++;
}
}
printf("%d\n",n);
fclose(fp);
return 0;
}
2、int fputs(const char *s, FILE *stream);
功能:
输出s到stream中
参数:
@s 代表要输出的字符串
@stream 代表 输出到的文件
返回值:
成功 非负数
失败 EOF
注意:
不会将 '\0' 输出
练习:
cat //fgets实现cat
#include<stdio.h>
#include<errno.h>
//类似cat功能
int main(int argc, const char *argv[])
{
//处理命令行参数
if (argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
//1.打开文件
FILE *fp = fopen(argv[1],"r+");
if(fp == NULL)
{
perror("fopen fail");
return -1;
}
//2.读写数据
int ret;
while((ret = fgetc(fp)) != EOF)
{
fputc(ret,stdout);
//printf("%c",ret);
}
//3.关闭
fclose(fp);
return 0;
}
cp_fputsfgets.c
#include<stdio.h>
#include<errno.h>
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
FILE *srt = fopen(argv[2],"w");
if(fp == NULL || srt == NULL)
{
printf("fopen fail\n");
return -1;
}
char buf[1024];
while(fgets(buf,1024,fp) != NULL)
{
fputs(buf,srt);
}
fclose(fp);
fclose(srt);
return 0;
}
"123" --- 文本文件 --- '1''2''3' //每个数据是按照固定的编码格式存放的 -ASCII
123 --- 二进制 --- 01111011 //
linux下操作时 ,没有区别
windows下区别
说明:
1.fgets 和 fputs 在拷贝 二进制文件时 可能出问题
(六)fread / fwrite
fread/fwrite //二进制读写函数 按对象读写
1、size_t fread( void *ptr, size_t size, size_t nmemb, FILE *stream);
2、size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:
二进制流的读/写
参数:
@ptr 表示存放数据的空间的地址
@size 要操作的单个元素(对象)的大小
@nmemb 要操作的元素(对象)的个数
@stream 要操作的文件
返回值:
成功 返回的是 成功操作到的对象的个数
失败 0
到达文件结尾 返回 0
应用:
主要 应用在 文件中 有固定格式的 场景
#include<stdio.h>
int main(int argc, const char *argv[])
{
char buf[10];
int ret = fread(buf,sizeof(char),10,stdin);
printf("ret = %d\n",ret);
fwrite(buf,sizeof(char),ret,stdout);
putchar('\n');
return 0;
}
练习:
将3个学生的信息,写到文件中
之后,
从文件中读出学生信息,打印
#include<stdio.h>
typedef struct stu
{
char name[20];
int sno;
float score;
}stu_t;
int main(int argc, const char *argv[])
{
stu_t s[3] = {{"tom",110,99},
{"liam",111,98},
{"beta",112,99}};
FILE *fp = fopen("stu.txt","w+");
if(fp == NULL)
{
perror("fopen fail");
return -1;
}
fwrite(s,sizeof(stu_t),3,fp);
rewind(fp);
stu_t s1[3];
fread(s1,sizeof(stu_t),3,fp);
int i = 0;
for(i = 0;i < 3;i++)
{
printf("name = %s\n",s1[i].name);
printf("sno = %d\n",s1[i].sno);
printf("score = %.2f\n",s1[i].score);
}
fclose(fp);
return 0;
}
cat_fread_fwrite.c
#include<stdio.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usag: %s <filename>\n",argv[0]);
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen fail");
return -1;
}
char buf[1024];
int ret = 0;
while(ret =fread(buf,sizeof(char),1024,fp))
fwrite(buf,sizeof(char),ret,stdout);
fclose(fp);
putchar('\n');
return 0;
}
(七)文件偏移量 rewind
int fseek(FILE* stream,long offset,int whence);
功能:
定位文件
参数:
@stream 表示要操作文件
@offset 表示文件偏移量
@whence 定位的参考点
SEEK_SET //相对于文件开头的
offset >=0
SEEK_CUR //相对于当前位置
offset>=0
offset<0 //不能超过这个文件开头
SEEK_END //相对于文件末尾
offset < 0 //不能超过这个文件开头
offset >= 0 //可以 --- 创建 空洞 文件
返回值:
成功 0
失败 -1
fseek(fp,100,SEEK_SET);//
fseek(fp,0,SEEK_SET);//定位到开头
fseek(fp,0,SEEK_END);//定位到末尾
应用:
创建空洞文件:
1.做偏移
2.写操作
#include<stdio.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("file fail");
return -1;
}
if(fseek(fp,100,SEEK_END) != 0)
{
perror("fseek fail");
return -1;
}
fputc('A',fp);
fclose(fp);
return 0;
}
(八) long ftell(FILE*stream);
功能:
获得当前文件的偏移量
void rewind(FILE*stream);
功能:
将文件偏移量设置到文件开头
获得某个文件的大小:
1. fseek(fp,0,SEEK_END);//定位到末尾
2. long len =ftell(fp);
实现 cp功能
#include<stdio.h>
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf("Usage: %s <src> <dest>\n",argv[0]);
return -1;
}
FILE *fp1 = fopen(argv[1],"r");
if(fp1 == NULL)
{
perror("file fail");
return -1;
}
fseek(fp1,0,SEEK_END);
long len = ftell(fp1);
FILE *fp2 = fopen(argv[2],"w");
if(fp2 ==NULL)
{
perror("file fail");
return -1;
}
fseek(fp2,len-1,SEEK_SET);
fputc('\0',fp2);
rewind(fp1);
rewind(fp2);
char buf[len];
fread(buf,sizeof(buf),1,fp1);
fwrite(buf,sizeof(buf),1,fp2);
fclose(fp1);
fclose(fp2);
return 0;
}
六、缓存
缓存 设计的目的提高效率
本质上来说 ---缓存其实就是一块内存空间
行缓冲,1k, terminal,主要用于人机交互stdout
缓存区满或者遇到\n刷新 1024
行缓存多是关于终端的一些操作
1.遇到\n刷新
2.缓存区满刷新
3.程序结束刷新
4.fflush刷新 fflush(stdout);
全缓冲,4k,主要用于文件的读写
缓存区满刷新缓存区 4096
对普通文件进行标准IO操作,建立
的缓存一般为全缓存
刷新条件:
1.缓存区满刷新
2.程序结束刷新
3.fflush来刷新 fflush(fp);
无缓冲,0k 主要用于出错处理信息的输出 stderr
不对数据缓存直接刷新
printf();==>>stdout
fprintf(strerr,"fopen error %s",filename);
界面交互 出错处理
./a.out < main.c //< 表示输入重定向
./a.out > main.c //> 表示输出重定向