今天学习标准IO的概念,特点,缓存区,文件IO函数的相关知识。
目录
标准IO
文件类型:
b c d -(普通文件) l s p
一、概念
标准IO就是C库中定义的一组用于输出输出的函数
二、特点
1.有缓存区(缓冲机制)
通过缓冲机制减少系统调用(内核向上提供的一组接口)的次数,提高效率
2.围绕流进行操作
流用FILE*来描述,FILE代表的是结构体,描述所操作文件的信息
3.默认打开了三个流
stdin(标准输入)、stdout(标准输出)、stderr(标准错误)
sudo ctags -R的使用 (追代码)
vi -t FILE(typedef定义数据类型、宏定义、结构体等)将光标定位在目标位置,ctrl+] :向下追代码
ctrl+t:回退
q:退出
三、缓存区
1.行缓存:和终端操作相关
刷新行缓存的条件:
1.程序正常退出
2.\n刷新3.缓存区满
4.强制刷新
判断缓存区大小的两种方式:
1.利用for循环判断缓存区大小
for(int i=0;i<300;i++)
{
printf("%4d",i);//打印0-255,说明缓存区大小为256*4=1024
}
while(1);
2.利用stdout
// 前提要启动缓存区,先向缓存区中写入一些数据
printf("缓存区空间大小为:");
// 通过stdout访问结构体中的成员,缓存区的结束地址和起始地址
printf("%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base); // 1024
强制刷新:
int main(int argc, char const *argv[])
{
printf("hello");
fflush(NULL); // 强制刷新 可以通过man fflush查看,int fflush(FILE *stream);
while (1);
return 0;
}
2.全缓存:和文件操作相关
刷新全缓存的条件:
1.程序正常退出
2.缓存区满
3.强制刷新 fflush
3.不缓存:标准错误 stderr
四、函数
1.打开文件
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
参数:
path:打开文件
mode:打开方式
r:只读,流被定位到文件开头
r+:可读可写,流被定位到文件开头
w:只写,文件不存在创建,文件存在清空,流被定位到文件开头
w+:可读可写,文件不存在创建,文件存在清空,流被定位到文件开头
a:追加,文件不存在创建存在追加,流被定位到文件末尾
a+:可读可写,文件不存在创建,存在追加,开始进行读时从头读,进行写时流被定位到文件末尾
返回值: 成功:文件流
失败:NULL,并且设置errno(错误码)
2.关闭文件
#include<stdio.h>
int fclose(FILE* stream);
功能:关闭文件
参数:stream:文件流
打开关闭文件案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "r");
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
// 关闭文件
fclose(fp);
return 0;
}
3.读写文件
(1)每次一个字符的读写
fgetc
#include <stdio.h>
int fgetc(FILE *stream);
功能:从文件中读一个字符
参数:stream:文件流
返回值:成功:读到字符
失败或读到文件末尾:EOF(-1)
补充:
#include <stdio.h>
int ferror(FILE *stream);
功能:判断读文件时是否出错
返回值:非0表示出错int feof(FILE *stream);
功能:判断读文件时是否到文件末尾
返回值:非0表示读到文件末尾
fgetc的案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "r");
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
int ch = fgetc(fp); // 调用一次读一个字符,多次调用继续向后读取字符
if (ch == EOF)
{
if (ferror(fp))
{
perror("读文件失败");
return -1;
}
if (feof(fp))
{
perror("已经读到了文件的末尾");
}
}
else
{
printf("%c\n", ch);
}
// 关闭文件
fclose(fp);
return 0;
}
fputc
#include <stdio.h>
int fputc(int c, FILE * stream)
功能:向文件中写入一个字符
参数:c:要写的字符
stream:文件流
返回值:成功:写的字符的ASCII
失败:EOF
fputc的案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "w+");
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
fputc('h', fp);
fputc(65, fp);
rewind(fp);
int ch = fgetc(fp);
printf("%c\n", ch);
ch = fgetc(fp);
printf("%c\n", ch);
// 关闭文件
fclose(fp);
return 0;
}
(2)每次一个字符串的读写
fgets
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取一串字符
参数:s:存放读取的字符串的首地址
size:读取的大小
stream:文件流
返回值:成功:读取的字符串的首地址
失败或读到文件末尾:NULL
特性:1.一次调用最多读取一行数据
2.实际读到个数为size-1个,末尾自动添加\0
fgets案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "r");
char buf[32];
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
fgets(buf, 6, fp);
printf("%s\n", buf);
fgets(buf, 2, fp);
printf("%s\n", buf);
for (int i = 0; i < 6; i++)
{
printf("%c ", buf[i]);
}
printf("\n");
// 关闭文件
fclose(fp);
return 0;
}
fputs
#include <stdio.h>
int fputs(const char *s, FILE *stream);
功能:向文件中写字符串
参数:s:要写的内容
stream:文件流
返回值:成功:非负整数
失败:EOF
fputs案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "r+");
char buf[32];
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
fputs("hello", fp);
rewind(fp);
fgets(buf, 6, fp);
printf("%s\n", buf);
// 关闭文件
fclose(fp);
return 0;
}
(3)二进制读写文件
可以读写任意类型的数据,以二进制的方式进行读写
fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素
参数: ptr :用来存放读取元素 (可以用来读取任意类型的数据)
size :元素大小 sizeof(数据类型)
nmemb :读取对象的个数
stream :要读取的文件
返回值:成功:读取对象的个数
失败或读到文件尾:0
fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:按对象写
参数:同上
返回值:成功:写的元素个数
失败 :0
二进制读写文件案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "w+");
int arr[3] = {1, 2, 3};
int arr1[3] = {0};
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
// 四个参数分别为:要写的数据地址,一个数据的大小,数据的个数,文件流
fwrite(arr, sizeof(int), 3, fp);
rewind(fp); // 将文件的位置指针定位到开头位置
fread(arr1, sizeof(int), 3, fp);
for (int i = 0; i < 3; i++)
{
printf("%d ", arr1[i]);
}
printf("\n");
// 关闭文件
fclose(fp);
return 0;
}
4.文件定位操作
rewind
void rewind(FILE *stream);
功能:将文件位置指针定位到起始位置
fseek
int fseek(FILE *stream, long offset, int whence);
功能:文件的定位操作
参数:stream:文件流
offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
whence:相对位置:
SEEK_SET:相对于文件开头
SEEK_CUR:相对于文件当前位置
SEEK_END:相对于文件末尾
返回值:成功:0
失败:-1
fseek案例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "w+");
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
fseek(fp, 5, SEEK_SET);
fputc('A', fp);
fseek(fp, -4, SEEK_CUR);
fputc('B', fp);
// 关闭文件
fclose(fp);
return 0;
}
ftell
long ftell(FILE *stream);
功能:获取位置指针当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置失败:-1
ftell案例
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen("./test.txt", "w+");
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
fseek(fp, 5, SEEK_SET);
fputc('A', fp);
fseek(fp, -4, SEEK_CUR);
fputc('B', fp);
long l = ftell(fp);
printf("%ld\n", l);
//计算文件中字符的个数
fseek(fp, 0, SEEK_END);
l = ftell(fp);
printf("%ld\n", l);
// 关闭文件
fclose(fp);
return 0;
}
补充:
1.rewind(fp)和fseek(fp, 0, SEEK_SET)等价
2.可以通过此函数计算文件中字符个数
3.当打开文件的方式为a或a+时,fseek不起作用
五、小练习
1.编程实现cat命令功能。
思路:打开文件,循环读文件,当读到文件末尾时循环结束,打印读到的数据,关闭文件
示例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen(argv[1], "r+");
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
for (int i = 0;; i++)
{
int ch = fgetc(fp);
if (ch == EOF)
{
if (ferror(fp))
{
perror("读文件失败");
return -1;
}
if (feof(fp))
{
break;
}
}
printf("%c", ch);
}
// 关闭文件
fclose(fp);
return 0;
}
2.编程实现wc -l命令的功能
思路:打开文件,循环读文件,当读到文件末尾时循环结束,在循环中判断是否是一行,如果是则对整型变量进行自加,关闭文件。
示例:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
// 打开文件
FILE *fp = fopen(argv[1], "r+");
char buf[100];
// 打开失败
if (NULL == fp)
{
perror("文件打开失败");
return -1;
}
printf("文件打开成功\n");
int len = 0;
while (fgets(buf, sizeof(buf), fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
{
len++;
}
}
printf("%d\n", len);
// 关闭文件
fclose(fp);
return 0;
}
3.编程实现cp命令功能。
cp 源文件 新文件名
要求:运行代码时在./a.out 后指定源文件和新文件
思路:先判断命令行参数是不是三个,分别打开两个文件,循环将源文件的内容读出来写到新文件中,循环的结束条件读到文件末尾节结束,关闭文件
示例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
if (argc != 3)
{
printf("命令行参数不是三个,请重新运行\n");
return -1;
}
FILE *fp = fopen(argv[1], "r+");
if (NULL == fp)
{
perror("原文件打开失败");
return -1;
}
printf("原文件打开成功\n");
FILE *fp1 = fopen(argv[2], "w+");
if (NULL == fp1)
{
perror("新文件打开失败");
return -1;
}
printf("新文件打开成功\n");
char buf[100];
int n;
while ((n = fread(buf, sizeof(char), sizeof(buf), fp)) != 0)
{
fwrite(buf, sizeof(char), n, fp1);
}
// 关闭文件
fclose(fp);
fclose(fp1);
return 0;
}