15. C语言 文件读写

        一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节。

        C 语言不仅提供了访问顶层的函数,也提供了底层(OS)调用来处理存储设备上的文件。

        在使用 open()、close()、read()、write()时,要调用头文件<fcntl.h>

15.1 打开文件

15.1.1 fopen()函数

        使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,函数原型如下:

FILE *fopen( const char* filename, const char* mode );

        filename 是字符串,用来命名文件;

        访问模式 mode 的值可以是下列值中的一个:

r

打开一个已有的文本文件,允许读取文件

w

打开一个文本文件,允许写入文件

如果文件不存在,则会创建一个新文件

在这里,的程序会从文件的开头写入内容

a

打开一个文本文件,以追加模式写入文件

如果文件不存在,则会创建一个新文件

在这里,的程序会在已有的文件内容中追加内容

r+

打开一个文本文件,允许读写文件

w+

打开一个文本文件,允许读写文件。

如果文件已存在,则文件会被截断为零长度

如果文件不存在,则会创建一个新文件

a+

打开一个文本文件,允许读写文件

如果文件不存在,则会创建一个新文件

读取会从文件的开头开始,写入则只能是追加模式

        fopen()函数返回值

        正常返回:被打开文件的文件指针。

        异常返回:NULL,表示打开操作不成功。

15.1.2 open()函数

        使用 open( ) 函数来创建一个新的文件或者打开一个已有的文件,函数原型如下:

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

以可读写方式打开文件.

上述三种旗标是互斥的, 也就是不可同时使用,但可与下列的旗标利用OR(|)运算符组合.

O_CREAT

若欲打开的文件不存在则自动建立该文件.

O_EXCL

如果O_CREAT 也被设置,此指令会去检查文件是否存在.

文件若不存在则建立该文件,否则将导致打开文件错误.

此外,若O_CREAT 与O_EXCL 同时设置,并且欲打开的文件为符号连接, 则会打开文件失败.

O_NOCTTY

如果欲打开的文件为终端机设备时,则不会将该终端机当成进程控制终端机.

O_TRUNC

若文件存在并且以可写的方式打开时,此旗标会令文件长度清为0,而原来存于该文件的资料也会消失.

O_APPEND

当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面.

O_NONBLOCK

以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中.

O_NDELAY

同O_NONBLOCK.

O_SYNC

以同步的方式打开文件.

O_NOFOLLOW

如果参数pathname 所指的文件为一符号连接,则会令打开文件失败.

O_DIRECTORY

如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。注:此为Linux2.2 以后特有的旗标, 以避免一些系统安全问题.

        参数mode 则有下列数种组合, 只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umaks).

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 权限

代表其他用户具有可执行的权限.

        open()函数返回值

        若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.

        错误代码:

EEXIST

参数pathname 所指的文件已存在,却使用了O_CREAT 和O_EXCL 旗标.

EACCESS

参数pathname 所指的文件不符合所要求测试的权限.

EROFS

欲测试写入权限的文件存在于只读文件系统内.

EFAULT

参数pathname 指针超出可存取内存空间.

EINVAL

参数mode 不正确.

ENAMETOOLONG

参数 pathname 太长.

ENOTDIR

参数pathname 不是目录.

ENOMEM

核心内存不足.

ELOOP

参数pathname 有过多符号连接问题.

EIO

I/O 存取错误.

15.2 关闭文件

15.2.1 fclose()函数

        关闭文件,使用 fclose( ) 函数。函数原型如下:

int fclose( FILE *fp );

        fclose()函数返回值

        如果成功关闭文件,fclose( ) 函数返回零。

        如果关闭文件时发生错误,函数返回 EOF。

        这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。

        EOF 是一个定义在头文件 stdio.h 中的常量。

15.2.2 close()函数

        关闭已打开的文件,指定的参数fd为open()或creat()打开的文件,使用 fclose( ) 函数。

        函数原型如下:

