在 Linux 中,文件总共被分成了 7 种,他们分别是:
1,普通文件 (regular) '- ':存在于外部存储器中,用于存储普通数据。
2,目录文件 (directory) 'd':用于存放目录项,是文件系统管理的重要文件类型。
3,管道文件 (pipe) 'p':一种用于进程间通信的特殊文件,也称为命名管道 FIFO。
4,套接字文件 (socket) 's':一种用于网络间通信的特殊文件。
5,链接文件 (link) 'l':用于间接访问另外一个目标文件,相当于 Windows 快捷方式。
6,字符设备文件 (character) 'c':字符设备在应用层的访问接口。
7,块设备文件 (block) 'b':块设备在应用层的访问接口。
一.系统IO对文件的操作
1.打开文件open()
1.1 需要的头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
1.2 函数的原型
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
函数参数:
pathname:你要打开的文件路径,用字符串的表示方式
flags | O_RDONLY:只读方式打开文件 | 这三个参数互斥 |
O_WRONLY:只写方式打开文件 | ||
O_RDWR:读写方式打开文件 | ||
O_CREAT:如果文件不存在,则创建该文件。 | ||
O_EXCL:如果使用 O_CREAT 选项且文件存在,则返回错误消息。 | ||
O_NOCTTY:如果文件为终端,那么终端不可以作为调用 open()系统调 用的那个进程的控制终端。 | ||
O_TRUNC:如文件已经存在,则删除文件中原有数据。 | ||
O_APPEND:以追加方式打开文件。 | ||
mode | 如果文件被新建,指定其权限为 mode (八进制表示法) |
如果使用了O_CREAT,open()函数就要加第三个参数,mode文件权限(八进制数表示)
1.3 返回值
成功:大于等于 0 的整数 (即文件描述符) 失败:-1
1.4 代码演示
int a = open("./1.txt", O_RDONLY);
int b = open("./2.txt", O_RDONLY);
int c = open("./3.txt", O_RDONLY);
printf("%d %d %d\n", a,b,c);
运行结果:
至于从3开始的原因是:0为键盘的标准输入,1是屏幕的标准输出,2是屏幕的标准输入
2.关闭文件close()
2.1 需要的头文件
#include <unistd.h>
2.2 函数的原型
int close(int fd);
函数参数:
fd:即将要关闭的文件的描述符
2.3 返回值
成功:0 失败:-1
2.4 代码演示
close(a);
close(b);
close(c);
3. 文件光标的偏移lseek()
在读写文件的时候有个偏移量的概念,即当前读写的位置,简单来说,就是在记事本中那个闪烁的竖线。这个位置可以获取,也可以人为调整。
3.1 需要的头文件
#include <sys/types.h>
#include <unistd.h>
3.2 函数的原型
off_t lseek(int fd, off_t offset, int whence);
函数参数:
fd:要调整位置偏移量的文件的描述符 | |
offset:新位置偏移量相对基准点的偏移,输入多少就在基准点的位置向右偏移多少 | |
whence:基准点 | SEEK_SET:文件开头处 |
SEEK_CUR:当前位置 | |
SEEK_END:文件末尾处 |
3.3 返回值
成功:新文件位置偏移量 失败:-1
3.4 代码演示
int fd = open("./3.txt", O_RDWR);
char ch;
//将文件光标偏移到文件结尾
int ls1 = lseek(fd, 0, SEEK_END);
//将文件光标偏移到文件开头
int ls2 = lseek(fd, 0, SEEK_SET);
//将文件光标在当前位置向右偏移6个字节
int ls3 = lseek(fd, 6, SEEK_CUR);
printf("ls1 = %d\nls2 = %d\nls3 = %d\n", ls1, ls2, ls3);
运行结果:
ls2为0是因为光标往前偏移 。
4.文件的读取read()
4.1 需要的头文件
#include <unistd.h>
4.2 函数的原型
ssize_t read(int fd, void *buf, size_t count);
函数参数:
fd:从文件fd 中读数据
buf:指向存放读到的数据的缓冲区
count:想要从文件 fd 中读取的字节数
4.3 返回值
成功:实际读到的字节数 失败:-1
4.4 代码演示 perror的使用
4.4.1 读取一个字符
char ch1 = 0;
int fd = open("./1.txt", O_RDONLY);
if (fd == -1)
{
perror("文件打开失败!");
return -1;
}
int n = read(fd, &ch1, sizeof(ch1));
if (n == -1)
{
perror("写入失败!");
return -1;
}
注:perror:是使用strerror(errno)打印出错误信息。使用errno需要头文件#include<errno.h>
4.4.2 读取一行字符
char ch2 = 0;
int n, fd = open("./1.txt", O_RDONLY);
if (fd == -1)
{
perror("文件打开失败!");
return -1;
}
while (ch2 != '\n')
{
n = read(fd, &ch2, sizeof(ch2));
if (n == 0) break;
printf("%c", ch2);
}
4.4.3 读取所有内容
char ch2[100] = { 0 };
int n, fd = open("./1.txt", O_RDONLY);
if (fd == -1)
{
perror("文件打开失败!");
return -1;
}
while (n = read(fd, ch2, sizeof(ch2) - 1))
{
if (n == -1)
{
perror("文件读取失败!");
return -1;
}
printf("%s", ch2);
memset(ch2, 0, sizeof(ch2));
}
5.文件的写入write()
5.1 需要的头文件
#include <unistd.h>
5.2 函数的原型
ssize_t write(int fd, const void *buf, size_t count);
函数参数:
fd:将数据写入到文件 fd 中
buf:指向即将要写入的数据
count:要写入的字节数
5.3 返回值
成功:实际写入的字节数 失败:-1
5.4 代码演示
5.4.1 写入一个字符
char ch1 = 'a';
int fd = open("./1.txt", O_WRONLY);
if (fd == -1)
{
perror("文件打开失败!");
return -1;
}
int n = write(fd, &ch1, 1);
if (n == -1)
{
perror("写入失败!");
return -1;
}
5.4.2 写入多个字符
char str[100] = "文件IO.exe(Win32):已加载:System32\msvcrt.dll。";
int fd = open("./1.txt", O_WRONLY);
if (fd == -1)
{
perror("文件打开失败!");
return -1;
}
int n = write(fd, str, strlen(str));
if (n == -1)
{
perror("写入失败!");
return -1;
}
注: 写入的字节数count要使用strlen(str),不要使用sizeof(str)。因为你写入内容时使用sizeof(str)会将str中所有内容写入到文件中。例如你str的大小100,但写入str里面的内容只有"hello world",但是你使用的是sizeof(str),那就会写入"hello world"之后再写入很多空格,直到写到100,所有写入的时候要使用strlen(str)写入str的有效长度的大小。
6.其他函数
6.1 复制文件描述符dup( )/dup2( )
头文件 | #include <unistd.h> | |
原型 | int dup(int oldfd); int dup2(int oldfd, int newfd); | |
参数 | oldfd:要复制的文件描述符 | |
newfd:指定的新文件描述符 | ||
返回值 | 成功 | 新的文件描述符 |
失败 | - 1 |
6.2 文件控制
6.2.1 ioctl()
头文件 | #include <sys/ioctl.h> | |
原型 | int ioctl(int d, int request, ...); | |
参数 | d:要控制的文件描述符 | |
request:针对不同文件的各种控制命令字 | ||
变参:根据不同的命令字而不同 | ||
返回值 | 成功 | 一般情况下是 0 ,但有些特定的请求将返回非负整数。 |
失败 | - 1 |
6.2.2 fcntl()
头文件 | #include <unistd.h> #include <fcntl.h> | |
原型 | int fcntl(int fd, int cmd, .../* arg */ ); | |
参数 | fd:要控制的文件描述符 | |
cmd:控制命令字 | ||
变参:根据不同的命令字而不同 | ||
返回值 | 成功 | 根据不同的 cmd ,返回值不同 |
失败 | - 1 |
对于 fcntl( )而言,其第二个参数命令字 cmd 有很多,下面的网址有一个汇总:
文件控制 fcntl()中的参数cmd汇总和内存映射mmap函数中参数flags汇总-CSDN博客
6.3 内存映射mmap()
功能 | 内存映射 |
头文件 | #include <sys/mman.h> |
原型 | void *mmap(void *addr, size_t length,int prot, int flags, int fd, off_t offset); |
参数:
addr: | 映射内存的起始地址。 如果该参数为 NULL, 则系统将会自动寻找一个合适的起始地址,一 般都使用这个值。 如果该参数不为 NULL,则系统会以此为依据来找到一个合适的起始地 址。在 Linux 中,映射后的内存起始地址必须是页地址的整数倍。 |
length: | 映射内存大小。 |
prot: | 映射内存的保护权限。 PROT_EXEC : 可执行。 PROT_READ : 可读。 PROT_WRITE : 可写。 PROT_NONE: 不可访问。 |
flags: | 文件控制 fcntl()中的参数cmd汇总和内存映射mmap函数中参数flags汇总-CSDN博客 |
fd: | 要映射的文件的描述符 |
offset: | 文件映射的开始区域偏移量,该值必须是页内存大小的整数倍,即必 须是函数 sysconf(_SC_PAGE_SIZE)返回值的整数倍。 |
二.标准IO对文件的操作
1.打开文件fopen()
1.1 需要的头文件
#include <stdio.h>
1.2 函数的原型
FILE *fopen(const char *path, const char *mode);
函数参数:
path:即将要打开的文件
mode | “r” : 以只读方式打开文件,要求文件必须存在。 |
“r+” : 以读写方式打开文件,要求文件必须存在。 | |
“w” : 以只写方式打开文件,文件如果不存在将会创建新文件,如果存 在将会将其内容清空。 | |
“w+” : 以读写方式打开文件,文件如果不存在将会创建新文件,如果存 在将会将其内容清空。 | |
“a” : 以只写方式打开文件,文件如果不存在将会创建新文件,且文件位 置偏移量被自动定位到文件末尾 (即以追加方式写数据) 。 | |
“a+” : 以读写方式打开文件,文件如果不存在将会创建新文件,且文件 位置偏移量被自动定位到文件末尾 (即以追加方式写数据) 。 |
1.3 返回值
成功:文件指针 失败:NULL
1.4 代码演示
FILE* fp = fopen("1.txt", "r+");
//如果值为NULL,则文件打开失败
if (fp == NULL)
{
printf("文件打开失败");
return -1;
}
2.关闭文件fclose()
2.1 需要的头文件
#include <stdio.h>
2.2 函数的原型
int fclose(FILE *fp);
函数参数:
fp:即将要关闭的文件
2.3 返回值
成功:0 失败:EOF(-1)
2.4 代码演示
fclose(fp);
3.文件指针的偏移 fseek()函数
头文件:#include <stdio.h>
函数原型:int fseek(FILE *stream, long offset, int whence);
参数:
stream:需要设置位置偏移量的文件指针 | |
offset:新位置偏移量相对基准点的偏移 | |
whence:基准点 | SEEK_SET:文件开头处 |
SEEK_CUR:当前位置 | |
SEEK_END:文件末尾处 |
返回值: 成功:0 失败:-1
代码演示:
//将文件光标偏移到文件结尾
fseek(fp, 0, SEEK_END);
//将文件光标偏移到文件开头
fseek(fp, 0, SEEK_SET);
//将文件光标在当前位置向右偏移6个字节
Fseek(fp, 6, SEEK_CUR);
4.读取文件
4.1 读取一个字符fgetc()函数
fgetc()函数:读一个,读取完会将文件指针移动到下一个字符
头文件:#include <stdio.h>
函数原型:int fgetc(FILE *stream);
参数:stream:文件指针
返回值: 成功:读取到的字符 失败:EOF( -1 )
备注:当返回 EOF 时,文件 stream 可能已达末尾,或者遇到错误
代码演示:fp为文件指针
char ch = fgetc(fp);
printf("%c\n", ch);
使用 fgetc()函数读取文件的所有内容
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
4.2 读取一行字符fgets()函数
fgets()函数:读一行,文件指针自动遇到下一行
头文件:#include <stdio.h>
函数原型:char *fgets(char *s, int size, FILE *stream);
参数:
s: 自定义缓冲区指针 |
size: 自定义缓冲区大小 |
stream:即将被读取数据的文件指针 |
返回值: 成功:自定义缓冲区指针 s 失败:NULL
备注:
①gets( )缺省从文件 stdin 读入数据
②当返回 NULL 时,文件 stream 可能已达末尾,或者遇到错误
代码演示:fp为文件指针
//读到结尾返回NULL
char str[100] = {0};
fgets(str, 100, fp);
printf("%s\n", str);
使用 fgets()函数读取文件的所有内容
char str[100] = {0};
while (fgets(str, 100, fp))
{
printf("%s", str);
memset(str,0,100);//清空str
}
printf("\n");
4.3 想读多少读多少 fread()函数
头文件:#include <stdio.h>
函数原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
ptr: 自定义缓冲区指针 |
size:数据块大小 |
nmemb:数据块个数 |
stream:即将被读取数据的文件指针 |
返回值:
成功:读取的数据块个数,等于 nmemb
失败:读取的数据块个数,小于 nmemb 或等于 0
备注:当返回值小于nmemb 时,文件 stream 可能已达末尾,或者遇到错误
代码演示:fp为文件指针
4.3.1 读取文件的前20个字节:
char str[100] = {0};
int n = fread(str,1,20, fp);
str[n] = "\0";//n<100,不然会发生数组越界
printf("%s\n", str)
4.3.2 读取文件的使用内容:
char str[100] = {0};
while (fread(str, 1, 20, fp))
{
printf("%s", str);
memset(str,0,100);//清空str
}
注:fread函数的返回值是读取的块数,写满一块才返回1
4.3.3 使用fread()函数实现fgetc()函数的功能
int myFgetc(FILE* fileP)
{
char ch; int n;
n = fread(&ch, 1, 1, fileP);
return n == 1 ? ch : EOF;
}
4.3.4使用fread()函数实现fgets()函数的功能
char* myFgets(char* str, int size, FILE* fileP)
{
int i = 0;
while ((str[i] = myFgetc(fileP)) != EOF)
{
if (fseek(fileP, 1, SEEK_CUR) == -1)
return NULL;
fseek(fileP, -1, SEEK_CUR);
if ((str[i] = myFgetc(fileP)) == '\n')
break;
i++;
}
str[i] = '\0';
return i > 0 ? str : NULL;
}
5.写入文件
5.1 写入一个字符到指定文件中 fputc()函数
头文件:#include <stdio.h>
函数原型:int fputc(int c, FILE *stream);
参数:
c:要写入的字符
stream:文件指针
返回值: 成功:写入到的字符 失败:EOF( -1 )
代码演示:fp为文件指针
fputc('A', fp);
fputc('B', fp);
fputc('C', fp);
5.2 写入一个字符串到指定文件中 fputs()函数
头文件:#include <stdio.h>
函数原型:int fputs(const char *s, FILE *stream);
参数:
s: 自定义缓冲区指针 |
stream:即将被写入数据的文件指针 |
返回值: 成功:非负整数 失败:NULL
代码演示:fp为文件指针
char* str = "hello world\n";
fputs(str, fp);
5.3 想写多少读多少 fwrite()函数
头文件:#include <stdio.h>
函数原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数:
ptr: 自定义缓冲区指针 |
size:数据块大小 |
nmemb:数据块个数 |
stream:即将被写入数据的文件指针 |
返回值:
成功:写入的数据块个数,等于 sinmembze
失败:写入的数据块个数,小于 nmemb 或等于 0
代码演示:fp为文件指针
5.3.1 :演示
char* str = "123456789abcdefg123456789abcdefg123456789abcdefg\n";
fwrite(str,1,strlen(str3), fp);
注:fread函数的返回值是写入的块数,写满一块才返回1
5.3.2 使用fread()函数实现fgetc()函数的功能
int myFputc(int c, FILE* fileP)
{
int n;
n = fwrite(&c, 1, 1, fileP);
return n == 1 ? c : EOF;
}
5.3.3 使用fread()函数实现fgets()函数的功能
int myFputs(const char* str, FILE* fileP)
{
int i = 0;
if (fseek(fileP, -1, SEEK_CUR) != -1)
{
fwrite("\n", 1, 1, fileP);
fseek(fileP, 1, SEEK_CUR);
}
while (str[i] != '\0')
{
fwrite(&str[i], 1, 1, fileP);
i++;
}
return i > 0 ? i : EOF;
}
6.其他函数
6.1 获取指定文件的当前位置偏移量
头文件 | #include <stdio.h> | |
原型 | long ftell(FILE *stream); | |
参数 | stream:需要返回当前文件位置偏移量的文件指针 | |
返回值 | 成功 | 当前文件位置偏移量 |
失败 | - 1 |
6.2 将指定文件的当前位置偏移量设置到文件开头处
头文件 | #include <stdio.h> |
原型 | void rewind(FILE *stream); |
参数 | stream:需要设置位置偏移量的文件指针 |
返回值 | 无 |
备注 | 该函数的功能是将文件 strean 的位置偏移量置位到文件开头处。 |
6.3 判断文件指针
功能 | feof():判断一个文件是否到达文件末尾 ferror():判断一个文件是否遇到了某种错误 | |
头文件 | #include <sys/ioctl.h> | |
原型 | int feof(FILE *stream); int ferror(FILE *stream); | |
参数 | stream:进行判断的文件指针 | |
返回值 | feof | 如果文件已达末尾则返回真,否则返回假 |
ferror | 如果文件遇到错误则返回真,否则返回假 |
三.系统IO和标准IO的区别
系统IO:
具有通用性,简约性,没有缓冲区,直接操作内核,open的返回值是一个整型(int)的文件描述符。
标准IO:
只能在有标准C库的情况下使用,有丰富的读写操作,有标准IO的缓冲区,要处理的数据会先存到标准IO缓冲区,等缓冲区满了之后,才会送到内核执行,fopen的返回值是一个指向文件结构体的指针,简称文件指针。