『C语言教程』7. 文件¶
T20:50:00+08:00
〇、引入¶
假如有一个函数 void printValue()
,怎么在这个函数中访问到外部的一个变量呢?
我们使通过一个指针变量来访问的:
在调用此函数并传入变量地址作为参数时,在函数内部即可通过指针 x
来访问到对应的变量。
文件也是如此,在程序外部有一个文件,我们也可以通过一个类似的 FILE*
型指针变量来访问到文件。
一、输入/输出流 与 FILE *
¶
在
<stdio.h>
中
C | |
---|---|
每一个 输入/输出流(I/O Stream) 都和一个外部的物理设备相关联(文件、标准输入流、打印机、端口号等等),
可以被一个 FILE*
类型的指针变量来表示,并可通过此指针来访问、控制 输入/输出流(I/O Stream)。
二、打开与关闭文件¶
2.1 fopen()
函数¶
C | |
---|---|
C89,C99,C11,C17等都是C语言的标准,新的标准会包含新的内容和修改,可以理解为语言的版本。
数字表示发表的年份,比如C11是2011年发表的标准。
标准C 指 C89.
参数:
filename
文件流要关联到的文件的名mode
文件访问模式
| 文件访问标志 | 含义 | 描述 | > | ------ | ---- | ---- | > |
r
| read | 打开一个存在的文件用来 读 (若不存在则返回空指针) | > |w
| write | 创建一个文件用来 写(若文件存在则覆盖) | > |a
| append | 打开/创建一个文件用来 在尾部追加写 | > |r+
| read extended | 打开一个存在的文件用来 读/写 (若不存在则返回空指针) | > |w+
| write extended | 创建一个文件用来 读/写(若文件存在则覆盖) | > |a+
| append extended| 打开/创建一个文件用来 读/在尾部追加写 |
Text Only 1
> 上述每一个都可以添加 `b` 标志来表示以二进制方式打开
2.2 fclose()
函数¶
C | |
---|---|
用完了要记得关闭,取消对文件的占用。
Closes the given file stream. Any unwritten buffered data are flushed to the OS. Any unread buffered data are discarded.
Whether or not the operation succeeds, the stream is no longer associated with a file, and the buffer allocated by setbuf or setvbuf, if any, is also disassociated and deallocated if automatic allocation was used.
The behavior is undefined if the value of the pointer stream is used after fclose returns. 成功返回0,否则返回EOF(-1)。
三、读写文件¶
2.1 一些函数¶
先来看普通的读入、输出函数:
-
读入:
-
int getchar(void);
从
stdin
中读入一个字符。成功返回读到的字符,失败返回
EOF
。 -
char *gets( char *str );
从
stdin
中读入字符串,存入str中(以换行或文件结束为终止)。成功则返回读到的字符串,失败则返回
NULL
空指针。 -
int scanf( const char *format, ... );
略。
-
输出:
-
int putchar( int ch );
向
stdout
中写入一个由ch
转化为的字符。成功则返回写入的字符,失败则返回
EOF
。 -
int puts( const char *str );
向
stdout
中写入字符串str
和一个\n
。成功则返回一个非负值,失败则返回
EOF
。 -
int printf( const char *format, ... );
略。
对于从特定的 输入/输出流 读入、输出,有以下函数:
这里 fgets()
比较特殊,与 gets()
有个较大区别,多了一个参数 count
表示每次读入最多读入的字符数量,即每次读入不只截止于换行、文件末尾,还会截止于读入 count
个字符后,并且不会忽略 \n
。
同时 fputs()
与 puts()
有个区别,不会在末尾添加 \n
。
两个新的函数:
C | |
---|---|
参数:
buffer
存储所读到的数据的地址size
读到每一个对象的字节数count
读的对象数stream
要执行读操作的流
返回值:
Number of objects read successfully, which may be less than count
if an error or end-of-file condition occurs.
If size
or count
is zero, fread
returns zero and performs no other action.
C | |
---|---|
参数同理,buffer
为要写入的数据起始地址。
返回值同理,The number of objects written successfully, which may be less than count
if an error occurs.
If size
or count
is zero, fwrite
returns zero and performs no other action.
2.2 基础读与基础写 r
w
¶
getc
在读到文件末尾后再读就会会失败,会返回 EOF
(End Of File)。
EOF
是一个常量,值为 -1
,被定义在在 <stdio.h>
中:
C | |
---|---|
2.3 二进制读与二进制写 rb
wb
¶
2.4 二进制读写与普通读写的区别¶
单位不同:
- 普通读写:字符
- 二进制读写:字节
数据不同:
- 普通读写:字符
- 二进制读写:二进制数据
比如同样存储一个数字 -2147483648
:
- 普通读写:
Text Only | |
---|---|
最终写入的数据为
Text Only | |
---|---|
对应
ASCII
码的二进制表示
- 二进制读写:
最终写入的数据为
Text Only | |
---|---|
-2147483648
的二进制表示
2.5 理解文件位置指针¶
文件位置指针决定了我们在使用诸如 fgetc()
, fgets()
等函数从文件中读取内容时读取到的是哪个位置的数据,每一次读取时均是从文件位置指针所在位置开始读,读完后将文件位置指针移动到之后的位置。
假如一个文件有如下内容(方括号代表一个字符(以文本模式打开)或一个字节(以二进制模式打开)):
Text Only | |
---|---|
0
处:
Text Only | |
---|---|
fgetc()
后会获取到文件位置指针所在位置的字符,并将其后移一个字符:
Text Only | |
---|---|
Text Only | |
---|---|
(此处的内容其实就是EOF)
同理,如果是如下的字符串,文件位置指针在读后会移动到字符串尾后一个字符的位置:
2.5.1 ftell()
函数¶
C | |
---|---|
获得
stream
流的文件位置指针位置
以内容如下的文件为例:
运行此程序,清晰地显示出了读入前后文件位置指针的变化:
C | |
---|---|
输出如下:
Text Only | |
---|---|
windows
下换行为\r
\n
,而linux
中为\n
。
\r
是 回车,光标回到本行行首\n
是 换行,光标移到下一行这一点细节要时常注意,经常因为忽略
\r
的存在而产生很多错误。
再看下面这个以字符串读入的例子:
C | |
---|---|
输出如下:
这样应该就理解了文件位置指针的概念了吧。
(刚才文件的文件位置指针对应关系如下:
2.5.2 rewind()
¶
C | |
---|---|
将
stream
流的 文件位置指针 移回起始位置
2.5.3 fseek()
¶
C | |
---|---|
设置文件读取指针的位置
参数:
- stream
文件流
- offset
偏移量
- origin
偏移量起始位置
- SEEK_SET
文件开始
- SEEK_CUR
当前位置
- SEEK_END
文件结尾
如果文件流是在 二进制模式 打开的,新的位置就确切的是 offset
所给的字节数造成的偏移。
注意:二进制模式下不能使用
SEEK_END
Binary streams are not required to support SEEK_END, in particular if additional null bytes are output.
If the stream is open in text mode, the only supported values for offset are zero (which works with any origin) and a value returned by an earlier call to ftell on a stream associated with the same file (which only works with origin of SEEK_SET).
If the stream is wide-oriented, the restrictions of both text and binary streams apply (result of ftell is allowed with SEEK_SET and zero offset is allowed from SEEK_SET and SEEK_CUR, but not SEEK_END).
In addition to changing the file position indicator, fseek undoes the effects of ungetc and clears the end-of-file status, if applicable.
If a read or write error occurs, the error indicator for the stream (ferror) is set and the file position is unaffected.
2.6 基础写与二进制写 a
ab
¶
以 a
模式打开文件时,文件位置指针位于起始位置(0),但是一旦执行任何写操作,会立即将其移动到文件末尾,并开始写入。
2.7 基础读写 r+
w+
a+
¶
方法 | 若文件不存在 | 若文件存在 | 初始文件位置指针的位置 |
---|---|---|---|
r+ |
返回 空指针 NULL |
--- | 0 |
w+ |
创建文件 | 覆盖 文件 | 0 |
a+ |
创建文件 | --- | 0 |
输入输出共用一个文件位置指针。
-
a+
当文件位置指针不在末尾时(文件位置指针所在位置已有内容时),输出函数都会成功,但不会覆盖掉文件中得分内容;当文件位置指针在末尾时,才能够实质性的向文件中写入内容。 -
w+
当文件位置指针不在末尾时(文件位置指针所在位置已有内容时),输出会替换掉那个位置的内容。
例:
C 文件内容为:
r+
与w+
的区别在于,当文件存在时不会覆盖文件。
2.8 二进制读写 rb+
wb+
ab+
¶
由题可知,不难发现,显然得到,略。
发挥你的主观能动性吧。