int close(int fd);

        close()函数返回值

        关闭成功返回0。

        失败则返回-1。

15.3 读取文件

15.3.1 fscanf( )函数

        往文件中写格式化数据,使用fscanf( ),函数原型如下:

int fscanf ( FILE * stream, const char * format, arg_list );

        fp:这是个文件指针,指出要将读取数据的文件。

        format:这是个指向字符串的字符指针,字符串中含有要数据读取的格式,所以该字符串成为格式串。格式串描述的规则与 scanf() 函数中的格式串相同。

        arg_list:是要写入文件的变量表列,各变量之间用逗号分隔。

        fscanf()函数返回值

        读取成功,则返回成功读取的项数;

        读取失败,则返回EOF

15.3.2 fread( )函数

        以二进制形式读取文件中的数据,使用fread( ),函数原型如下:

int fread(void *buffer, unsigned sife, unsigned count, FILE *fp);

        buffer:这是一个 void 型指针,指出要将读入数据存放在其中的存储区首地址。

        sife:指出一个数据块的字节数,即一个数据块的大小尺寸。

        count:指出一次读入多少个数据块(sife)。

        fp:这是个文件指针,指出要从其中读出数据的文件。

        fread()函数返回值

        正常返回:实际读取数据块的个数,即 count。

        异常返回:如果文件中剩下的数据块个数少于参数中 count 指出的个数,或者发生了错误,返回 0 值。此时可以用feof() 和 ferror() 来判定到底出现了什么情况。

15.3.3 read()函数

        用于文件描述符对应的文件中读取数据,使用read( ),函数原型如下:

ssize_t read(int fd, void *buf, size_t count);

        fd: 是文件描述符, 从command line获取数据时,为0

        buf: 为读出数据的缓冲区;

        count: 为每次读取的字节数(是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移)

        read()函数返回值

        成功:返回读出的字节数

        失败:返回-1,并设置errno,如果在调用read之前到达文件末尾,则这次read返回0。

15.3.4 read和fread 的区别

        fread返回的是一个FILE结构指针,而read返回的是一个int的文件号。

        read是UNIX系统中的系统调用,是类UNIX系统,提供给程序员操作文件的接口。

        fread是C语言提供的读取文件的函数库。实际上底层也是靠调用的read来实现。

        包括open,read, write, seek,close,和fopen,fread,fwrite,fseek,fclose。

        他们分别表示无缓冲读写和有缓冲读写。

        read发生在内核空间,而fread发生在用户空间。

        read其实也有缓存,但空间不大。

        一般来说使用fread比使用read的效率更高。

        如果文件的大小是10k

        用read/write,且只分配了2k的缓存,则要将此文件读出需要做5次系统调用来实际从磁盘上读出。

         用fread/fwrite,则系统自动分配缓存,则读出此文件只要一次系统调用从磁盘上读出。

        但在接近底层时,使用read函数更合适一些,这样避免了数据进入用户空间,内核直接实现,效率更高。

15.4 写入文件

15.4.1 fprintf( )函数

        往文件中写格式化数据,使用fprintf( ),函数原型如下:

int fprintf(FILE *fp, char *format, arg_list);

        fp:这是个文件指针,指出要将数据写入的文件。

        format:这是个指向字符串的字符指针,字符串中含有要写出数据的格式,所以该字符串成为格式串。格式串描述的规则与 printf() 函数中的格式串相同。

        arg_list:是要写入文件的变量表列,各变量之间用逗号分隔。

        fprintf()函数返回值

        读取成功,则返回成功读取的项数;

        读取失败,则返回EOF

15.4.2 fwrite( )函数

        以二进制形式写数据到文件中去,使用fwrite( ),函数原型如下:

