一 ,先来了解一下什么叫流??
计算机中大量设备都与I/O有关。CD驱动器,软盘硬盘驱动器,网络 连接,通信接口,视频适配器就是很常见的外设。每一种外设都有不同的特性和操作协议。OS负责这些不同设备的通信细节,并向用户提供一个更为简单的统一的I/O接口。而ANSI C进一步对I/O的概念进行了抽象。就C程序而言,所谓的I/O操作就是简单从程序移进,移出字节的事情,这种字节流就叫做 流。文件和设备的操作都是基于流的操作。
流分为两种:文本流 和 二进制流。
文本流在不同的系统中实现不一样
二进制流中的字节是安装程序编写的形式写入到文件和设备中,而且完全根据从文件或设备中读取的形式读入到程序。
二,什么是文件呢?
文件是一段数据的集合,这些数据可以是有规则的,也可以是无序的集合。在stdio.h有一个非常重要的东西,文件指针,每个文件都会在内存中开辟一块空间,用于存放文件的相关信息,这些信息保存在一个结构体变量中,FILE *pIn,点击FILE转到定义看一下,这个结构体在VS编译器中的定义如下:
struct _iobuf {
char *_ptr; //指向buffer中第一个未读的字节
int _cnt; //记录剩余的未读字节的个数
char *_base;//文件的缓冲
int _flag;//打开文件的属性
int _file;//获取文件描述
int _charbuf;//单字节的缓冲,即缓冲大小仅为1个字节,如果为单字节缓冲,_base将无效
int _bufsiz;//记录这个缓冲大小
char *_tmpfname;//
};
typedef struct _iobuf FILE;
FILE是一个数据结构,用于访问一个流。,如果你激活了几个流,每个流都会对应一个FILE结构
我们来看一下如何打开一个文件:
FILE *fopen( const char *filename, const char *mode );
- filename是文件名,这里写的是文件的路径。
- mode是打开模式
下图是文件的使用方式:
例如:
int main()
{
FILE* pf;
pf = fopen("myfile.txt", "w");
if (pf != NULL)
{
fputs("fopen example", pf);
fclose(pf);
pf=NULL;
}
system("pause");
return 0;
}
注意的一些点:
文件判断是否打开成功;
关闭文件;
文件指针置空
关闭文件:
函数原型:int fclose( FILE *stream );
用于关闭流。如:fclose(pf);
对于每一个C程序而言,至少打开三个流:
标准输入(stdin),标准输出(stdout),标准错误(stderr),他们都是一个指向FILE结构的指针。
通常:标准输入为:键盘设备;标准输出:终端或屏幕。
stderr函数用法:
有没有换行它都会输出结果,而stdout 标准输出在默认情况下,stdout是行缓冲的,它的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。
int main()
{
FILE*pf = fopen("unexistfile.dat", "r");
if (pf == NULL)
{
fprintf(stderr, "open error");
}
else
{
fclose(pf);
}
system("pause");
return 0;
}
三,
I/O函数:
I/O函数有三种方式:
单个字符,文本行,二进制数据。以下是几种输入输出函数:
功能 | 函数名 | 适用于 |
字符输入函数 | getchar | 标准输入流 |
字符输出函数 | putchar | 标准输出流 |
字符输入函数 | fgetc, getc | 所有输入流 |
字符输出函数 | fputc, putc | 所有输出流 |
文本行输入函数 | fgets,gets | 所有输入流 |
文本行输出函数 | fputs,puts | 所有输出流 |
格式化输入函数 | scanf | 标准输入流 |
格式化输出函数 | printf | 标准输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
- fgetc(getc)和fputc(putc)
fgetc(getc)用于输入一个字符。从指定的文件中获取字符,并作为返回值返回。
函数原型:int fgetc( FILE *stream );
ch = fgetc(pf);
下例是把myfile.txt文件中的内容输出到标准输出流上。
char ch = 0;
FILE* pf = fopen("myfile.txt", "r");
if (pf == NULL)
{
perror("error opening file");
exit(0);
}
while (ch != EOF)
{
ch = fgetc(pf);
putc(ch, stdout);
}
fclose(pf);
fputc(putc)
用于输出一个字符。下例是把从键盘输入的信息输出到myfile.txt文件中,以’$’作为结束符。
函数原型:int fputc( int c, FILE *stream );
char ch;
FILE* pf = fopen("myfile.txt", "w");
if (pf == NULL)
{
perror("error opening file");
exit(0);
}
ch = getchar();
while (ch != '$')
{
fputc(ch, pf);
ch = getchar();
}
fclose(pf);
2.fgets和fputs函数
fgets函数
用于文本行的输入
函数原型:char *fgets( char *string, int n, FILE *stream );
fgets(str,n,pf);从pf所指文件中读入n-1个字符放入以str为起始地址的空间中;遇到换行或文件结束符则结束,返回值是str。
char buf[10] = { 0 };
FILE *pf = fopen("myfile.txt", "r");
if (pf == NULL)
{
perror("open file for reading");
exit(EXIT_FAILURE);
}
fgets(buf, 9, stdin);
fputs(buf, stdout);
fclose(pf);
fputs函数
是把文本行输出到文件中
函数原型:int fputs( const char *string, FILE *stream );
fputs(str,pf);把str所指向的地址中的内容输出到pf所指向的文件中。
3.fscanf和fprintf函数
fscanf函数
是格式化输入函数,和printf相似,区别在于这个函数既使用文件流,又适用于标准输入流。
函数原型:int fscanf( FILE *stream, const char *format [, argument ]… );
fscanf(pf,"%d,%d",&a,&b);
fscanf(stdin,"%d,%d",&a,&b);
char ch = 0;
int num = 0;
char arr[10] = { 0 };
fscanf(stdin, "%s %d %c", arr, &num, &ch);
fprintf(stdout,"%s %d %c\n", arr, num, ch);
fprintf函数
格式化输出函数。
函数原型:int fprintf( FILE *stream, const char *format [, argument ]…);
fprintf(pf,"%d,%d",a,b);
fprintf(stdout,"%d,%d",a,b);
4.sprintf()和sscanf()函数
int sprintf( char *buffer, const char *format [, argument] … );
int sscanf( const char *buffer, const char *format [, argument ] … );
返回值字符串的长度。
可以把格式化数据转换成字符串,用sprintf()函数
也可以把字符串转换成格式化数据,用sscanf()函数
如下例
struct S
{
char name[10];
int age;
float f;
};
int main()
{
//struct S stu = { "zhangsan", 20, 1.5f };
//char buf[100] = { 0 };
//sprintf(buf, "%s %d %f", stu.name, stu.age, stu.f);//字符串形式放入buf中
//printf("%s\n", buf);
struct S tmp = { 0 };
struct S stu = { "zhangsan", 20, 1.5f };
char buf[100] = { 0 };
sprintf(buf, "%s %d %f", stu.name, stu.age, stu.f);//字符串形式放入buf中
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age),&(tmp.f));//从buf中拿出字符串转换成格式化数据
printf("%s %d %f\n", tmp.name, tmp.age, tmp.f);//打印结构体tmp的成员变量的值
system("pause");
return 0;
}
5.fread和fwrite函数
fwrite函数
以二进制方式输出
函数原型:size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
fwrite(buf, sizeof(char), sizeof(buf), pf);
- 从buf是一个指针,是要获取数据的地址。
- size个 单位:字节
- count最多写多少个
pf写入到目标文件
例如这个题是把结构体信息写入到myfile.txt这个文件中。
struct S
{
char name[10];
int age;
float f;
};
int main()
{
struct S stu = { "zhangsan", 20, 1.2f };
FILE* pf = fopen("myfile.txt", "w");
if (pf == NULL)
{
perror("error opening file");
exit(EXIT_FAILURE);
}
fwrite(&stu, sizeof(struct S), 1, pf);//二进制的形式写进去
fclose(pf);
pf = NULL;
system("pause");
return 0;
}
fread函数:
函数原型:size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
- fread(buf,size,count,pf);
这个题是把pf 所指向的文件中的信息读入到结构体中。
struct S
{
char name[10];
int age;
float f;
};
int main()
{
struct S stu = { 0};
FILE* pf = fopen("myfile.dat", "r");
if (pf == NULL)
{
perror("error opening file");
exit(EXIT_FAILURE);
}
fread(&stu, sizeof(struct S), 1, pf);
printf("%s %d %f\n", stu.name, stu.age, stu.f);
fclose(pf);
pf = NULL;
system("pause");
return 0;
}
6.fseek函数
fseek是文件定位函数,用来移到文件指针到指定的位置上,从此位置开始进行读写。
函数原型: int fseek( FILE *pf, long offset, int origin );
- pf文件指针
- offset偏移量,单位字节
origin是起始位置,指定位移量是从哪个位置开始偏移
origin这个参数有三种选项:
SEEK_CUR—文件指针的当前位置
- SEEK_END—文件结尾
- SEEK_SET—文件开始
fseek(pf,-1,SEEK_CUR); 从当前位置向前偏移一个字节
fseek(pf,0,SEEK_SET); 从起始位置开始
fseek(pf,4,SEEK_SET); 从起始位置向后偏移4个字节
7. ftell函数
用来获取文件指针的当前位置,成功返回当前位置指针相对文件开始位置的字节数失败返回-1L。
函数原型:long ftell( FILE *stream );
可以用来测试一个文件的长度:
long n;
fseek(pf,0,SEEK_END);
n=ftell(pf);
8.rewind函数
功能是 使文件指针回到文件开头。
函数原型:void rewind( FILE *stream );
rewind(pf);
9.clearerr函数
功能是 重置文件流的错误指示。
函数原型:void clearerr( FILE *stream );
10.feof函数
测试文件流是否读到了文件尾
函数原型:int feof( FILE *stream );
- 调用成功返回一个非零值,失败返回0。
- 返回非零值说明已经到达文件尾。