Skip to content

LV030-文件IO简介

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

一、文件打开与关闭

1. open()

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

c
/* 需包含的头文件 */
#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 :表示打开文件所采用的操作。

(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

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

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

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

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

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

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

c
/* 需要包含的头文件 */
#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);

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

2. close()

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

c
/* 需包含的头文件 */
#include <unistd.h>

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

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

函数参数

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

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

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

c
/* 需要包含的头文件 */
#include <unistd.h>

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

注意事项

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

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

2. 使用实例

c
/* 头文件 */
#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;
}

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

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

二、文件读写

1. 基本函数

1.1 read()

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

c
/* 需包含的头文件 */
#include <unistd.h>

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

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

函数参数

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

返回值】 返回值是 ssize_t 类型(表示有符号的 size_t ),成功时返回实际读取的字节数;出错时返回 EOF ,读到文件末尾时返回 0 。EOF 是 end of file 的缩写,表示文件末尾,是在 stdio.h 中定义的宏,它的值是一个负数,往往是 -1 ,但是不绝对是 -1 ,也可以是其他负数,这要看编译器的规定。

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

c
/* 需要包含的头文件 */
#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 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <unistd.h>

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

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

函数参数

  • fd :文件描述符。

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

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

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

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

c
/* 需要包含的头文件 */
#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. 使用实例

c
/* 头文件 */
#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);
}

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

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

三、文件定位

1. lseek()

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

c
/* 需包含的头文件 */
#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 。

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

c
/* 需要包含的头文件 */
#include <sys/types.h>
#include <unistd.h>

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

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

2. 使用实例

c
/* 头文件 */
#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);
}

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

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