这篇继续说一下IO中常见的函数以及标准IO的机理。
内容在书13.4,13.5,13.6章。
1 文件IO函数
上一篇介绍的函数都类似于文件IO函数。主要区别在于文件IO需要用FILE*指定待处理的文件。
而接下来介绍的函数则使用 指向FILE的指针(如stdout)指定一个文件。
1.1 fprintf_s()与fscanf_s()
这两个函数的工作方式与printf()和scanf()类似,区别在于这两个函数的第一个参数需要指定待处理的文件。
1.1.1 将键盘输入的文字记录至磁盘并从磁盘中读出再输出
//将键盘输入内容写入到磁盘再读取并输出
#include <stdio.h>
#include <string.h>
#define LENGTH 128
int main(void)
{
char out[] = "J:\\IO_Write\\keyboard.txt";
show_write_keyboard_input(out);
return 0;
}
int show_write_keyboard_input(char* path)
{
FILE* out;
char file_name[LENGTH];
char string[LENGTH];
printf("请在看到这一行字之后开始输入文字,若输入#则代表结束输入。\n");
strncpy_s(file_name, LENGTH - 8, path, LENGTH - 8);
file_name[LENGTH - 8] = '\0';
fopen_s(&out, file_name, "a+");//a+:追加模式
while ((fscanf_s(stdin, "%s", string, 120) == 1) && string[0] != '#')//第一个字符为#则中断输入
{
fprintf_s(out, "%s", string);
}
rewind(out);//指针返回文件开始处
printf_s("现在开始输出刚才从键盘输入的内容:\n");
while (fscanf_s(out, "%s", string, 120) == 1)
{
fprintf_s(stdout, "%s\n", string);
}
return 0;
}
运行结果
请在看到这一行字之后开始输入文字,若输入#则代表结束输入。
g4f1g1f1g1f1f1
fdsdgd544651
411fdsggffsg#46465
#
现在开始输出刚才从键盘输入的内容:
g4f1g1f1g1f1f1fdsdgd544651411fdsggffsg#46465
1.2 fgets()与fputs()
下面的表格简单的对比了这两个函数。
函数 | 参数 | 返回值 |
---|---|---|
fgets() | char*,待输入字符串大小(int), FILE* | char* |
fputs() | char*, FILE* | int |
fgets()将会读取输入直到第一个换行符的后面,或文件结尾,或STRLEN-1个字符。随后会在字符串末尾添加一个空字符表示这是一个字符串。在读取到EOF时会返回NULL
**fputs()**与puts()的不同在于,fputs()在输出字符串时不会在末尾为其添加换行符。
2 随机访问
2.1 fseek()与ftell()
fseek()可以将文件看成一个数组,在fopen()打开的文件中移动到任意字节处。
ftell() 则是用于得到文件位置指针当前位置相对于**文件首的偏移**字节数
2.1.1 倒序输出文件的内容
#include <string.h>
#define LENGTH 128
int main(void)
{
//文件内容是26个字母小写a-z,大写A-Z,数字1234567890
char path[] = "J:\\IO_Write\\random.txt";
random_read_input(path);
return 0;
}
int random_read_input(char* path)
{
FILE* in;
char input[LENGTH];
char c;
long count, last;
strncpy_s(&input, LENGTH - 8, path, LENGTH - 8);
input[LENGTH - 8] = '\0';
fopen_s(&in, input, "r");
fseek(in, 0L, SEEK_END); //定位到文件末尾
last = ftell(in);
for ( count = 1L; count <= last; count++)
{
fseek(in, -count, SEEK_END); //从最后往最前面读取
c = getc(in);
putchar(c);
}
printf("\n");
fclose(in); //关闭文件
return 0;
}
运行结果
0987654321ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba
2.1.2 fseek()的工作原理
可以通过下面的表格对比一下这两个函数。
函数 | 参数 | 返回值 |
---|---|---|
fseek() | FILE*, 偏移量(long), 模式(int) | int |
ftell() | FILE* | long |
对于fseek()中的偏移值,可以是证书,可以是负数,也可以是0,但是必须加上L转换为long类型。
第三个参数,则是确定起始点。下面是C语言预定义的常量。
模式 | 偏移量起始点 | 旧实现的替代值 |
---|---|---|
SEEK_SET | 文件开始处 | 0L |
SEEK_CUR | 当前位置 | 1L |
SEEK_END | 文件结尾 | 2L |
看看下面的函数,想想代表了什么。
fseek(in, 10L, SEEK_SET); //定位至文件开始第10字节
fseek(in, -5L, SEEK_CUR); //定位至当前位置后第5字节
fseek(in, -15L, SEEK_END); //文件结尾处回退15字节
2.2 fgetpos()与fsetpos()
这两个函数主要是解决fseek()与ftell()的文件大小只有long范围的问题。
这两个函数用fpos_t代替了原来的long类型。
3 标准IO的机理
对于文件的IO,第一步操作一般都是使用fopen_s()打开文件,该函数同时还创建了一个缓冲区。
第二步则是通过输入函数读取内容,可能是标准输入流,也可能是一个文件。一旦调用这些函数,文件的数据块就会被拷贝至缓冲区。缓冲区的大小在不同的实现中有不同的数值,一般都是512字节的倍数。最初调用时,除填充缓冲区之外,还需要设置FILE*指向结构的值。
初始化完成并已经将缓冲区所有字符读取完毕后,会将下一块缓冲区大小的数据读取至缓冲区,重复上面的操作直至将整个文件读取完毕。
输出函数操作与读取输入操作相反,当缓冲区被填满时,数据将会被拷贝至文件中。