文章目录
前言
标准输入/输出(Standard Input/Output)是编程中非常基础且重要的概念。在C语言中,标准输入和输出通常指的是键盘和屏幕,但它们也可以重定向到文件或其他设备。
一、什么是标准IO?
- 在
C库
中定义了一组用于输入input
和输出outout
的函数 - 只能操作普通文件
- 用
FILE
结构体描述文件信息,FILE*
被称为流
对文件的操作都是围绕流进行的 - 默认打开三个流
- 标准输入
stdiin
- 标准输出
stdout
- 标准错误
stderr
- 标准输入
- 有缓存区
二、缓存区
标准I/O流有内置的缓冲机制,这意味着数据可能不会立即被写入磁盘或显示在屏幕上,而是先存储在缓冲区中,这样可以提高程序运行效率。
标准IO有三种缓存方式:行缓存、全缓存、不缓存
1.行缓存
行缓存和终端相关,如标准输出stdout
。
刷新条件:
- 换行符
\n
刷新 - 程序正常退出时刷新
- 缓存区满刷新
- 缓存区大小为 1KB(1024B)
-强制刷新fflush(NULL)
- 缓存区大小为 1KB(1024B)
2.全缓存
全缓存和文件操作相关,如fprintf()
。
刷新条件:
- 程序正常退出
- 缓存区满刷新
- 强制刷新
fflush
3.不缓存
标准错误strerr
不缓存直接输出
标准错误它的主要用途是输出错误信息,这些信息不会被重定向,通常直接输出到屏幕,即使标准输出被重定向了。
三、文件相关函数
1.打开文件
FILE *fopen(const char *path, const char *mode);
功能:以只能方式打开对应文件
参数:
path:要打开路径下的文件,是个字符串
mode:打开方式,是个字符串
r:只读,流被定位到文件开头
r+:可读可写,流被定位到文件开头
w:只写,文件不存在创建,文件存在清空,流被定位到文件开头
w+:可读可写,文件不存在创建,文件存在清空,流被定位到文件开头
a:追加,文件不存在创建,存在追加,流被定位到文件末尾
a+:可读可写,文件不存在创建,存在追加,开始进行读时从头读,进行写时流
注:这里所说的流其实就是文件中鼠标光标位置,流被定为在开头,就等于是光标在开头
返回值:
成功 返回文件流
失败 返回NULL,并且设置errno(错误码)
例子
// 打开当前路径下的test文件,只读方式
// 由FILE *类型的变量fp接收返回文件流
FILE *fp = fopen("./test", "r");
// 判断是否打开成功
if (NULL == fp)
{
perror("read err\n");
return -1;
}
2.关闭文件
int fclose(FILE* stream);
功能:关闭文件
参数:
stream:文件流
3.文件读写
通过函数可以对文件进行读取或写入操作,但文件必须具有相应的读写权限(在打开文件的时候设置)
读写一个字符
读字符:
int fgetc(FILE *stream);
功能:
从文件中读一个字符
参数:
stream:文件流
返回值:
成功:读到字符
失败或读到文件末尾:EOF(-1)
写字符:
int fputc(int c, FILE * stream)
功能:向文件中写入一个字符
参数:
c:要写的字符
stream:文件流
返回值:
成功:写的字符的ASCII
失败:EOF
例子:
//读写一个字符
//当前目录下有一个test1文件,里面只有一个字符A
#include <stdio.h>
int main()
{
// 打开当前路径下的test文件,可读写方式
// 由FILE*类型的变量fp接收返回文件流
FILE *fp = fopen("./test1", "r+");
// 判断是否打开成功
if (NULL == fp)
{
perror("read err\n");
return -1;
}
// 读取一个字符
int c = fgetc(fp);
// 打印读取字符
printf("读取到一个:%c\n",c);
// 写一个字符,并判断是否成功
c = 'B';
if(fputc(c,fp) == EOF)
{
perror("fputc error");
return -1;
}else
{
printf("写入A\n");
}
//关闭文件
fclose(fp);
return 0;
}
执行结果:
补充:
fgetc()
的返回值并不能区别是读取失败还是读到文件末尾。如要区分需要使用一下两个函数
int ferror(FILE* stream)
功能:判断读文件时是否出错
返回值:非0表示错
int feof(FILE* stream)
功能:读文件时是否读到文件末尾
返回值:非0表示读到文件末尾
读写一个字符串
读字符串
char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取一串字符
参数:
s:存放读取的字符串的首地址(可以是一个char类型的字符串数组)
size:读取的大小,实际读取大小为size-1,因为字符串末尾自动补'\0'
stream:文件流
返回值:
成功:读取的字符串的首地址
失败或读到文件末尾:NULL
特性:
1.一次调用最多读取一行数据
2.实际读到个数为size-1个,末尾自动添加'\0'
写字符串
int fputs(const char *s, FILE *stream);
功能:向文件中写字符串
参数:
s:要写的内容 (可以是char类型的字符串数组)
stream:文件流
返回值:
成功:非负整数
失败:EOF
例子:
//读写一个字符串
//当前目录下有一个test1文件,里面有字符AB
#include <stdio.h>
int main()
{
// 打开当前路径下的test文件,可读写方式
// 由FILE*类型的变量fp接收返回文件流
FILE *fp = fopen("./test1", "r+");
// 判断是否打开成功
if (NULL == fp)
{
perror("read err\n");
return -1;
}
// 读取一字符串
char s[3] = "";
// 要读取AB两个字符,则要设置大小为3,因为字符串末尾自动补'\0'
fgets(s,3,fp);
// 打印读取字符
printf("读取到一个:%s\n",s);
// 写一个字符串,并判断是否成功
if(fputs(s,fp) == EOF)
{
perror("fputc error");
return -1;
}else
{
printf("写入一个%s\n",s);
}
//关闭文件
fclose(fp);
return 0;
}
二进制读写
以二进制的方式进行读写,可以读写任意类型的数据。
//读
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:
从文件流读取多个元素
参数:
ptr :用来存放读取元素 (可以用来读取任意类型的数据)
size :元素大小 sizeof(数据类型)
nmemb :读取对象的个数
stream :要读取的文件
返回值:
成功:读取对象的个数
读到文件尾或失败:0
//写
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:
按对象写
参数:
ptr :写的取元素 (可以用来读取任意类型的数据)
size :元素大小 sizeof(数据类型)
nmemb :读取对象的个数
stream :要读取的文件
返回值:
成功:写的元素个数
失败 :0
fprintf
fprintf 函数是C语言中的一个标准库函数,用于格式化输出到流中。它通常用于将格式化的字符串写入到文件或标准输出(如屏幕)。
fprintf 的功能类似于 printf 函数,但是 printf 是输出到标准输出流(通常是屏幕),而 fprintf 可以指定输出到任何流。
int fprintf(FILE *stream, const char *format, ...);
参数:
stream: 指向一个已经打开的文件流的指针。
format: 一个格式控制字符串,与 printf 中的格式字符串相同,用来控制输出格式。
...: 变长参数列表,提供要输出的数据。
比printf多了一个文件流,用来设定输出位置,其余功能与printf相同
fprintf(fp, "Name: %s, Age: %d\n", name, age); // 输出到文件
例子
//创建一个空文件test1
#include <stdio.h>
int main()
{
// 打开当前路径下的test文件,可读写方式
// 由FILE*类型的变量fp接收返回文件流
FILE *fp = fopen("./test1", "r+");
// 判断是否打开成功
if (NULL == fp)
{
perror("read err\n");
return -1;
}
char name[9] = "xiaoming";
int age = 100;
// 写一数据
fprintf(fp, "Name: %s, Age: %d\n", name, age); // 输出到文件
//关闭文件
fclose(fp);
return 0;
}
执行结果:
test1文件中多了fprintf打印的信息
文件定位(光标)操作
读写字符都是以光标位置作为起始位置,每次读写后,光标位置都会向后移动,读/写一个字符就往后移动一个单位
可以通过ftell
获取指定文件当前光标的位置
long ftell(FILE *stream);
功能:
获取位置指针当前的文件位置
参数:
要检测的文件流
返回值:
成功:当前的文件光标位置
失败:-1
将光标移动到文件开头
void rewind(FILE *stream);
功能:将文件位置指针(光标)定位开头
如想将光标移动到指定的位置,使用fseek()函数
int fseek(FILE *stream, long offset, int whence);
功能:文件指针的定位操作
参数:
stream:文件流
offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
whence:相对位置:
SEEK_SET:相对于文件开头
SEEK_CUR:相对于文件当前位置
SEEK_END:相对于文件末尾
返回值:成功:0
失败:-1
以上叫法文件位置
流位置
都是指文件位置指针
通俗的讲就是文件中鼠标的光标位置
有人觉得叫光标太俗了所以取了很多奇怪的名字,都不如直接叫光标好理解。
我们还可以通过光标位置在计算文档中有多少字符
//相对文件末尾偏移0
fseek(fp,0, SEEK_END);
//再获取光标位置
long len = ftel(fp);
总结
标准输入、输出和错误是C语言中处理数据输入和输出的基础。理解它们的工作原理对于编写健壮的程序至关重要,尤其是在处理用户交互和文件操作时。