FILE
类型和相关函数(如 fopen
, fwrite
, fread
, fclose
)进行操作。
在C语言中,文件存储是一个非常重要的概念,它允许程序将数据持久化到磁盘上,以便在程序运行结束后仍然可以访问这些数据,下面将从文件的基本操作、文件类型、文件读写模式以及一些常见的文件处理函数等方面进行详细讲解。
1、打开文件
在C语言中,使用fopen()
函数来打开一个文件,其原型为:
FILE fopen(const char filename, const char mode);
filename
是文件名的字符串,包括路径(如果有)。"data.txt"
表示当前目录下的data.txt
文件。
mode
是文件打开模式,用于指定文件的打开方式,如只读("r"
)、只写("w"
)、追加("a"
)等,以下是一些常见的模式及其含义:
模式 | 含义 |
"r" | 以只读方式打开文件,如果文件不存在,fopen() 返回NULL 。 |
"w" | 以只写方式打开文件,如果文件已存在,清空文件内容;如果文件不存在,创建新文件。 |
"a" | 以追加方式打开文件,写入的数据会添加到文件末尾,如果文件不存在,创建新文件。 |
"r+" | 以读写方式打开文件,文件必须存在,否则返回NULL 。 |
"w+" | 以读写方式打开文件,如果文件已存在,清空文件内容;如果文件不存在,创建新文件。 |
"a+" | 以读写方式打开文件,读取时从文件开头,写入时追加到文件末尾,如果文件不存在,创建新文件。 |
2、关闭文件
当完成对文件的操作后,需要使用fclose()
函数关闭文件,以释放与文件相关的资源,其原型为:
int fclose(FILE stream);
stream
是通过fopen()
函数打开文件时返回的文件指针,如果成功关闭文件,fclose()
返回0
;如果出现错误,返回EOF
(通常在stdio.h
中定义为 -1)。
3、检测文件是否成功打开
在使用文件之前,应该检查fopen()
函数的返回值是否为NULL
,以确定文件是否成功打开。
FILE fp = fopen("data.txt", "r"); if (fp == NULL) { perror("Error opening file"); return -1; }
perror()
函数会根据全局变量errno
的值输出相应的错误信息。
1、文本文件
文本文件中的数据是以字符形式存储的,每行数据以回车换行符(`
`)这种文件适合存储人类可读的文本信息,如配置文件、日志文件等,在读写文本文件时,可以使用字符数组或字符串来处理数据。
char buffer[100]; while (fgets(buffer, sizeof(buffer), fp) != NULL) { printf("%s", buffer); }
fgets()
函数用于从文件中读取一行数据,并将其存储到buffer
中。
2、二进制文件
二进制文件中的数据是以二进制形式存储的,它可以包含任何类型的数据,如图像、音频、视频等,在读写二进制文件时,不能将其视为文本进行处理,需要按照数据的原始格式进行读写,读取一个整数:
int num; fread(&num, sizeof(int), 1, fp);
fread()
函数用于从文件中读取指定数量的数据块,并将其存储到num
变量中。
1、顺序读写
读取文本文件
使用fgetc()
函数可以逐个读取文件中的字符,直到文件结束。
int ch; while ((ch = fgetc(fp)) != EOF) { putchar(ch); }
fgetc()
函数返回文件中的下一个字符,并将其转换为unsigned char
类型,当到达文件末尾时,返回EOF
。
putchar()
函数用于将字符输出到标准输出设备(通常是屏幕)。
写入文本文件
使用fputc()
函数可以逐个写入字符到文件中。
fputc('A', fp); fputc(' ', fp);
fputc()
函数将指定的字符写入到文件中。
2、随机读写
移动文件指针
在文件中定位特定位置可以使用fseek()
函数,其原型为:
int fseek(FILE stream, long int offset, int origin);
stream
是文件指针。
offset
是偏移量,表示要移动的字节数,如果为正数,表示向文件末尾方向移动;如果为负数,表示向文件开头方向移动。
origin
是起始位置,可以是以下三个值之一:
值 | 含义 |
SEEK_SET | 文件开头 |
SEEK_CUR | 当前位置 |
SEEK_END | 文件末尾 |
读取随机位置的数据
首先使用fseek()
函数将文件指针移动到指定位置,然后使用fread()
函数读取数据,读取文件中第10个字节开始的5个字节数据:
fseek(fp, 9, SEEK_SET); char buffer[5]; fread(buffer, 1, 5, fp);
写入随机位置的数据
同样先使用fseek()
函数定位,然后使用fwrite()
函数写入数据,在第10个字节处写入5个字节的数据:
fseek(fp, 9, SEEK_SET); char data[] = "ABCDE"; fwrite(data, 1, 5, fp);
1、获取文件大小
可以使用fseek()
函数和ftell()
函数组合来获取文件的大小,首先将文件指针移动到文件末尾,然后使用ftell()
函数获取当前文件指针的位置,即文件的大小。
fseek(fp, 0, SEEK_END); long filesize = ftell(fp);
2、修改文件权限
在Unix/Linux系统中,可以使用chmod()
函数来修改文件的权限,其原型为:
int chmod(const char path, mode_t mode);
path
是文件的路径。
mode
是新的权限模式,例如S_IRUSR | S_IWUSR
表示所有者具有读写权限。
在Windows系统中,没有直接的等效函数,但可以通过系统调用或其他方法来实现类似的功能。
1、缓冲区溢出
在进行文件读写操作时,要确保缓冲区的大小足够容纳要读写的数据,如果缓冲区过小,可能会导致数据丢失或程序崩溃,在读取一行文本时,如果缓冲区大小小于实际行的长度,那么超出缓冲区的部分将被丢弃。
2、文件打开模式的匹配
当以某种模式打开文件进行写操作时,再次以不兼容的模式打开该文件可能会导致数据丢失或错误,以"w"
模式打开一个已经存在的文件并写入数据,将会清空该文件的内容;如果之后又以"r"
模式打开该文件读取数据,将无法读取到之前写入的数据。
3、文件指针的位置
在进行文件读写操作时,要注意文件指针的位置,每次读写操作都会改变文件指针的位置,因此在进行下一次读写操作之前,可能需要重新定位文件指针,在读取完一行数据后,如果要继续读取下一行数据,需要将文件指针移动到下一行的开头。
4、错误处理
在文件操作过程中,可能会出现各种错误,如文件不存在、磁盘空间不足、权限不足等,在进行文件操作时,应该及时检查函数的返回值,并进行相应的错误处理,在打开文件失败时,应该提示用户并退出程序。
C语言中的文件存储涉及到多个方面的知识和操作,通过掌握这些知识,可以方便地实现数据的持久化存储和读取,为程序的开发和应用提供有力的支持。
问题1:为什么在以“r”模式打开一个不存在的文件时,fopen()函数会返回NULL?
答:在C语言中,当以“r”(只读)模式尝试打开一个不存在的文件时,fopen()
函数会返回NULL
,这是因为“r”模式要求文件必须已经存在且可读,如果指定的文件不存在,操作系统无法找到该文件来满足只读打开的要求,所以fopen()
函数无法成功打开文件,从而返回NULL
作为错误指示,这是为了提醒程序员该文件不存在,需要进行错误处理,比如提示用户或者采取其他合适的措施(如创建文件后再打开等)。
问题2:在写入二进制文件时,为什么要按数据的原始格式进行读写?
答:二进制文件中的数据是以二进制形式存储的,它可以包含任何类型的数据,如图像、音频、视频等复杂数据结构,与文本文件不同,二进制数据不是简单的字符序列,不能直接按照字符的方式进行解读和处理,如果按照文本格式读写二进制文件,会导致数据解析错误、数据丢失或损坏等问题,因为二进制数据的存储和读取依赖于其特定的格式和字节顺序,只有按照原始格式进行读写操作,才能正确地保存和恢复数据的完整性和准确性,确保程序能够正确地处理这些数据(如正确显示图像、播放音频等)。