作者:along
时间:2020年12月30日14:12:10
1、标准io
1.1 概念
(1)文件分类:
文件类型 |
描述 |
普通文件 |
也称磁盘文件,可以随机存储二进制数据,fseek可以自由定位某个位置。 |
目录文件 |
即文件夹。 |
管道文件 |
从一端发送数据,另一端可以接受数据,可用于进程之间的通信。 |
设备文件 |
有两种类型:字符型设备和块设备。其中字符型设备一次只能读出或写入一个字节的数据,例如调制解调器、打印机、声卡、键盘和鼠标;块设备则必须以一定的大小读出或者写入数据,包括CD-ROM、RAM驱动器和磁盘驱动器。一般的,字符设备用于传输数据,块设备用于存储数据。 |
套接字文件 |
网络编程TCP/UDP使用的套接字,也可以当做文件来处理。 |
(2)流
FILE *fp 指针流,是调用标准IO(C语言库)打开文件时,有操作系统产生的一个结构体指针。
(3)缓存
设置缓冲区之后可以减小操作系统的 开销,提高系统的运行效率 。
当我们在调用标准IO的函数的时候,操作系统从用户态切换到内核态,然后在执行相应的命令,执行完毕之后,将结果返回到用户态,这个时候又得从内核态切换到用户态,这样来回切换,加大了操作系统的开销,为了减少这种情况的发生,则设立缓冲机制,直接将所要操作的内容先读到缓冲区当中,再需要的的时候从缓冲区读到内核当中。
类型 |
描述 |
全缓冲区 |
当缓冲满 或者遇到换行符 或者程序结束或者强制性刷新缓冲区,这样全缓冲区的内容会输出 。 |
行缓冲区 |
按行进行读写操作,同样遇到换行符 或者强制性刷新缓冲区,内容会输出 。 |
不缓冲 |
比如错误码 不经过缓冲区直接打印到终端 。 |
fflush(NULL)函数将强制刷新缓存区
1.2 常用IO函数
- fopen:文件打开
- fclose:文件关闭
- fread:文件块读取
- fwrite:文件块写入
- fscanf:
- fprintf:
- fgets:文件字符串读取
- fputs:文件字符串写入
- fgetc:文件字符读取
- fputc:文件字符写入
1.3 文件打开
#include
FILE *fopen(const char *path, const char *mode);
参数名 |
描述 |
path |
文件路径和文件名 |
mode |
文件打开模式 |
打开模式 |
描述 |
r |
以读的方式来打开文件 ,从文件的起始位置操作文件 (文件必须存在) |
r+ |
以读写的方式来打开文件从文件的起始位置操作文件 同上 |
w |
以写的方式来打开文件,如果文件不存在,则创建文件,如果文件存在,清空文件 。 |
w+ |
以读写的方式来打开文件,如果文件不存在,则创建,如果文件存在,则清空文件 |
a |
追加的方式打开文件,对文件进行写操作 |
a+ |
以追加的方式打开文件,对文件进行写操作 。 |
结果 |
返回值 |
成功 |
返回指针流 |
失败 |
返回NULL,并设置全局变量errno- |
#include
int main()
{
FILE *fp = fopen("./dfa/aa", "w");
if(NULL == fp)
{
//printf("fopen is error\n");
perror("perror: fopen is error\n");
return -1;
}
else
{
printf("fopen is oK\n");
}
return 0;
}
#==================成功时
[root@liu CODE2]# ./a.out
fopen is oK
#==================失败时
[root@liu CODE2]# ./a.out
perror: fopen is error
: No such file or directory
[root@liu CODE2]#
1.4 文件关闭
#include
int fclose(FILE *fp);
结果 |
返回值 |
成功 |
返回 0 |
失败 |
返回EOF,并设置全局变量errno |
1.5 判断文件是否结束
#include
int feof(FILE *stream);
- 功能
判断文件是否结束(到文件末尾即代表结束)
- 参数
结果 |
返回值 |
文件结束 |
返回 非0值 |
文件未结束 |
返回 0 |
1.6 按块读取
#include
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 功能
从文件流中读取内容到ptr缓存区,一次性读取 nmemb 个块数据,每个块的大小为 size字节
- 参数
参数名 |
描述 |
ptr |
读取内容存放的缓存区 |
size |
读取的块的大小 |
nmenb |
读取块的个数 |
stream |
指针流 |
结果 |
返回值 |
读取成功 |
返回 读取块的个数 |
达到文件末尾 |
0值 |
读取失败 |
返回负值或者小于期望读取的块个数 |
1.7 按块读取
#include
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
- 功能
将ptr缓存区的内容写入文件流,一次性写入取 nmemb 个块数据,每个块的大小为 size字节
- 参数
参数名 |
描述 |
ptr |
写入内容存放的缓存区 |
size |
写入的块的大小 |
nmenb |
写入块的个数 |
stream |
指针流 |
结果 |
返回值 |
写入成功 |
返回 写入块的个数 |
达到文件末尾 |
0值 |
读取失败 |
返回负值或者小于期望写入的块个数 |
例子:
【以后补上】
1.8 按字节读
#include
int fgetc(FILE *stream);
结果 |
返回值 |
读取成功 |
返回强转为int类型的字符值 |
读取失败 |
-1 |
1.8 按字节读
#include
int fputc(int c, FILE *stream);
结果 |
返回值 |
写入成功 |
返回写入字节的字符值 |
读取失败 |
-1 |
#include
int main()
{
FILE *fp = fopen("aa", "r");
if(NULL == fp)
{
printf("fopen is error\n");
return -1;
}
char buf[64] = {0};
char *buf_p;
buf_p = fgets(buf, 20, fp);
printf("buf==>%s<==", buf);
fclose(fp);
return 0;
}
1.9 按行读
#include
char *fgets(char *s, int size, FILE *stream);
- 功能
从文件流stream读取大小为size字节的内容到s缓存区;如果在size范围内读到换行,那么只会读取本行内容。
- 参数
参数名 |
描述 |
s |
要读取内容的缓存区 |
size |
要读取数据的字节大小 |
stream |
文件流 |
结果 |
返回值 |
读取成功 |
读取的字符串指针 |
读取失败 |
NULL |
【以后补上】
1.10 按行写
#include
int fputs(const char *s, FILE *stream);
- 功能
向文件流stream写入s缓存区的内容
- 参数
参数名 |
描述 |
s |
要写入内容 |
stream |
文件流 |
结果 |
返回值 |
写入成功 |
非负整数 |
读取失败 |
EOF |
【以后补上】
3、静态库和动态库
3.1 简介
- 库的概念
库是功能的集合,通俗一点说,所谓一个库,就是一个文件,这个文件可以在编译时由编译器直接连接到可执行程序中,也可以在运行时由操作系统的根据需要动态加载到内存中。
- 静态库
静态的库后缀------xxxx.a 在程序进行编译阶段直接编译到程序到当中打包到可执行二进制文件当中。
- 动态库
编译程序常用的一种库 ,首先动态库集中在系统的特殊路径下面,当程序运行的时候,动态库会根据需要加载相应的动态库到内存当中 。 -------xxxxxx.so
- 动态库和静态库的差异
静态库与动态库 二者代码的载入时刻不同
- 库存在的意义
当我们在编写一个程序的时候,如果从头到尾全部靠自己写的代码来实现功能,那么代码量会非常大,如果全部程序都采用这种方法,那么一个简单的功能可能会占据很大硬盘和内存空间,这个时候就需要用库,特别是动态库,提前准备好一些动态库的集合放到操作系统的指定文件夹,当程序需要的时候动态的去这个文件夹中调用,而不是把全部代码都写在一个程序里,这样代码量就能控制在我们能接受的范围内,而静态库的会在代码编译的时候载入进去,则是方便独立程序的可移植性,看可以放到另外的环境里,也能够正常使用。
3.2 静态库的制作
例子:
(1)创建三个文件
add.c main.c 和add.h
add.c文件定义加法函数,main.c则调用该函数,内容如下:
//add.c
#include
#include "add.h"
int fun(int a, int b)
{
return (a + b);
}
//add.h
int fun(int a, int b);
//main.c
#include
#include add.h
int main()
{
printf("result=%d\n", fun(1, 2));
return 0;
}
(2)对add.c进行预处理和汇编,但是不进行链接:
gcc -c add.c
得到目标文件 add.o
(3)把add.o 制作为库文件
ar cr libadd.a add.o
libadd.a即为目标文件
- lib 静态库的前缀
- .a 静态库的后缀表示是静态库
- 其中add是静态库
(4)通过链接静态库的方式编译main.c
gcc -static main.c -L./ -ladd
-L : 加库文件的路径
-l : 制作的静态库文件
如果出现错误
[root@liu lib]# gcc -static main.c -L./-ladd
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
可以安装:
yum install -y glibc-static
3.3 动态库的制作
三个文件同上
(1)对add.c进行预处理和汇编,但是不进行链接:
gcc -c add.c
得到目标文件 add.o
(2)将add.o 制作为动态库文件
gcc -shared -fpic -o libadd.so add.o
(3)将制作好的动态库文件libadd.so放入特定的库文件夹中
cp libsdd.so /usr/lib
或者
cp libsdd.so /usr/lib64
(4)通过链接动态库的方式编译main.c
gcc -o d main.c -L./ -ladd
(5)运行
./d
2、文件IO
2.1 简介
文件IO由 POSIX 标准来说明的,其本质是一组函数接口,通过调用这组接口,来实现相应的功能。它可以对文件进行管理 ,可以操作普通文件,也可以设备文件。
文件IO 的接口属于系统调用,系统调用接口属于 linux内核本身自带 。
主要接口:
open close read write
2.2 文件描述符
- 文件IO 的操作的对象是文件描述符 。
文件描述符是一组非负整数 (0 1 2 3………) 。
- 通过文件IO,一个程序(进程)可以在运行的过程中同时打开多个文件,每个程序运行起来后,(内核)系统中就有一个记录表专门记录这个程序打开的各个文件。每打开一个文件,记录表就会用一个新的结构体变量来保存这个文件的相关信息(这个结构是看不到)。如果打开多个文件,记录表中就会有多个这样的结构体变量分别保存多个文件的相关信息,它们构成了一个结构体数组,而数组的每一个元素的下标就是所谓的文件描述符。
所以文件描述符是一个较小的非负整数(0—1023),它代表记录表的一项,即上面说的数组中的某个元素的下标。通过文件描述符和一组基于文件描述符的文件操作函数,就可以实现对文件的打开、关闭、读写、删除等操作。
- 当使用文件IO 来打开文件,同样 会自动打开三个文件描述符,0,1,2.对应的是 输入 输出 以及错误码,相当于标准的输入 标准的输出 标准的错误流 一致 。 所以一般使用文件IO打开文件,返回的文件描述符从3开始 。文件描述符 不是随机分配,是从最小且没有被占用的开始分配 。
描述符 |
用途 |
0 |
stdin输入 ,标准的输入 |
1 |
stdout 输出,标准的输出 |
2 |
stderr错误,标准的错误 |
例子:
#include
#include
#include
#include
#include
int main()
{
char buf[6] = {'0'};
//读取键盘输入的内容 到buf缓冲当中
read(0,buf,5);
// 1 输出到终端
write(1,buf,5);
printf("\r\n");
// 2 输出到终端
write(2,buf,5);
return 0;
}
2.3 文件IO和标准IO
- 标准IO 遵循ANSI c标准 ,文件IO 遵循的是POSIX 标准
- 标准IO 带缓冲区,文件IO 不带缓冲区
- 标准IO 操作的核心对象是指针流, 文件IO 操作的核心对象是文件描述符fd
- 标准IO 只能操作普通文件 文件IO可以操作普通文件还可以操作设备文件 。
- 标准IO 是属于c库,移植性强,文件IO 属于linux 内核自带的接口 。
2.4 常用函数
- open
- close
- read
- write
- lseek
- readdir
- opendir
2.5 文件打开与创建
#include
#include
#include
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
函数 |
功能 |
open |
打开文件 |
creat |
创建文件 |
参数 |
描述 |
pathname |
要打开的文件名 |
flags |
打开的方式 |
mode |
创建文件的权限 |
结果 |
返回值 |
成功 |
新的文件描述符 |
失败 |
返回-1,并设置errno |
宏 |
含义 |
O_RDONLY |
以读的方式打开 |
O_WRONLY |
以写的方式打开 |
O_RDWR |
以读写的方式打开 |
O_CREAT |
文件不存在的时候用来创建文件 |
O_EXCL |
(检测文件是否存在) 一般与O_CREAT 连用 用来判断文件是否存在,如果文件存在,则报错 。 |
O_TRUNC |
文件存在,清空文件 |
O_APPEND |
已追加的方式打开文件 |
注:可以多个一起用,例如
(O_RDONLY | O_WRONLY )
宏 |
含义 |
S_IRWXU |
00700 允许 文件 的 属主 读 , 写 和 执行 文件 |
S_IRUSR (S_IREAD) |
00400 允许 文件 的 属主 读 文件 |
S_IWUSR (S_IWRITE) |
00200 允许 文件 的 属主 写 文件 |
S_IXUSR (S_IEXEC) |
00100 允许 文件 的 属主 执行 文件 |
S_IRWXG |
00070 允许 文件 所在 的 分组 读 , 写 和 执行 文件 |
S_IRGRP |
00040 允许 文件 所在 的 分组 读 文件 |
S_IWGRP |
00020 允许 文件 所在 的 分组 写 文件 |
S_IXGRP |
00010 允许 文件 所在 的 分组 执行 文件 |
S_IRWXO |
00007 允许 其他 用户 读 , 写 和 执行 文件 |
S_IROTH |
00004 允许 其他 用户 读 文件 |
S_IWOTH |
00002 允许 其他 用户 写 文件 |
S_IXOTH |
00001 允许 其他 用户 执行 文件 |
注: 上述的参数可以多个一起 连用 : (S_IRWXU |S_IRWXG| S_IRWXO )
mode 只有 当 在 flags 中 使用 O_CREAT 时 才 有效 , 否则 被 忽略。
#include
#include
#include
#include
#include
int main()
{
int fd = open("hello.date",O_RDONLY|O_CREAT,0666);
if(fd < 0)
{
printf("open is error\r\n");
return -1;
}
return 0;
}
#include
#include
#include
#include
#include
int main()
{
int fd = open("hello.date",O_RDONLY|O_CREAT|O_EXCL,0666);
if(fd < 0)
{
printf("open is error\r\n");
return -1;
}
return 0;
}
2.6 文件的读写操作
2.6.1 读操作
#include
ssize_t read(int fd, void *buf, size_t count);
- 功能
从文件中读取count个字节到buf中
- 参数
参数名 |
含义 |
fd |
要读取文件的文件描述符 |
buf |
缓冲区的首地址 |
count |
要读取的字节数 |
结果 |
返回值 |
成功 |
写入的字节数 |
失败 |
-1,并置位errno |
2.6.2 写操作
#include
ssize_t read(int fd, void *buf, size_t count);
- 功能
向文件中写入buf缓存区count字节的数据
- 参数
参数名 |
含义 |
fd |
要写入文件的文件描述符 |
buf |
缓存区的首地址 |
count |
要写入的字节数 |
结果 |
返回值 |
成功 |
写入的字节数 |
失败 |
-1,并置位errno |
2.6.3 例子
【以后添加】
2.7 改变文件大小
#include
#include
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
参数名 |
含义 |
path |
文件的文件名 |
fd |
文件描述符 |
length |
修改的长度 |
【待续...】
2.8 改变文件读写位置
#include
#include
off_t lseek(int fd, off_t offset, int whence);
2.9 目录扫描
2.9.2 目录信息
#include
#include
DIR *opendir(const char *name);
读取
#include
struct dirent *readdir(DIR *dirp);
- 描述目录的结构体
struct dirent
原型
struct dirent {
ino_t d_ino; /* 文件的索引号 */
off_t d_off; /* 文件的偏移量*/
unsigned short d_reclen; /* 长度 */
unsigned char d_type; /*类型 */
char d_name[256]; /*文件名称*/
};
【待续...】
2.9.2 获取文件的详细信息
-- 函数原型
#include
#include
#include
int stat(const char *path, struct stat *buf);
-- 结构体原型
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
【待续...】