LV04-03-文件操作-文件IO

本文主要是C语言——文件I/O基本操作的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

一、文件打开与关闭

1. 基本函数

1.1 open()

在 linux 下可以使用 man 2 open 命令查看该函数的帮助手册。

1
2
3
4
5
6
7
8
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/* 函数声明 */
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

【函数说明】该函数打开一个文件,获取文件描述符;打开文件时使用两个参数,创建文件时第三个参数指定新文件的权限。

【函数参数】

  • pathname :被打开的文件名(可包括路径名)。
  • flags :表示打开文件所采用的操作。
点击查看详细的 flag 常量

(1) flag 常量可取的值

O_RDONLY只读方式打开文件。这三个参数互斥
O_WRONLY可写方式打开文件。
O_RDWR 读写方式打开文件。
O_CREAT 如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限。
O_EXCL 如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
O_NOCTTY使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。
O_TRUNC 如文件已经存在,那么打开文件时先删除文件中原有数据。
O_APPEND以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。

【注意】前三个参数必须指定,且只能指定一个,后边的几个可以与前边搭配使用。

(2) flag 常量与标准 I/O 文件打开权限关系、

r O_RDONLY
r+O_RDWR
w O_WRONLY | O_CREAT | O_TRUNC, 0664
w+O_RDWR | O_CREAT | O_TRUNC, 0664
a O_WRONLY | O_CREAT | O_APPEND, 0664
a+O_RDWR | O_CREAT | O_APPEND, 0664
  • mode :表示被打开文件的存取权限,为 8 进制表示法。此参数只有在建立新文件时有效。新建文件时的权限会受到 umask 值影响,实际权限是 mode - umaks
点击查看什么是 umask

在类 unix 系统中, umask 是确定掩码设置的命令,该掩码用来设定文件或目录的初始权限。 umask 确定了文件创建时的初始权限:

1
文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限

文件初始默认权限为 0666 ,目录为 0777 ,若用户 umask 为 0002 ,则新创建的文件或目录在没有指定的情况下默认权限分别为 0664 、 0775 )。

在 Linux 下,我们可以使用 umask 命令来查看当前用户默认的 umask 值,同时也可以在 umask 命令后面跟上需要设置的 umask 值来重新设置 umask 。

