LINUX
中一切皆文件。
有
7
种类型的文件 :
1、
常规文件
( -
日常用到最多
);
1) ASCII码文件;
2) 二进制的文件;
2、
目录
( d
文件夹;目录下创建子目录
/
文件
);
3、
字符设备
(c
设备文件;代表一个具体设备 按键
i2c);
4、
块设备
(b
设备文件;磁盘
u
盘
);
5、
有名管道
(p
进程间通信
);
6、
套接口
(s
网络通信
);
7、
符号连接
(
类似
windows
快捷方式
);
![](https://img-blog.csdnimg.cn/2522bc562afe43868730ec9d0359995c.png)
LINUX 中设备分为字符设备,块设备,网络设备三种;设备节点(设备加载了设备驱动后,在/dev 目录下就会有一个文件和对应)。
1.标准 IO
在标准
I/O
中,流用
FILE *
来描述。
标准
IO
有缓冲
(
在系统提供的
C
库函数的缓存
),
分为
全缓冲,行缓冲和无缓冲 。
![](https://img-blog.csdnimg.cn/e1e0694584a3445fae47f4f6d8ffc32c.png)
最里面是
Unix
内核,在它之上是
Unix
的系统调用,各种库文件是建立再系统调用之上的,可以由用户程序(application
)调用,
Shell
是一个比较特殊的应用程序(
application
),它提供了运行其他用户程序的接口。用户程序(application
)可以在
Shell
上运行,可以调用
Library
运行,也可以直接调用
system calls 运行。
标准
IO
通过内部缓冲机制减少系统调用,
实现更高的效率。
标准IO
的所有操作都是围绕“流”来进行。流负责操作缓冲区,
输入输出是操作的实现,流操作是通过文件指针来操作。
流(
stream
)
定义:所有的
I/O
操作仅是数据简单的从程序移进或者移出,这种字节流,就称为流。
分类:文本流
/
二进制流。
流的特点
:
有序连续
,
具有方向性。
标准IO/缓冲IO
1)
不带缓区
标准错误输出 stderr
流不缓冲,只要有数据就刷新;
例如在 malloc
是如果是
NULL
用
perror()
标准错误输出
p=malloc()
if(NULL==p){
perror("malloc");
}
2)
行缓存
标准输入
stdin
键盘
/
输出
stdout
流屏幕;
例如
printf
什么时候显示到屏幕上
?
行缓存:
(1).
遇到换行符
(2).
程序正常结束
(
有些异常退出不刷新缓存
)
(3).
刷新
fflush()
强制
3)
全缓存
读写磁盘文件
全缓存呢?
(1)
当缓存区满
(2)
程序正常结束
(3)
刷新
fflush()
要求填满整个缓冲区后才进行
I/O
系统调用操作。对于磁盘文件的操作通常使用全缓冲的方式访问。第一次执行 I/O
操作时,
ANSI
标准的文件管理函数通过调用
malloc
函数获得需要使用的缓冲区,默认大小为
8192
。
![](https://img-blog.csdnimg.cn/6aaf56e2144d46299ae5a1c2ee0c7a1c.png)
非缓冲IO/文件IO
系统不自动开辟确定大小的缓冲区,而是由程序为每个文件设定缓冲区。
修改缓冲区类型和大小可调用下面的函数。
![](https://img-blog.csdnimg.cn/1d5d6756e12a4668959422d97e9f99a0.png)
标准 IO 文件操作
打开文件
在标准
IO
编程中,对文件操作之前必须先打开文件,打开文件用:
fopen()
FILE *fopen(const char *path, const char *mode);
path:要打开的文件路径
made:打开的方式
r
只读
r+
可读写
,
文件必须存在
w 只写
若文件存在清空再写
若文件不存在创建新文件
w+
读写
a
只写
若文件存在末尾追加写
若文件不存在创建新文件
a+
读写
一次读一个字符
int getchar(void)
;
//默认从标准输入读数据
int getc(FILE *stream);
//从指定的文件中读数据 类似
fgetc
用宏来实现
int fgetc(FILE *stream);
//从指定的文件中读数据
fgetc 从
fopen
返回的
stream
读一个字符
,
返回
unsigned char
并转换
int
当读到文件末尾或出错返回 EOF
函数 getchar()
等同于
getc(stdin)
读一个
ch=fgetc(fp);printf("%c",ch);
我想多读几个怎么办?
以上有局限性不知道要读多少
主要要理解文件指针的移动
用 getchar
从键盘输入字符并打印到屏幕上
,
直到输入
#
为止
ch=getchar() !=’#’
printf(“%c”,ch);
一次写一个字符
int putchar(int c);
//默认向标准输出写数据
int putc(int c, FILE *stream);
//向指定的文件中写数据
int fputc(int c, FILE *stream);
//向指定的文件中写数据
函数 putchar(c)
等价于
putc(c,stdout)
。
返回EOF
表示出错了
,
返回
unsigned int
表示写的字符
以上是一次读写一个字符,
但是当文件很大一次读一个字符效率太低
#include <stdio.h>
#include <stdlib.h>
/*********************
* 按字符copy文件
* xurx
* 2022/10/12
**********************/
int main(int argc,char ** argv){
char ch = 0;
if(argc != 3){
printf("输入参数不正确\n");
exit(1);
}
FILE * fb = fopen(argv[1], "r");
if(fb == NULL){
printf("打开文件失败\n");
exit(1);
}
FILE * fw = fopen(argv[2], "w");
while((ch =fgetc(fb)) != EOF){
fputc(ch, fw);
}
fclose(fb);
fclose(fw);
printf("文件copy成功\n");
return 0;
}
一次读写一行
char *gets(char *s);
//默认从键盘读数据,不读换行符
'\n'
int puts(const char *s);
//默认向屏幕输出数据 自带换行符
char *fgets(char *s, int size, FILE *stream);
//从
stream
指定的文件中读数据
int fputs(const char *s, FILE *stream);
//向
stream
指定的文件中写数据
从 stream
流读最多
size
大小的
char
读到哪里
?
读到 s
里面去
,
当读到
EOF
或换行符
\n
结束;
把读到新行保存到 s
中并且添加
'\0';
返回值,读完或出错返回 NULL;
一次读一行 stream-->size-->s;
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
/*********************
* 按行copy文件
* xurx
* 2022/10/12
**********************/
#define CACHE_SIZE 256
int main(int argc,char ** argv){
char ca[CACHE_SIZE] = {0};
if(argc != 3){
printf("输入参数不正确\n");
exit(1);
}
FILE * fb = fopen(argv[1], "r");
if(fb == NULL){
printf("打开文件失败\n");
exit(1);
}
FILE * fw = fopen(argv[2], "w");
while(fgets(ca, CACHE_SIZE, fb) != NULL)
{
fputs((char *)ca,fw); //返回非负的值表示成功
bzero(ca, CACHE_SIZE);
}
printf("文件copy成功\n");
return 0;
}
工程问题
:
防止数组的下标越界 确定数组最后一个字符一定为
'\0';
char buf[SIZE];
bzero(buf,SIZE);//写
0
清空
fgets(buf,SIZE-1,fp)
buf[SIZE-1]='\0'
gets
的使用
,
编译提示超出缓冲区会导致栈溢出
,
有局限性尽量不使用。
小结
:
gets 读一行字符串
,
不包含
\n,
不能控制缓冲区溢出;
fgets 读一行字符串
,
包含
\n,
可控制缓冲区溢出;
puts 打印一行字符串
,
自带换行符;
fputs 打印一行字符串
,
不带换行符
,
文件流可输出多行字符串;
直接读写
![](https://img-blog.csdnimg.cn/a0cb335dec0f4efca20af49078ca6125.png)
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
/*********************
* 按区块copy文件
* xurx
* 2022/10/12
**********************/
#define CACHE_SIZE 64
int main(int argc,char ** argv){
if(argc != 3){
printf("输入参数不正确\n");
exit(1);
}
char ca[CACHE_SIZE] = {'\0'};
FILE * fb = fopen(argv[1], "r");
if(fb == NULL){
printf("打开文件失败\n");
exit(1);
}
FILE * fw = fopen(argv[2], "w");
while(!feof(fb) && !ferror(fb))
{
bzero(ca, CACHE_SIZE);
fread((char *)ca, 1, CACHE_SIZE-1, fb);
ca[CACHE_SIZE-1] = '\0';
int b = strlen(ca);
if(b>0)
fwrite((char *)ca, 1, b, fw);
}
printf("文件copy成功\n");
return 0;
}
用 fwrite 写
int a=100
到文件
size=4(sizeof(int)) nmemb=1
./7_fwrite a.dat
hexdump a.dat
0000000 0064 0000
练习
:
写
char
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
FILE *fp;
int a=100;
char b='a';
char c='b';
if(argc!=2){
printf("usage: %s <filename>\n", argv[0]);
exit(1);
}
fp = fopen(argv[1], "w");
if(NULL == fp) {
perror("fopen fail");
exit(1);
}
fwrite(&a,sizeof(a),1,fp);
fwrite(&b,sizeof(b),1,fp);
fwrite(&c,sizeof(c),1,fp);
return 0;
}
$ hexdump b.dat
0000000 0064 0000 6261
0000006
feof()和 ferror()
可适用于
ASCII
文件和二进制文件
如果读写多次
,
循环多久不做
?
返回值
读/
写成功返回读写成功的数量
到文件末尾还是出错需要 feof
或
ferror
来决定
feof 返回值 如果文件结束返回非
0
值否则返回
0
ferror 返回值 未出错返回
0
值
,
否则返回非
0
出错
常见的用法:
int ch;
while (!feof(fp) && !ferror(fp)) { //feof(fp)
返回
0
没结束
ferror(fp)
返回
0
没出错
ch = fgetc(fp);
}
关闭文件
关闭流
清空缓冲区
释放缓存空间
标准 IO 小结
缓冲区
1.无缓冲,perror stderr;
2.行缓冲,换行符,
正常结束
,fflush;
3.全缓冲,缓冲满,正常结束
,fflush;
读写
1. 一次读写一个字符
fgetc getc getchar fputc putc putchar;
结束 EOF (-1);
2. 一次读写一行
fgets gets fputs puts
结束
NULL;
3. 一次读写数据块
fread fwrite
结束
feof ferror;
4. 打开关闭
open fclose;
第 1,2
种适合
ascii
文本文件 第
3
种适合 文本文件和二进制;
格式化标准输出
fprintf
向文件写数据
int printf(const char *format, ...);
//默认向标准输出写数据
int fprintf(FILE *stream, const char *format, ...);
//向
stream
指向的文件中写数据
//const char *format 是常量只读 防止字符串被篡改
sprintf
向内存中写数据
int sprintf(char *str, const char *format, ...);
//向内存中写数据
//方向:format格式化字符串-->str (
内存
)
int sprintf(char *str, const char *format, ...);
//向内存中写数据
int snprintf(char *str, size_t size, const char *format, ...);
//向内存中写数据
,size
防止溢出指定
把各种不同的类型转换为字符串类型
格式化标准输入
fscanf 从文件读数据
int scanf(const char *format, ...);
//默认从键盘获取数据
int fscanf(FILE *stream, const char *format, ...);
//从
stream
指向的文件中获取数据
int sscanf(const char *str, const char *format, ...);
//从内存中获取数据
ftell 和 fseek
获取文件指针所在的位置
不会调整文件指针
设置文件指针的位置
fseek 设置文件指针的位置 offset whence
whence:
SEEK_SET
文件起始位置
SEEK_CUR 文件当前位置
SEEK_END 文件末尾
为正向后移动
为负向前移动
返回值:
成功返回文件位置
失败返回-1 设置 errno
实现空洞文件
空洞文件的好处是:空洞文件对多线程共同创作文件是很有用的。因为我们在创建一个很大文件的时候,我们就把一个文件分成很多的段,然后采用多线程的方式,让每个线程负责写入其中的某一段的数据。这样的话比我们用单个线程写入是快很多的。