- 流支持不同的读写方式
- 读写一个字符:fgetc()/fputc()
- 读写一行:fgets()和fputs()
- 读写若干个对象:fread()/fwrite(), 每次读/写若干个对象,而每个对象具有相同的长度
字符读写
字符输入
- 用来输入一个字符
- 头文件:
#include <stdio.h>
函数原型
int fgetc(FILE *stream);- 从流中读取下一个字符,并将其作为unsigned char强制转换为int类型返回,若到文件末尾或出错时返回EOF(-1)
int getc(FILE *stream);//宏- 等价于fgetc(),不同之处是它可以作为一个宏来实现,对stream进行多次求值
- getc和fgetc区别是一个是宏一个是函数
int getchar(void);- 成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1)
- getchar 等同于fgetc(stdin)
- stdin 也是FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)
- 调用getchar会阻塞,等待你的键盘输入
函数返回值是int类型不是char类型,主要是为了扩展返回值的范围
打开文件后读取,是从文件开头开始读,a+a+读完一个后读写指针会后移
c
#include <stdio.h>
int main(int argc, const char *argv[])
{
int c;
FILE * fp = NULL;
if((fp = fopen("test.txt","r+")) == NULL ){
perror("fopen\n");
return -1;
}
printf("fopen success\n");
while(1){
#if 0
if((c = fgetc(fp)) == EOF ){
perror("fgetc");
break;
}
#else
if((c = getc(fp)) == EOF ){
perror("getc");
break;
}
#endif
printf("%c", (char)c);
}
fclose(fp);
return 0;
}
字符输出
- 头文件:
- #include <stdio.h>
函数原型
int fputc(int c, FILE *stream);- 将字符c(强制转换为无符号字符)写入流
int putc(int c, FILE *stream);//宏- 等价于fputc(),不同之处是它可以作为宏来实现,对stream进行多次求值
int putchar(int c);- 成功时返回写入的字符,出错时返回EOF
- putchar©等同于fputc(c, stdout)
- fputc()、putc()和putchar()返回作为无符号字符写入的字符,在错误时返回EOF
以上函数返回和输入参数都是int类型
遇到这种错误:Bad file descriptor, 很可能是文件打开的模式错误(只读模式去写,只写模式去读),或文件的读写权限不能满足文件打开设置的需求
c
#include <stdio.h>
int main(int argc, const char *argv[])
{
int c;
FILE * fp = NULL;
if ((fp = fopen("test.txt","a+")) == NULL ){
perror("fopen\n");
return -1;
}
printf("fopen success\n");
#if 1
if((c = fputc('a', fp)) == EOF ){
perror("fputc");
return -1;
}
#elif 0
if((c = putc('a', fp)) == EOF ){
perror("putc");
return -1;
}
#else
putchar('b');
#endif
printf("c = %c\n", c);
fclose(fp);
return 0;
}
例程:字符读写复制文件
c
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp_a = NULL, *fp_b = NULL;
int c = 0;
if(argc < 3){
printf("Usage: %s <be copied file> <copied file>\n", argv[0]);
return -1;
}
if( (fp_a = fopen(argv[1], "r+")) == NULL ){
perror("be copied file fopen");
return -1;
}
if( (fp_b = fopen(argv[2] , "w+")) == NULL ){
perror("copied file fopen");
fclose(fp_a);
return -1;
}
printf("fopen files ok\n");
do{
if((c = fgetc(fp_a)) == EOF){
perror("fgetc");
break;
}
if( fputc(c, fp_b) == EOF ){
perror("fputc");
break;
}
} while(1);
fclose(fp_a);
fclose(fp_b);
return 0;
}
行输入输出
行输入
- 头文件:
#inculde <stdio.h>
函数原型
char *gets(char *s);- 读取标准输入到缓冲区s
注意这个函数是危险的,不检查缓冲区溢出,永远不要使用gets()
gets()从stdin读取一行到s所指向的缓冲区,直到一个结束换行符或EOF,它用空字节('\0')替换。因为在事先不知道数据的情况下不可能知道gets()将读取多少字符,并且因为gets()将继续存储超过缓冲区末尾的字符,所以使用它是非常危险的。它可能被用来破坏计算机安全,使用fgets()代替。
char *fgets(char *s, int size, FILE *stream);- 输入的数据超出size,size-1个字符会保存到缓冲区,最后添加'\0',
- 输入数据少于size-1,后面会添加'\n'和'\0'
- 返回值:
- 成功时返回s,到文件末尾或出错时返回NULL
- 读取文件内容
c
#include <stdio.h>
#define N 32
#define M 6
int main(int argc, const char *argv[])
{
FILE * fp = NULL;
char buf[M] = {0};
if( (fp = fopen("test.txt", "r+")) == NULL ){
perror("fopen");
return -1;
}
while(1){
if ( fgets(buf, M, fp) == NULL ){
perror("fgets");
break;
}
printf("test look:%s", buf);
}
printf("fgets from file ok\n");
fclose(fp);
return 0;
}
行输出
- 头文件:
- #include <stdio.h>
- 函数原型:
int puts(const char *s);int fputs(const char *s, FILE *stream);
- 返回值:
- 成功时返回非负整数(输出字符个数)
- 出错时返回EOF(-1)
- 参数:
- puts将缓冲区s中的字符串输出到stdout,并追加'\n'
- fputs将缓冲区s中的字符串输出到stream,不追加'\n'
c
#include <stdio.h>
#define N 32
int main(int argc, const char *argv[])
{
FILE * fp = NULL;
char buf[N] = {0};
if( (fp = fopen("test.txt", "a+")) == NULL ){
perror("fopen");
return -1;
}
if(fgets(buf, N, stdin) == NULL){
perror("fgets");
fclose(fp);
return -1;
}
if(fputs(buf, fp) == EOF){
perror("fputs");
fclose(fp);
return -1;
}
printf("fputs ok\n");
fclose(fp);
return 0;
}
例程:统计文件行数
- 如何统计一个文本文件包含多少行?
c
#include <stdio.h>
#include <string.h>
#define N 32
int main(int argc, const char *argv[])
{
FILE * fp = NULL;
int cout = 0;
char buf[N] = {0};
if( (fp = fopen("test.txt", "a+")) == NULL ){
perror("fopen");
return -1;
}
while(1){
if(fgets(buf, N, fp) == NULL){
perror("fgets");
break;
}
if(buf[strlen(buf)-1] == '\n'){
cout++;
}
}
printf("cout=%d\n", cout);
fclose(fp);
return 0;
}
二进制、对象读写
- 文本文件:每一个字符都是一个字节,外存存储对应ASCII码值。对于数值,以字符形式在外存上存储对应ASCII值。文本流中的数据可以显示和打印出来,都是用户可以读懂的信息
- 二进制文件:流中的数据是按照二进制编码的方式(值的方式)来存放文件。二进制数据也可在屏幕上显示, 但其内容无法读懂
- fread、fwrite既可以读写文本文件,也可以读写数据文件;
fread 读
- 头文件:
#include <stdio.h>
- 函数原型:
size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
- 参数:
void *ptr读内容要放的位置的指针size_t size读取的"块"大小size_t n每次函数运行读取的"块"个数FILE *fp要读取文件的文件指针
- 返回值:
- 成功返回读写的对象个数
- 出错时返回EOF
c
#include <stdio.h>
#define N 10
int main(int argc, const char *argv[])
{
FILE * fp = NULL;
char buf[N] = {0};
size_t ret = -1;
if ((fp = fopen(argv[1], "r+")) < 0 ){
perror("fread");
return -1;
}
ret = fread(buf, sizeof(buf), 1, fp);
if(0 > ret){
perror("fread");
fclose(fp);
return -1;
}
printf("test buf=%s", buf);
fclose(fp);
return 0;
}
fwrite 写
- 头文件:
#include <stdio.h>
- 函数原型:
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
- 参数:
- void *ptr 要写的内容的位置指针
- size_t size 写的一个"块"大小
- size_t n 每次函数运行写的"块"个数
- FILE *fp 要写入文件的文件指针
- 返回值:
- 成功返回读写的对象个数
- 出错时返回EOF
c
#include <stdio.h>
#include <unistd.h>
typedef struct note{
int id;
char name[8];
float score;
} STU;
int main(int argc, const char *argv[])
{
FILE * fp = NULL;
if((fp=fopen("test.txt", "a+")) < 0){
perror("perror");
return -1;
}
STU s[2] = {{1, "wang", 98},{2, "li", 95}};
int len = sizeof(STU);
if(fwrite(&s[0], len, 1, fp) < 0){
perror("fwrite");
fclose(fp);
return -1;
}
fflush(fp);
sleep(5);
if(fwrite(&s[1], len, 1, fp) < 0){
perror("fwrite");
fclose(fp);
return -1;
}
sleep(5);
fclose(fp);
if((fp=fopen("test.txt", "r+")) < 0){
perror("perror");
return -1;
}
STU ret;
fread(&ret, len, 1, fp);
printf("%d,%s,%f\n", ret.id, ret.name, ret.score);
fclose(fp);
return 0;
}
文件写完后,文件指针指向文件末尾,如果这时候去读,读不出来内容的
例程:读写复制文件
- 如何利用 fread / fwrite 实现文件的复制?
c
#include <stdio.h>
#include <string.h>
#define N 512
int main(int argc, const char *argv[])
{
FILE *fp_r = NULL, *fp_w = NULL;
if(argc<3){
printf("Usage: %s <source file> <destination file>\n", argv[0]);
return -1;
}
if((fp_r = fopen(argv[1], "r")) == NULL){
printf("fopen source file failed\n");
return -1;
}
if((fp_w = fopen(argv[2], "a+")) == NULL){
printf("fopen destination file failed\n");
return -1;
}
char buf[N] = {0};
size_t bytes_read, bytes_write;
while(1){
// 判断文件流指针是不是已经移动到文件末尾
if(feof(fp_r)){
printf("read file in last line\n");
break;
}
if((bytes_read = fread(buf, 1, sizeof(buf), fp_r)) < 0){
printf("fread failed\n");
goto END;
}
if((bytes_write = fwrite(buf, 1, bytes_read, fp_w)) < 0){
printf("fwrite failed\n");
goto END;
}
// 如果写入的数据量不等于读取的数据量,则表示写入失败
if(bytes_read != bytes_write){
printf("failed to write to destination file\n");
goto END;
}
bzero(buf, sizeof(buf));
}
printf("file copied successfully form '%s' to '%s'\n", argv[1], argv[2]);
END:
fclose(fp_r);
fclose(fp_w);
return 0;
}
- 字符数组使用后,空间不会自己清空,不影响下次使用需要主动清。bzero函数可以给指定的空间上都填充ASCII值0,表示清空
c
#include <strings.h>
void bzero(void *s, size_t n);
- feof函数可以判断文件是否读到末尾(文件读写指针),检查stream 指向的流的文件结束指示符,如果已达到,则返回非零值
c
#include <stdio.h>
int feof(FILE *stream);
格式化输入输出
格式化输入
int fscanf(FILE *stream, const char *format, ...);- 从文件流读取格式化输入
int sscanf(const char *str, const char *format, ...);- 从字符串读取格式化输入
int scanf(const char *format, ...);- 标准输入版本(实际是fscanf的特殊形式)
- 等价于 fscanf(stdin, format, ...)
- 成功时返回输出的字符个数,出错时返回EOF
格式化输出
int fprintf(FILE *stream, const char *fmt, ...);- 格式化输出到文件流
int sprintf(char *s, const char *fmt, ...);- 格式化输出到字符串
int printf(const char *format, ...);- 标准输出版本
- 等价于 fprintf(stdout, format, ...)
- 成功时返回输出的字符个数,出错时返回EOF
c
#include <stdio.h>
int main(int argc, const char *argv[])
{
char buf[32] = {0};
#if 0
FILE *fp = NULL;
if((fp = fopen(argv[1], "a+")) == NULL){
perror("fopen");
return -1;
}
fprintf(fp, "%s", "hello");
fscanf(stdin, "%s", buf);
fprintf(fp, "%s", buf);
fclose(fp);
#endif
int a, b, c;
sscanf("1 2", "%d %d %d", &a, &b, &c);
sprintf(buf, "a=%d,b=%d,c=%d\n", a, b, c);
printf("%s\n", buf);
return 0;
}