【返回值】 返回值是一个 int 类型,成功时返回文件描述符(非负整数);出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/* 至少应该有的语句 */
int fd;/* 接收文件描述符 */
/* 以只写方式打开文件01open_close.txt。如果文件不存在则创建,如果文件存在则清空 */
fd = open("01open_close.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);

【注意事项】 该函数可以打开设备文件,但是不能创建设备文件

1.2 close()

在 linux 下可以使用 man 2 close 命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <unistd.h>

/* 函数声明 */
int close(int fd);

【函数说明】该函数关闭一个打开的文件。

【函数参数】

  • fd :已打开文件的文件描述符。

【返回值】 返回值是一个整数,成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
/* 需要包含的头文件 */
#include <unistd.h>

/* 至少应该有的语句 */
int ret;/* 保存文件关闭返回值 */
ret = close(fd);

【注意事项】

(1)程序结束时自动关闭所有打开的文件。

(2)文件关闭后,文件描述符不再代表文件。

2. 使用实例

点击查看实例
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/** =====================================================
* Copyright © hk. 2022-2022. All rights reserved.
* File name: 01open_close.c
* Author : fanhua
* Description: 文件IO——文件打开关闭
* ======================================================
*/
/* 头文件 */
#include <stdio.h>
/* read write close函数 */
#include <unistd.h>
/* open 函数 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char*argv[])
{
int fd;/* 接收文件描述符 */
int ret;/* 保存文件关闭返回值 */
/* 1. 打开文件 man 2 open*/
fd = open("01open_close.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
/* 2. 判断是否打开成功 */
if(fd < 0)
{
printf("open file err\n");
return 0;
}
else
{
printf("sucess,fd=%d\n",fd);
}
/* 3. 关闭文件 */
ret = close(fd);
if(ret < 0)
{
printf("close failed\n");
}

ret = close(fd);
printf("ret=%d\n", ret);
return 0;
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

二、文件读写

1. 基本函数

1.1 read()

在 linux 下可以使用 man 2 read 命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <unistd.h>

/* 函数声明 */
ssize_t read(int fd, void *buf, size_t count);

【函数说明】该函数从文件中读取数据。

【函数参数】

  • fd :文件描述符。
  • buf :接收数据的缓冲区。
  • count :需要读取的字节数,不应超过 buf 。

【返回值】 返回值是 ssize_t 类型(表示有符号的 size_t ),成功时返回实际读取的字节数;出错时返回 EOF ,读到文件末尾时返回 0 。

点击查看 EOF 说明

EOF 是 end of file 的缩写,表示文件末尾,是在 stdio.h 中定义的宏,它的值是一个负数,往往是 -1 ,但是不绝对是 -1 ,也可以是其他负数,这要看编译器的规定。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <unistd.h>

/* 至少应该有的语句 */
int fd;
char buff[32]={0};
int ret;
fd = open("02read_write.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
ret = read(fd, buff, 32);

【注意事项】

(1)读取过程中,未读完 count 字节时,若遇到换行,则会一起读取。

(2)对于⼀个数组,总是要自动分配⼀个 ‘\0’ 作为结束,所以实际有效的 buf 长度就成为 sizeof(buf) - 1 了,最好就是在读取完成后自己加上一个 ‘\0’ ,以防止后边打印出现乱码的情况。

1.2 write()

在 linux 下可以使用 man 2 write 命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <unistd.h>

/* 函数声明 */
ssize_t write(int fd, const void *buf, size_t count);

【函数说明】该函数向文件写入数据。

【函数参数】

  • fd :文件描述符。

  • buf :要写入文件的数据的缓冲区。

  • count :需要写入的字节数,不应超过 buf 。

【返回值】返回值是一个 ssize_t 类型,成功时返回实际写入的字节数;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <unistd.h>

/* 至少应该有的语句 */
int fd;
char buff[32]= "fanhualuojin";
int ret;
fd = open("02read_write.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
ret = write(fd, buff, strlen(buff));

【注意事项】 数据写入完毕后,文件指针指向文件尾部,此时直接读取文件,则什么也读不到,可以使用后边的函数移动指针,再进行读取。

2. 使用实例

点击查看实例
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/** =====================================================
* Copyright © hk. 2022-2022. All rights reserved.
* File name: 02read_write.c
* Author : fanhua
* Description: 文件IO——文件读写
* ======================================================
*/
/* 头文件 */
#include <stdio.h>
/* read write close函数 */
#include <unistd.h>
/* open 函数 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* strlen 函数 */
#include <string.h>


void test_read();
void test_write();

int main(int argc,char*argv[])
{
test_read();
// test_write();
return 0;
}

/**
* @Function: ssize_t read(int fd, void *buf, size_t count);
* @Description: 成功时返回实际读取的字节数;出错时返回EOF
*/
void test_read()
{
/* 定义一个文件描述符变量 */
int fd;
char buff[32]={0};
int ret;
/* 1. 打开当前路径下文件 */
fd = open("02read_write.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
/* 2. 判断是否打开成功 */
if(fd < 0)
{
printf("open file err\n");
return;
}
else
{
printf("sucess,fd=%d\n",fd);
}
/* 3. 读取文件内容 */
ret = read(fd, buff, 32);
if(ret < 0)
{
perror("read");
close(fd);
return;
}
buff[31] = 0;
printf("read buff=%s\n", buff);
/* 4. 关闭文件 */
close(fd);
}

/**
* @Function: ssize_t write(int fd, void *buf, size_t count);
* @Description: 成功时返回实际写入的字节数;出错时返回EOF
*/
void test_write()
{
/* 定义一个文件描述符变量 */
int fd;
char buff[32]= "fanhualuojin";
char a[32];
int ret;
/* 1. 打开当前路径下文件 */
fd = open("02read_write.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
/* 2. 判断是否打开成功 */
if(fd < 0)
{
printf("open file err\n");
return;
}
else
{
printf("sucess,fd=%d\n",fd);
}
/* 3. 写入文件内容 */
ret = write(fd, buff, strlen(buff));
if(ret < 0)
{
perror("write");
close(fd);
return;
}
printf("sucess,write count=%d\n",ret);
/* 4. 读取文件内容,这里会发现什么也读不到,可以看下一个例子,这里还仅仅是测试写入函数 */
ret = read(fd , a, 32);
if(ret < 0)
{
perror("read");
close(fd);
return;
}
a[31] = 0;
printf("read buff=%s\n", a);
printf("read count=%d\n",ret);
/* 5. 关闭文件 */
close(fd);
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

三、文件定位

1. lseek()

在 linux 下可以使用 man 2 lseek 命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <sys/types.h>
#include <unistd.h>
/* 函数声明 */
off_t lseek(int fd, off_t offset, intt whence);

【函数说明】该函数用于读写文件时偏移文件指针,定位读写位置。

【函数参数】

  • fd :文件描述符。

  • offset :相对于 whence (基准)的偏移量。

  • whence :添加偏移 offset 的位置。

点击查看 whence 参数
常量描述
SEEK_SET文件的开头
SEEK_CUR文件指针的当前位置
SEEK_END文件的末尾

【返回值】返回值是 off_t 类型,成功时返回当前的文件读写位置;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
/* 需要包含的头文件 */
#include <sys/types.h>
#include <unistd.h>

/* 至少应该有的语句 */
lseek(fd, 0, SEEK_SET);

【注意事项】lseek()允许将文件偏移量设置在文件末尾之外(但这不会改变文件的大小)。如果稍后在此时写入数据,则后续读取间隙( a “hole”)中的数据将返回空字节(‘\0’),直到读到数据实际写入的位置为止。

2. 使用实例

点击查看实例
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/** =====================================================
* Copyright © hk. 2022-2022. All rights reserved.
* File name: 03lseek.c
* Author : fanhua
* Description: 文件IO——文件读写
* ======================================================
*/
/* 头文件 */
#include <stdio.h>
/* read write close函数 */
#include <unistd.h>
/* open 函数 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* strlen 函数 */
#include <string.h>


void test_none_lseek();
void test_lseek();

int main(int argc,char*argv[])
{
// test_none_lseek();
test_lseek();
return 0;
}

/**
* @Function:
* @Description: 写入数据后,直接读取,会发现什么也读不到,原因就在于写入完毕后,文件指针移动到了文件末尾。
*/
void test_none_lseek()
{
/* 定义一个文件描述符变量 */
int fd;
char buff[32]= "fanhualuojin";
char a[32];
int ret;
/* 1. 打开当前路径下文件 */
fd = open("03lseek.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
/* 2. 判断是否打开成功 */
if(fd < 0)
{
printf("open file err\n");
return;
}
else
{
printf("sucess,fd=%d\n",fd);
}
/* 3. 写入文件内容 */
ret = write(fd, buff, strlen(buff));
if(ret < 0)
{
perror("write");
close(fd);
return;
}
printf("sucess,write count=%d\n",ret);
/* 4. 读取文件内容 */
ret = read(fd , a, 32);
if(ret < 0)
{
perror("read");
close(fd);
return;
}
a[31] = 0;
printf("read buff=%s\n", a);
printf("read count=%d\n",ret);
/* 5. 关闭文件 */
close(fd);
}

/**
* @Function: off_t lseek(int fd, off_t offset, intt whence);
* @Description: 成功时返回实际写入的字节数;出错时返回EOF
*/
void test_lseek()
{
/* 定义一个文件描述符变量 */
int fd;
char buff[32]= "fanhualuojin";
char a[33];
int ret;
/* 1. 打开当前路径下文件 */
fd = open("03lseek.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
/* 2. 判断是否打开成功 */
if(fd < 0)
{
printf("open file err\n");
return;
}
else
{
printf("sucess,fd=%d\n",fd);
}
/* 3. 写入文件内容 */
ret = write(fd, buff, strlen(buff));
if(ret < 0)
{
perror("write");
close(fd);
return;
}
printf("sucess,write count=%d\n",ret);

/* 4. 设置文件指针位置 */
lseek(fd, 0, SEEK_SET);

/* 5. 读取文件内容 */
ret = read(fd, a, 33);
if(ret < 0)
{
perror("read");
close(fd);
return;
}
a[sizeof(a)-1] = 0;/* 设置字符串结束标志,防止乱码 */
printf("read buff=%s\n", a);
printf("read count=%d\n",ret);
/* 6. 关闭文件 */
close(fd);
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序