目录
一、Linux系统介绍
linux本质上是个软件,linux系统分了内核空间和用户空间,如果要使用到linux系统的功能需要通过系统调用来使用到,系统调用就是指linux系统本身提供的函数

linux系统下,实现一个用户的应用程序:
两条路径:
1.直接通过系统调用直接实现
2.通过库的方式实现---函数库---一般是别人基于linux系统调用实现的功能更丰富使用更方便的函数
下面讲解linux本身提供的函数:标准IO
二、标准IO
首先介绍文件---一组相关信息的集合
linux将很多东西都抽象成文件
1.文件信息介绍:
- 普通文件(常规文件) 例:1.txt 图片 视频 音频 可执行文件ls -l 1.txt
d 目录文件(directory) 例:文件夹 目录 ls -l 目录
c 字符设备文件(char) 例:某一个硬件---鼠标,键盘,摄像头等 ls -l/dev/input
b 块设备文件(block) 例:硬盘等存储设备---512Bytes ls -l /dev/sda
l 软连接文件(link) 例:相当于Windows下的快捷方式 ls -l /dev/lib //浅蓝色
s 套接字文件 例:主要指的是本地socket---进程间通信
p 管道文件(pipe) 例:进程间通信 mkfifo pipe
ls -l pipe
2.打开文件:
FILE *fopen(const char *pathname, const char *mode);
参数说明:
pathname : 文件路径(相对或绝对)
mode : 打开模式
模式 说明 文件不存在 文件存在
"r" //只读 失败 从头读
"r+" //读写 失败 从头读写
"w" //只写 创建 清空内容"w+" //读写 创建 清空内容
"a" //追加 创建 追加到末尾
"a+" //读写 创建 追加,可读任意位置返回值:
成功: 返回 FILE * 指针
失败: 返回 NULL
示例代码:
FILE *fp = fopen("1.txt", "r");//在当前目录打开一个1.txt文件
if (fp == NULL) {
perror("fopen fail"); // 打印错误信息
return -1;
}
简要介绍一下流的概念:

操作系统默认打开了三个流:
stdin //标准输入 //FILE*指针 //获取键盘输入的值
键盘上ctrl+d可以产生一个EOF
stdout //标准输出,输出到终端
stderr //标准出错//也是个输出
3.关闭文件:
int fclose(FILE *stream); //fclose(fp);
作用: 刷新缓冲区并关闭文件
返回值: 成功返回 0,失败返回 EOF(-1)
注意: 必须关闭文件,否则数据可能丢失!
这里介绍一下perror和printf的区别:

注意看缓冲区,主要是这里的区别!
一个是无缓冲,一个是行缓冲,不懂就去了解下面的缓冲区机制
4.按字符读写:
int fgetc(FILE *stream); //读
int fputc(int c, FILE *stream); //写
功能:从流中获得数据(一次只能读取一个字符)
返回值:
成功:返回读取到的字符的数值
失败:返回EOF(-1)当到达文件结尾也会返回EOF
练习:写一个cat和cp函数:
cat函数:
#include <stdio.h>
// ./a.out 1.txt
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)
{
//printf("fopen fail!\n");
perror("fopen fail");
return -1;
}
//2.读取
int ret = 0;
while ((ret = fgetc(fp)) != EOF)
{
//printf("%c",ret);
//printf("%c",ret);
fputc(ret,stdout);
}
//3.关闭
fclose(fp);
//fclose(fp);
return 0;
}
cp函数:
#include <stdio.h>
//./a.out 1.txt 2.txt
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("Usage: %s <src> <dest>\n",argv[0]);
return -1;
}
//1.打开
FILE *fp_s = fopen(argv[1],"r");
FILE *fp_d = fopen(argv[2],"w");
if (fp_s == NULL || fp_d == NULL)
{
perror("fopen fail ");
return -1;
}
//2.读写
int ret = 0;
while ((ret = fgetc(fp_s)) != EOF)
{
fputc(ret,fp_d);
}
//3.关闭
fclose(fp_s);
fclose(fp_d);
return 0;
}
5.按字符串读写:
char *fgets(char *s, int size, FILE *stream); //读
重要特性:
最多读取 size - 1 个字符
遇到换行符 \n 会停止,换行符也会被读入被保存到s指定的空间
自动在末尾添加 \0返回值: 成功返回 s ,失败/EOF 返回 NULL
int fputs(const char *s, FILE *stream); //写将s中的字符串输出到流中
不会自动添加换行符
返回值: 成功返回非负数,失败返回-1
练习:写一个cp函数和cat函数,和记录文件行数的函数
cp函数:
#include <stdio.h>
void do_copy(FILE *fp_s,FILE *fp_d)
{
//2.读写
char s[1024] = {0};
while (fgets(s,sizeof(s),fp_s) != NULL)
{
fputs(s,fp_d);
}
}
//./a.out 1.txt 2.txt
int main(int argc, const char *argv[])
{
if (argc != 3)
{
printf("Usage: %s <src> <dest>\n",argv[0]);
return -1;
}
//1.打开
FILE *fp_s = fopen(argv[1],"r");
FILE *fp_d = fopen(argv[2],"w");
if (fp_s == NULL || fp_d == NULL)
{
perror("fopen fail ");
return -1;
}
//2.拷贝
do_copy(fp_s,fp_d);
//3.关闭
fclose(fp_s);
fclose(fp_d);
return 0;
}
cat函数:
#include <stdio.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usege : %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fail");
return -1;
}
char ret[100] ={0};
while(fgets(ret,sizeof(ret),fp)!=NULL)
{
printf("%s",ret);
}
fclose(fp);
return 0;
}
记录文件行数:
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usege : %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fail");
return -1;
}
char ret[100] ={0};
int cnt = 0;
while(fgets(ret,sizeof(ret),fp)!=NULL)
{
if(ret[strlen(ret)-1] == '\n')
{
++cnt;
}
}
printf("%s has %d line\n",argv[1],cnt);
fclose(fp);
return 0;
}
注意:
fputs 如果要输出的数据中没有换行,它不会自己输出换行
fputs要输出的是字符串,遇到\0结束
puts默认会输出换行
fgetc和fputc拷贝普通文件和二进制都没问题,但是文件大时,效率不高fgets和fputs拷贝普通文件没问题,二进制有问题(\0)
6.按对象读写(二进制流读写)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); //读
参数说明:
ptr : 接收数据的缓冲区 //char s[100]
size : 每个数据项的大小(字节) //一般是sizeof(char)
nmemb : 要读取的数据项个数 //一般是sizeof(s)
返回值: 实际读取的数据项个数
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); //写ptr : 接收数据的缓冲区 //char s[100]
size : 每个数据项的大小(字节) //一般是sizeof(char)
nmemb : 要写入的数据项个数 //一般是sizeof(s)
返回值: 实际读取的数据项个数
用途:在结构体上使用这个函数读写会更便利:
例如:
cs
typedef struct {
char name[20];
int sno;
float score;
} Student;
Student students[3] = {
{"Tom", 110, 99.5},
{"Jerry", 111, 99.9},
{"Jack", 112, 100}
};
// 写入 3 个学生记录
fwrite(students, sizeof(Student), 3, fp);
// 读取 1 个学生记录
Student s;
fread(&s, sizeof(Student), 1, fp);
练习:写一个cp函数和cat函数:
cat函数:
注意,如果用printf来输出到终端的话,需要加一个'/0',且读取元素的个数需要时sizeof(buf)-1,因为该函数不会自己补'\0',且会把缓冲区直接读满,所以需要-1给数组留一个位置加一个'\0'进去
cs
#include <stdio.h>
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usege : %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fail");
return -1;
}
int ret = 0;
char buf[100];
while((ret = fread(buf,sizeof(char),sizeof(buf),fp)) != 0)
{
// buf[ret] ='\0';
// printf("%s",buf);
fwrite(buf,sizeof(char),ret,stdout);
}
fclose(fp);
return 0;
}
cp函数:
cs
#include <stdio.h>
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf("Usege : %s <filename> <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fail");
return -1;
}
int ret =0;
FILE *fp2 = fopen(argv[2],"w");
if(fp2 == NULL)
{
perror("fail");
return -1;
}
char s[100]={0};
while((ret = fread(s,sizeof(char),sizeof(s),fp))!= 0)
{
fwrite(s,sizeof(char),ret,fp2);
}
fclose(fp);
fclose(fp2);
return 0;
}
7.文件定位:
移动文件位置指针
int fseek(FILE *stream, long offset, int whence);
参数说明:
offset : 偏移量(可正可负)
whence : 起始位置
SEEK_SET //文件开头
SEEK_CUR //当前位置
SEEK_END //文件末尾返回值: 成功返回 0,失败返回 -1
cs
// 移动到文件开头
fseek(fp, 0, SEEK_SET);
// 移动到文件末尾
fseek(fp, 0, SEEK_END);
// 向前移动 10 字节
fseek(fp, -10, SEEK_CUR);
// 移动到第 100 个字节
fseek(fp, 100, SEEK_SET);
获取文件位置指示器
long ftell(FILE *stream);
返回值: 当前位置相对于文件开头的偏移量
cs
//示例
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
printf("文件大小: %ld 字节\n", file_size);
回到文件开头
void rewind(FILE *stream);
等价于fseek(fp, 0, SEEK_SET)
8.格式化输入输出
int printf( const char *format, ...);//格式化输出到终端
int fprintf(FILE *stream, const char *format, ...);//格式化输出到文件
int sprintf(char *str, const char *format, ...);//格式化输出到数组
示例:
cs
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("%d:hello\n",2026);
fprintf(stdout,"%d:hello\n",2026);
char s[100];
sprintf(s,"%d:hello\n",2026);
printf("%s",s);
return 0;
}
讲解一下缓冲区机制:
行缓存---一般与终端关联---1024字节
特点(什么时候会被刷新?)
1.遇到/n
2.缓冲区满
3.程序正常退出(1.从main函数返回 2.exit函数)
4.fflush函数 //强制刷新
全缓存 ---与普通文件关联---4096字节特点(什么时候会被刷新?)
1.遇到/n
2.缓冲区满
3.程序正常退出(1.从main函数返回 2.exit函数)
4.fflush函数 //强制刷新
不缓存 ---错误信息的输出设计为不缓存
注意:
这里的fprintf就是写入文件就是全缓存,必须使用fflush(fp)冲刷才可以写入,不然没有输出结果
printf如果看不到结果也可能是因为程序没结束,或者没加\n
int fflush(FILE *stream); //冲刷缓冲区
csprintf("加载中..."); fflush(stdout); // 立即显示,不等换行符
查看缓冲区大小:
cs
// 需要先触发缓冲区分配(如调用 getchar())
printf("stdin 缓冲区: %ld 字节\n",
stdin->_IO_buf_end - stdin->_IO_buf_base);
printf("stdout 缓冲区: %ld 字节\n",
stdout->_IO_buf_end - stdout->_IO_buf_base);
printf("stderr 缓冲区: %ld 字节\n",
stderr->_IO_buf_end - stderr->_IO_buf_base);
9.错误处理
int feof(FILE *stream); //检测文件结束 文件指针达到EOF返回真
int ferror(FILE *stream); //检测错误 文件指针不在EOF或者出错则返回真
void perror(const char *s); //打印错误信息
三、总结
字符fgetc() 、fputc() 效率低,简单
字符串fgets() 、fputs() 按行处理
二进制块fread() 、fwrite() 效率高,通用
要理解的概念:1. FILE 结构体: 封装了文件描述符和缓冲区
2. 系统函数的功能和格式
3. 缓冲区机制: 提高 I/O 效率的关键
4. 文件指针: 记录当前读写位置