标准I/O
标准I/O由ANSI C标准定义
标准IO通过缓冲机制减少系统调用,实现更高效率
围绕流进行操作,默认打开三个流
在用户层,可移植性好
流
FILE 流 stream
标准IO用一个结构体类型来存放大考的文件的相关信息
标准IO的所有操作都是围绕FILE来进行
一个FILE结构体就代表一个打开的文件
流的缓冲类型
全缓冲(文件相关)
当流的的缓冲区无数据或无空间时才执行IO操作
行缓冲(终端相关)
刷新标准输出缓存区的条件:
- \n
- 程序正常退出
- 缓存区满
- 强制刷新:fflush(NULL)
无缓冲(标准错误)
数据直接写入文件,流不进行缓冲
标准错误
打开流 打开文件 fopen
FILE *fopen(const char *path, const char *mode);
成功时返回流指针,出错时返回NULL
r | 只读,当文件不存在时报错,文件流定位到文件开头,文件必须存在 |
r+ |
可读可写,当文件不存在时报错,文件流定位到文件开头,文件必须存在 |
w |
只写,文件不存在创建,存在则清空 |
w+ |
可读可写,文件不存在创建,存在则清空 |
a |
追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾 |
a+ |
读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾 |
#include<stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
if((fp=fopen("test.txt","w+"))==NULL);//关系运算符的优先级比赋值要高,需要加括号
{
perror("fopen err"); //如果以r方式打开,没有这个文件会报错,打印错误信息。
return -1;
}
printf("fopen success\n");
//2. 关闭文件
fclose(fp);
return 0;
}
读写流
每次读写一个字符:fgetc()、fputc()
每次读一个字符fgetc()
int fgetc(FILE * stream);
功能:从文件中读取一个字符,并将当前文件指针位置向后移动一个字符。
参数:stream:文件流
返回值:成功:读到的字符
失败或读到文件末尾:EOF(-1)
每次写一个字符fputc()
int fputc(int c, FILE * stream);
功能:向文件中写入一个字符, 成功写入后文件指针会自动向后移动一个字节位置。
参数:c:要写的字符
stream:文件流
返回值:成功:写的字符的ASCII
失败:EOF(-1)
getchar()等同于fgetc(stdin)
putchar()等同于fputc(c,stdout);
feof
int feof(FILE * stream);
功能:判断文件有没有到结尾,也就是当前所在位置后面还有没有字符。
返回:如果到达文件末尾,返回非零值。如果后面还有字符则返回0。
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
fp = fopen("test.txt", "r+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
char ch = fgetc(fp);
printf("%c %d\n", ch, ch);
ch = fgetc(fp);
printf("%c %d\n", ch, ch);
if (feof(fp))
{
printf("end1\n");
//不会打印出来,因为此时末尾还有一个EOF。
}
ch = fgetc(fp);
printf("%c %d\n", ch, ch);
if (feof(fp))
{
printf("end2\n");
//会打印出来,因为此时已经到末尾了
}
//2. 关闭文件
fclose(fp);
return 0;
}
使用上述函数实现cat功能
#include <stdio.h>
int main(int argc, char const *argv[])
{
char ch;
if (argc != 2)
{
printf("format: %s <filename>\n", argv[0]);
return -1;
}
//1.打开文件
FILE *fp = fopen(argv[1], "r");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("open success\n");
//2.循环用fgetc获取文件内容
//3.当读到文件末尾EOF就结束
// while ((ch = fgetc(fp)) != EOF)
// {
// //4.将读到内容打印到终端
// fputc(ch, stdout);
// }
//或者
while (1)
{
ch = fgetc(fp);
if (feof(fp))
break;
fputc(ch, stdout);
}
//5.关闭文件
return 0;
}
每次一串字符的读写 fgets() 和 fputs()
char * fgets(char *s, int size, FILE * stream);
功能:从文件中每次读取一行字符串
参数: s:存放字符串的地址
size:期望一次读取的字符个数
stream:文件流
返回值:成功:s的地址
失败或读到文件末尾:NULL
特性: 每次实际读取的字符个数为size-1个,会在末尾自动添加\0
每次读一行,遇到\n或者到达文件末尾后不再继续读下一行
并把它存储在s所指向的字符串内。
int fputs(const char *s, FILE * stream);
功能:向文件中写字符串
参数:s:要写的内容
stream:文件流
返回值:成功:非负整数
失败:EOF
fgets() 和 fputs()对文件
#include<stdio.h>
int main(int argc, char const *argv[])
{
char buf[100];
FILE *fp;
//打开文件
if((fp=fopen("test.txt","r+"))==NULL)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
//读写
fgets(buf,100,fp);
fputs(buf,stdout);
fgets(buf,100,fp);
fputs(buf,stdout);
fgets(buf,100,fp);
fputs(buf,stdout);
fclose(fp);
return 0;
}
#include<stdio.h>
int main(int argc, char const *argv[])
{
char buf[32]="55555";
fgets(buf,32,stdin);
printf("%s\n",buf);//
fputs("man",stdout);
printf("%s\n",buf);//
return 0;
}
练习:通过fgets实现"wc -l 文件名"命令功能(计算文件行数)
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int len = 0;
char buf[32] = "";
if (argc != 2)
{
printf("format: %s <filename>\n", argv[0]);
return -1;
}
FILE *fp = fopen(argv[1], "r");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
//循环读,读到末尾结束
while (fgets(buf, 32, fp) != NULL)
{
//是否有换行,如果有就累加行数
if (buf[strlen(buf) - 1] == '\n')
len++;
}
printf("%d %s\n", len, argv[1]);
//因为最后一行没有换行所以少一行,对标wc -l
return 0;
}
总结
返回值 | 举例 | |||
fopen |
FILE
*
fopen
(
const
char
*
path
,
const
char
*
mode
);
| 成功返回文件流 失败返回NULL |
fp = fopen("test.txt", "r"); if (NULL == fp) { perror("fopen err"); //如果以r方式打开,没有这个文件会报错,打印错误信息。 return -1; } printf("fopen success\n"); | |
fclose |
int
fclose
(
FILE
*
stream
);
|
fclose
(
fp
);
| ||
fgetc |
int
fgetc
(
FILE
*
stream
);
| 成功返回读到的字符的ASCII 失败或者读到文件末尾返回EOF(-1) |
//对文件读操作 char ch = fgetc(fp);
//对终端读操作
char ch = fgetc(stdin); |
从文件中读取一个字符,并将当前文件指针位置向后移动一个字符 |
fputc |
int fputc(int c, FILE * stream); |
成功:写的字符的ASCII 失败:EOF(-1) |
//向文件中写
fputc('a', fp); fputc(98, fp); //向终端中写 ch = fgetc(stdin)
fputc
(
ch
,
stdout
);
| |
fgets | ||||
fgets | ||||
feof | ||||
ferror | ||||
fread |
size_t
fread
(
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
FILE
*
stream
);
| |||
fwrite |
size_t
fwrite
(
const
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
FILE *stream); | |||
freopen |
FILE
*
freopen
(
const
char
*
pathname
,
const
char
*
mode
,
FILE
*
fp
);
|
文件IO
标准IO与文件IO比较
标准IO | 文件IO |
ANSIC | POSTX |
带缓冲 | 无缓冲 |
流FILE | 文件描述符fd |
什么是文件IO
1posix(可移植操作系统接口)定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符
访问各种类型文件
LINUX下,标准IO基于文件IO实现
文件描述符
每一个打开的文件都对应一个文件描述符
非负整数,每次分配最小的可用的,从0开始依次递增
文件IO操作通过文件描述符完成
012
open
int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
flags:打开文件的方式
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:不存在创建
O_TRUNC:存在清空
O_APPEND:追加
返回值:成功:文件描述符
失败:-1
例如:O_WRONLY|O_CREAT|O_TRUNC ==> "w" 可写,不能存在时创建,存在则清空
当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限
int open(const char *pathname, int flags, mode_t mode);
最后权限=创建出来的文件指定权限值&(~umask)
例如:指定权限为0666(8进制)
最终权限为0666&(~umask) = 0666&0775 = 0664