一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节。
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个名额。
商业合作加微信,项目接包与外包。