int fwrite(void *buffer, unsigned sife, unsigned count, FILE *fp);

        buffer:这是一个 void型指针,指出要将其中数据输出到文件的缓冲区首地址。

        sife:指出一个数据块的字节数,即一个数据块的大小尺寸。

        count:一次输出多少个数据块(sife)。

        fp:这是个文件指针,指出要从其中读出数据的文件。

        fwrite()函数返回值

        正常返回:实际输出数据块的个数,即 count。

        异常返回:返回0值,表示输出结束或发生了错误。

15.4.3 write()函数

        用于将数据写入到文件描述符对应的文件,使用write( ),函数原型如下:

ssize_t write(int fd, const void *buf, size_t count);

        fd:是文件描述符(输出到command line,就是1)

        buf:通常是一个字符串,需要写入的字符串

        count:是每次写入的字节数

        write()函数返回值

        成功:返回写入的字节数

        失败:返回-1并设置errno

15.5 C语言文件操作示例

15.5.1 使用fprintf()+fscanf()
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			文件中读写格式化数据,使用fprintf()+fscanf().
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	char name[] = "Hello,World!";
	int x = 12;
	int y = 12345;

	char data[20];
	int x2 = 0;
	int y2 = 0;
	/*-----------------------*/
	FILE *fp=NULL;
	unsigned char File_name[] = "Data.txt";
	/*-----------------------*/
	fp = fopen(File_name, "w");//写入数据
	if (fp == NULL)
	{
		printf("The file %s can not be opened.\n", File_name);
		return;
	}
	else
	{
		fprintf(fp, "%s %5d %d\n", name, x, y);
		fprintf(fp, "This is Data.txt\n");

		printf("fprintf() call OK!!!\n");
	}
	fclose(fp);
	/*-----------------------*/
	fp = fopen(File_name, "r");//读取数据
	if (fp == NULL)
	{
		printf("The file %s can not be opened.\n", File_name);
		return;
	}
	fscanf(fp, "%s %5d %d\n", data, &x2, &y2);
	fclose(fp);
	/*-----------------------*/	//打印读取的数据内容
	printf("*data=%s\n", data);
	printf("x2==%d\n", x2);
	printf("y2==%d\n", y2);
	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

15.5.2 使用fwrite()+fread()
/*-----------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
/*-----------------------------------------------------------------------*/
/* 结构体 */
struct DATA 
{
	char name[20];
	int year[5];
	int month[5];
	int day[5];
	int hour[5];
	int min[5];
	int sec[5];
};
/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			文件中读写格式化数据,使用fwrite()+fread().
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	struct DATA data = {
		"Worker",	//name
		{ 2020, 2021, 2022, 2023, 2024 },	//year
		{ 1, 2, 3, 4, 5 },		//month
		{ 10, 11, 12, 13, 14 },	//day
		{ 15, 16, 17, 18, 19 },	//hour
		{ 20, 21, 22, 23, 24 },	//min
		{ 25, 26, 27, 28, 29 }	//sec
	};
	/*-----------------------*/
	FILE *file = fopen("data.txt", "wb");
	if (file == NULL)	//打开文件进行写入,若文件不存在,则会创建文件
	{
		perror("Unable to open file for writing");
		return 1;
	}
	else
	{
		fwrite(&data, sizeof(struct DATA), 1, file);//使用 fwrite() 写入结构体数据
	}

	fclose(file);//关闭文件

	/*-----------------------*/
	struct DATA read_data;
	FILE *file_read = fopen("data.txt", "rb");//读取文件并打印内容
	if (file_read == NULL) 
	{
		perror("Unable to open file for reading");
		return 1;
	}
	else
	{
		fread(&read_data, sizeof(struct DATA), 1, file_read);//读取结构体数据
	}

	fclose(file_read);//关闭文件

	/*-----------------------*/
	printf("Name: %s\n", read_data.name);	//打印读取的数据
	for (int i = 0; i < 5; i++) 
	{
		printf("Date %d: %d-%d-%d %d:%d:%d\n",
			i + 1,
			read_data.year[i],
			read_data.month[i],
			read_data.day[i],
			read_data.hour[i],
			read_data.min[i],
			read_data.sec[i]
			);
	}

	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

