目录
在C语言编程中,文件操作是一项非常重要的技能。通过文件操作,我们可以实现数据的持久化存储、读取外部数据源等功能。
1.什么是文件
在C语言中,文件处理是一个强大的功能,它允许程序与持久化存储介质(如硬盘)上的文件进行交互。这种交互可以包括读取程序所需的数据、存储程序产生的结果或简单地操作和管理文件。
1.1 程序文件和数据文件
在C语言中,我们通常将源代码文件(如
.c
文件)称为程序文件,它们包含了程序执行的指令。当这些文件被编译器编译后,会生成可执行文件(如Windows下的.exe
文件或Linux下的可执行文件)。另一方面,数据文件是用于存储程序所需或产生的数据的文件。这些数据文件可以是文本文件,也可以是二进制文件。文本文件包含人类可读的文本数据,而二进制文件则包含程序可以直接解析的二进制数据。
1.2 文件名操作
在C语言中,文件名通常用于标识和引用文件。文件名可以包含路径信息,以便程序能够找到存储在文件系统不同位置的文件。
1.2.1 路径分隔符
在不同的操作系统中,路径分隔符可能不同。在Windows系统中,通常使用反斜杠(
\
)作为路径分隔符,而在UNIX和Linux系统中,则使用正斜杠(/
)。在C语言中编写跨平台的代码时,最好使用\\
(在字符串中使用转义字符)或/
(在大多数系统中都有效)作为路径分隔符。
1.2.2 拼接文件路径
在C语言中,可以使用字符串操作函数(如
strcat
)来拼接文件路径和文件名。这允许程序动态地构建文件路径,以便在不同的位置查找或创建文件。示例:拼接文件路径
1.3 文件操作函数
C语言提供了一组标准库函数来执行文件操作,包括打开文件、关闭文件、读取文件、写入文件等。以下是一些常用的文件操作函数:
fopen
:打开文件并返回一个文件指针。fclose
:关闭已打开的文件。fread
:从文件中读取数据。fwrite
:将数据写入文件。fgetc
:从文件中读取一个字符。fputc
:将一个字符写入文件。fgets
:从文件中读取一行文本。fputs
:将字符串写入文件。fseek
:移动文件的位置指针。ftell
:返回文件位置指针的当前位置。
1.4 二进制文件与文本文件
在C语言编程中,文件操作是一个重要的组成部分。根据文件内容的存储方式,我们可以将文件分为文本文件和二进制文件。
1.4.1 文本文件
文本文件是以文本形式存储数据的文件,其中数据以字符序列的形式存在。在C语言中,我们可以使用标准库函数来读取和写入文本文件。
文本文件的写入
在C语言中,我们可以使用
fprintf
函数或fputs
函数来写入文本文件。fprintf
函数允许我们按照指定的格式将数据写入文件,而fputs
函数则直接将字符串写入文件。示例代码(使用
fprintf
函数):
文本文件的读取
读取文本文件时,我们可以使用
fscanf
函数或fgets
函数。fscanf
函数允许我们按照指定的格式从文件中读取数据,而fgets
函数则读取文件中的一行。示例代码(使用
fgets
函数):
1.4.2 二进制文件
二进制文件是以二进制形式存储数据的文件,其中数据以字节为单位进行存储。在C语言中,我们可以使用fread
和fwrite
函数来读写二进制文件。
二进制文件的写入
使用
fwrite
函数可以将数据以二进制形式写入文件。该函数需要指定要写入的数据的指针、每个数据项的大小、要写入的数据项的数量以及文件指针。示例代码:
二进制文件的读取
读取二进制文件时,我们使用
fread
函数。与fwrite
类似,该函数也需要指定要读取的数据的指针、每个数据项的大小、要读取的数据项的数量以及文件指针。示例代码:
2.文件的打开和关闭
2.1 流和标准流
在C语言中,流是一个重要的概念,用于在程序内部和外部数据源(如文件、控制台等)之间传输数据。标准流则是预定义的、用于标准输入/输出操作的流。
2.1.1 流
在C语言中,流是一种抽象的数据类型,它代表了数据的源或目标。流可以是文件、控制台、网络套接字等任何可以接收或发送数据的设备或数据源。流提供了一种方便的方式来处理这些数据源,使程序员无需关心数据的具体来源或去向。
在C语言中,流是通过文件指针(
FILE *
)来访问的。文件指针是一个指向FILE
结构体的指针,该结构体包含了流的所有状态信息(如缓冲区、文件位置等)。通过文件指针,我们可以调用各种函数来读取、写入、定位或关闭流。
2.1.2 标准流
标准流是C语言中预定义的三个流,用于标准输入/输出操作。它们分别是:
- 标准输入流:用于从键盘或其他输入设备读取数据。在C语言中,可以使用
scanf
、getchar
等函数从stdin
读取数据。- 标准输出流:用于将数据输出到控制台或屏幕。在C语言中,可以使用
printf
、putchar
等函数将数据写入stdout
。- 标准错误流:用于输出错误信息或诊断信息。与
stdout
类似,但通常用于输出需要引起注意的重要信息。在C语言中,可以使用fprintf
、fputs
等函数将数据写入stderr
。标准流是全局的,并且始终可用,无需程序员显式打开或关闭。它们是通过宏在
<stdio.h>
头文件中定义的,分别对应三个预定义的文件指针:stdin
、stdout
和stderr
。
2.1.3 使用标准流
下面是一个简单的示例程序,演示了如何使用标准流进行输入和输出操作:
在上面的示例中,我们首先使用
printf
函数将提示信息输出到标准输出流。然后,我们使用scanf
函数从标准输入流读取一个整数。接下来,我们再次使用printf
函数将读取到的整数输出到标准输出流。最后,如果输入的整数小于0,我们使用fprintf
函数将错误信息输出到标准错误流。
2.2 文件指针
在C语言中,文件操作是编程中常见的任务之一。为了实现对文件的读写操作,C语言提供了文件指针这一重要概念。文件指针是一个指向FILE
结构体的指针,该结构体包含了文件的各种信息,如文件的位置、缓冲区等。通过文件指针,我们可以对文件进行打开、读写、关闭等操作。
2.2.1 文件指针的定义
在C语言中,文件指针通常被定义为
FILE *
类型。例如:这里,
fp
就是一个文件指针变量,用于存储文件操作的相关信息。
2.2.2 文件指针的初始化
文件指针在使用之前需要进行初始化,也就是要打开一个文件。C语言提供了
fopen
函数来打开文件,该函数返回一个FILE *
类型的指针。例如:
在上面的代码中,
fopen
函数尝试以只读方式("r")打开名为"filename.txt"的文件,并将返回的文件指针赋值给fp
。如果文件打开失败(如文件不存在或没有读取权限),则fopen
返回NULL
,此时我们应该检查fp
是否为NULL
并处理错误。
2.2.3 文件指针的读写操作
一旦文件被成功打开,我们就可以使用文件指针进行读写操作了。C语言提供了多个函数来实现这一功能,如fread
、fwrite
、fscanf
、fprintf
等。
2.2.3.1 读取文件
以下是一个使用
fgets
函数从文件中读取一行文本的示例:在这个例子中,
fgets
函数从fp
指向的文件中读取一行文本,并将其存储在buffer
数组中。然后,我们使用printf
函数将读取到的内容输出到控制台。
2.2.3.2 写入文件
以下是一个使用
fprintf
函数将数据写入文件的示例:在这个例子中,
fprintf
函数将字符串"Hello, World!\n"写入到fp
指向的文件中。注意,这里使用的是和printf
函数类似的格式字符串。
2.2.3.3 文件指针的关闭
在使用完文件后,我们应该使用
fclose
函数来关闭文件。关闭文件可以释放与文件关联的资源,并确保所有的输出都被正确地写入到文件中。例如:在这个例子中,
fclose
函数关闭了由fp
指向的文件。
2.2.3.4 注意事项
- 检查文件打开是否成功:在使用文件指针之前,一定要检查文件是否成功打开。如果文件打开失败,后续的读写操作将无法正常进行。
- 正确关闭文件:在使用完文件后,一定要关闭文件。不关闭文件可能会导致数据丢失或文件损坏。
- 注意文件路径:在打开文件时,需要提供正确的文件路径。如果路径不正确,文件将无法被打开。
- 注意文件读写模式:在打开文件时,需要指定正确的读写模式。不同的读写模式会影响文件的访问权限和数据的处理方式。
- 注意缓冲区:C语言中的文件操作通常是基于缓冲区的。这意味着在写入文件时,数据可能先被写入到缓冲区中,而不是直接写入到文件中。因此,在关闭文件之前,需要确保所有的输出都被刷新到文件中。可以使用
fflush
函数来刷新缓冲区。
2.3 文件的打开和关闭
在C语言中,文件的操作是编程中不可或缺的一部分。无论是读取配置文件、保存用户数据还是进行日志记录,文件操作都扮演着重要的角色。
2.3.1 文件的打开
在C语言中,文件的打开是通过fopen
函数实现的。该函数接受两个参数:要打开的文件名和打开模式,并返回一个FILE *
类型的指针,该指针指向一个用于后续文件操作的FILE
结构体。
fopen函数原型
filename
:要打开的文件名(包括路径,如果需要的话)。mode
:打开文件的模式,可以是以下值之一或它们的组合:
"r"
:只读模式。文件必须存在。"w"
:只写模式。如果文件不存在,则创建它;如果文件存在,则清空其内容。"a"
:追加模式。如果文件不存在,则创建它;如果文件存在,则数据将被写入到文件末尾,而不会覆盖原有内容。"r+"
:读写模式。文件必须存在。"w+"
:读写模式。如果文件不存在,则创建它;如果文件存在,则清空其内容。"a+"
:读写模式。如果文件不存在,则创建它;如果文件存在,则数据将被写入到文件末尾,而不会覆盖原有内容。示例代码
在上面的示例中,我们尝试以只读模式打开名为"example.txt"的文件。如果文件打开失败(例如文件不存在或没有读取权限),则
fopen
函数返回NULL
,我们需要检查并处理这个错误。
2.3.2 文件的关闭
在完成对文件的操作后,应该使用fclose
函数来关闭文件。关闭文件可以释放与文件关联的资源,并确保所有的输出都被正确地写入到文件中。
fclose函数原型
stream
:要关闭的文件的指针。
fclose
函数返回一个整数值,如果关闭成功则返回0,否则返回EOF(通常是-1)。然而,在实际编程中,我们通常只检查fclose
函数是否返回了非零值,以判断关闭操作是否失败。示例代码
在上面的示例中,我们使用
fclose
函数来关闭之前打开的文件。如果关闭失败,则fclose
函数返回非零值,我们可以根据需要进行错误处理。
2.3.3 注意事项
- 检查文件是否成功打开:在使用文件指针之前,一定要检查文件是否成功打开。如果文件打开失败,后续的读写操作将无法正常进行。
- 正确关闭文件:在使用完文件后,一定要关闭文件。不关闭文件可能会导致数据丢失或文件损坏,同时也可能会占用系统资源。
- 避免文件指针泄漏:在函数内部打开的文件,应该在函数结束前关闭。如果需要在函数之间传递文件指针,请确保正确地管理这些指针的生命周期。
- 处理错误:在使用文件操作时,应该始终检查函数的返回值以处理可能出现的错误。例如,如果
fopen
返回NULL
,则表示文件打开失败;如果fclose
返回非零值,则表示文件关闭失败。- 文件路径:在打开文件时,需要提供正确的文件路径。如果路径不正确或文件不存在,文件将无法被打开。可以使用相对路径或绝对路径来指定文件的位置。
- 文件权限:在打开文件时,需要确保程序具有足够的权限来访问该文件。如果程序没有足够的权限,文件将无法被打开或写入。
3.文件的顺序读写
在C语言中,文件的顺序读写是文件操作的基本形式之一。顺序读写意味着数据按照它们在文件中的存储顺序进行读取或写入。
3.1 打开文件
在进行文件的读写操作之前,首先需要打开文件。这可以通过
fopen
函数实现,该函数返回一个指向FILE
结构体的指针,用于后续的文件操作。在上面的示例中,我们尝试以只读模式("r")打开名为"example.txt"的文件。如果文件打开失败,
fopen
将返回NULL
,此时我们需要进行错误处理。
3.2 顺序读取文件
使用
fgets
函数读取一行
fgets
函数用于从文件中读取一行文本。它读取指定数量的字符(包括换行符和空字符)或读取到行尾(EOF),并将它们存储在提供的缓冲区中。在这个示例中,我们使用
fgets
函数从file
指向的文件中读取一行文本,并将其存储在buffer
数组中。然后,我们使用printf
函数将读取到的内容输出到控制台。当fgets
返回NULL
时,表示已经读取到文件末尾。使用
fscanf
函数格式化读取
fscanf
函数允许我们按照指定的格式从文件中读取数据。例如,我们可以从文件中读取整数或浮点数。在这个示例中,我们使用
fscanf
函数从文件中读取整数,并将其存储在number
变量中。然后,我们使用printf
函数将读取到的整数输出到控制台。当fscanf
的返回值不等于1时(通常是因为读取失败或已经到达文件末尾),我们停止循环。
3.3 顺序写入文件
使用
fputs
函数写入一行
fputs
函数用于将字符串写入文件。它接受两个参数:要写入的字符串和文件指针。在这个示例中,我们使用
fputs
函数将字符串"Hello, World!\n"写入到file
指向的文件中。使用
fprintf
函数格式化写入
fprintf
函数允许我们按照指定的格式将数据写入文件。它的用法与printf
函数类似,但第一个参数是文件指针。在这个示例中,我们使用
fprintf
函数将整数42以字符串形式写入到file
指向的文件中。
3.4 关闭文件
在完成对文件的读写操作后,应该使用
fclose
函数来关闭文件。这可以释放与文件关联的资源,并确保所有的输出都被正确地写入到文件中。如果
fclose
函数返回非零值,则表示关闭文件时发生了错误。我们应该检查并处理这个错误。
3.5 注意事项
- 检查文件是否成功打开:在进行文件操作之前,一定要检查文件是否成功打开。如果文件打开失败,后续的读写操作将无法正常进行。
- 正确处理文件结束:在读取文件时,要注意检查是否已经到达文件末尾。可以使用
feof
函数来检查文件结束标志。- 避免缓冲区溢出:在读取文件时,要确保提供的缓冲区足够大,以避免缓冲区溢出。可以使用
fgets
函数的第二个参数来指定要读取的最大字符数。- 注意文件路径和权限:在打开文件时,需要提供正确的文件路径,并确保程序具有足够的权限来访问该文件。
- 关闭文件:在完成对文件的操作后,一定要关闭文件以释放资源。不关闭文件可能会导致数据丢失或文件损坏。
4. 文件的随机读写
4.1 fseek
fseek
函数用于设置文件的位置指针。它允许你将文件指针移动到文件的任意位置,以便从那里开始读取或写入。函数的原型如下:
stream
:这是指向FILE
对象的指针,表示要操作的文件。offset
:表示相对于whence
的偏移量,以字节为单位。whence
:这是一个参数,指定从哪个位置开始计算偏移量。它可以是以下三个常量之一:
SEEK_SET
:文件的开头。SEEK_CUR
:当前的位置。SEEK_END
:文件的末尾。如果成功,
fseek
返回0;如果失败,返回非零值。示例
4.2 ftell
ftell
函数返回当前文件位置指针的偏移量(相对于文件开头的字节数)。它的原型如下:
stream
:这是指向FILE
对象的指针,表示要操作的文件。
ftell
返回当前位置指针的偏移量,如果发生错误,则返回-1L。示例
4.3 rewind
rewind
函数将文件位置指针重置为文件的开头。它实际上等同于fseek(stream, 0, SEEK_SET)
。函数的原型如下:
stream
:这是指向FILE
对象的指针,表示要操作的文件。示例
5.文件读取结束的判定
在C语言中,当我们从文件中读取数据时,经常需要判断是否已经读取到文件的末尾,以便正确地处理文件数据并避免越界访问。C语言提供了几种方法来检测文件读取是否结束,其中最常见的是使用文件结束标志(EOF)和feof
函数。
5.1 文件结束标志(EOF)
在C语言中,文件结束标志(EOF)是一个特殊的常量,用于表示文件读取的结束。EOF通常被定义为-1,但这不是一个固定的值,而是由系统或编译器定义的。当使用文件I/O函数(如
fgetc
、fgets
、fscanf
等)读取文件时,如果到达文件末尾,这些函数将返回EOF。
5.2 使用feof
函数
除了直接检查文件I/O函数的返回值是否为EOF外,还可以使用
feof
函数来检测文件是否已经读取到末尾。feof
函数接受一个文件指针作为参数,并返回一个整数值,用于指示文件是否已经到达末尾。如果文件已经到达末尾,feof
将返回非零值(通常为1);否则返回零。以下是一个使用
feof
函数判断文件读取结束的示例:在上面的示例中,我们使用
while (!feof(file))
循环来读取文件内容,直到文件结束。在每次循环中,我们使用fgets
函数从文件中读取一行数据,并检查其返回值是否为NULL。虽然fgets
在到达文件末尾时会返回NULL,但直接在while
循环中检查fgets
的返回值可能会导致在最后一次读取时跳过数据(因为当fgets
返回NULL时,循环已经终止)。因此,我们通常将feof
作为循环条件,并在循环内部检查fgets
的返回值。
6. 文件缓冲区
ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为 程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓 冲区的大小根据C编译系统决定的。