15.5.3 使用write()+read()

/*-----------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
/*-----------------------------------------------------------------------*/
/* 结构体 */
struct DATA 
{
	unsigned char name[8];
	unsigned int year[5];
	unsigned int month[5];
	unsigned int day[5];
};

/*-----------------------------------------------------------------------*/
/*
*  @brief
*		关于 main 的功能描述:
*			文件中读写格式化数据,使用fwrite()+fread().
*  @param[in]  void
*
*  @return  return 0
*
*  @implements 低层需求标识号 XXXX
*/
int main()
{
	int i = 0;
	struct DATA data = {
		"Farmer",	//name
		{ 2020, 2021, 2022, 2023, 2024 },	//year
		{ 1, 2, 3, 4, 5 },		//month
		{ 10, 11, 12, 13, 14 },	//day
	};
	/*-----------------------*/
	//打开文件进行写入,文件不存在则创建,使用 O_WRONLY 表示写入,O_CREAT 创建文件,O_TRUNC 清空文件
	int file = open("data.bin", O_WRONLY | O_CREAT, 00700);
	if (file == -1)
	{
		printf("Unable to open file for writing");
		return 1;
	}

	int bytes_written = write(file, &data, sizeof(struct DATA));//使用 write() 写入结构体数据
	if (bytes_written == -1) 
	{
		printf("Error writing to file");
		close(file);
		return 1;
	}

	close(file);//关闭文件

	/*-----------------------*/
	struct DATA read_data;//创建结构体以存储读取的数据
	memset(&read_data, 0, sizeof(struct DATA));

	//读取文件并打印内容
	int file_read = open("data.bin", O_RDONLY, 00700);	//以只读模式打开文件
	if (file_read == -1)
	{
		printf("Unable to open file for reading");
		return 1;
	}


	int bytes_read = read(file_read, &read_data, sizeof(struct DATA));//使用 read() 读取结构体数据
	if (bytes_read == -1)
	{
		printf("Error reading from file");
		close(file_read);
		return 1;
	}

	close(file_read);//关闭文件

	/*-----------------------*/
	printf("Name: %s\n", read_data.name);	//打印读取的数据
	for (int i = 0; i < 5; i++)
	{
		printf("Date %d: %d-%d-%d\n",
			i + 1,
			read_data.year[i],
			read_data.month[i],
			read_data.day[i]
		);
	}
	printf("*-----------------------*\n\n\n");
	/*-----------------------*/

	/*-----------------------*/
	unsigned char read_string[100] = {0};//创建字符串以存储读取的数据
	memset(&read_string, 0, sizeof(unsigned char)*100);

	//读取文件并打印内容
	int string_read = open("data.bin", O_RDONLY);	//以只读模式打开文件
	if (string_read == -1)
	{
		printf("Unable to open file for reading");
		return 1;
	}

	int str_read = read(string_read, read_string, sizeof(struct DATA));//使用 read() 读取结构体数据
	if (str_read == -1)
	{
		printf("Error reading from file");
		close(string_read);
		return 1;
	}

	close(string_read);//关闭文件

	/*-----------------------*/
	printf("*-----------------------*\n");
	printf("unsigned char read_string[100]== 0x \n");	//打印读取的数据
	for (i = 0; i < 72; i++)
	{
		printf("%.2x ", read_string[i]);
		if (i % 8 == 7)
			printf("\n");
	}
	printf("*-----------------------*\n");
	/*-----------------------*/
	return 0;
}
/*-----------------------------------------------------------------------*/

Visual Studio 运行结果:

非常感谢您的支持!创作不易,转发备注出处!

自用展示内容,不定期更新维护内容,让我们把 C开发 变得更专业。

十年开发基本功,常年开班收徒(一年学徒制度),每年10个名额。

商业合作加微信,项目接包与外包。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值