C — 文件输入/输出

一、概述

1.1 概念

文件: 通常是在磁盘或固态硬盘上的一段已命名的存储区。

文件模式: C提供了两种文件模式,文本模式二进制模式

文本模式: 如果文件使用二进制编码的字符(Unicode)表示文本,该文件就是文本文件。
二进制模式: 如果文件中的二进制代表机器语言或图片或音视频编码,该文件就是二进制文件。

1.2 指向标准文件的指针

标准文件文件指针通常使用的设备
标准输入stdin键盘
标准输出stdout显示器
标准错误stderr显示器

二、标准 I/O 中常见的函数

标准库 <stdio.h>
链接:https://www.runoob.com/cprogramming/c-standard-library-stdio-h.html

2.1 fopen() 与 fclose() 与 fflush() 函数

函数声明作用
FILE *fopen(const char *filename, const char *mode)使用给定的 mode (文件访问模式) 打开 filename 所指向的文件;
int fclose(FILE *stream)关闭流 stream,刷新所有的缓冲区。如果流成功关闭,则该方法返回0。如果失败,则返回 EOF。
int fflush(FILE *stream)刷新缓冲区,将所有输出缓冲区的数据发送到输出的文件中。
成功返回0。失败返回 EOF,且设置错误标识符(即 feof)。

打开文件的 mode (模式) 如下:

模式描述
“r”以读模式打开文件;
该文件必须存在。
“w”以写模式打开文件;
文件如果存在,则清空原有的内容;
文件不存在则创建文件;
“a”以写模式打开文件;
写操作向文件末尾追加数据;
文件不存在则创建文件;
“r+”读写模式打开文件;
内容写入文件末尾;
该文件必须存在;
“w+”读写模式打开文件;
文件如果存在,则清空原有的内容;
文件不存在则创建文件;
“a+”读写模式打开文件;
内容写入文件末尾;
文件不存在则创建文件;

示例

#include <stdio.h>
#include <stdlib.h>

int main() {
   FILE *fp = NULL;  // 将指针声明为 NULL 是一个好习惯,NULL 是定义在 stdlib.h 的宏
   fp = fopen("elson.txt", "w");  // 以一写模式打开一个文件
   
   if (fp == NULL) {  // 文件打开失败
       exit(EXIT_FAILURE); //EXIT_FAILURE 是定义在 stdlib.h 的宏
   }
   
   fprintf(fp, "%s", "我是 Elson."); // 将文本写入指定的文件
   fclose(fp); 	// 关闭文件流
   return(0);
}

2.2 getchar() 与 putchar() 、getc() 与 putc() 、fgetc() 与 fputc() 函数

函数声明作用
int getchar(void)从标准输入 stdin 中获取一个(无符号)字符。
int putchar(int char)把参数 char 指定的(无符号)字符写入到标准输出 stdout 中。
int getc(FILE *stream)从指定的 stream 中获取下一个(无符号)字符,并把位置标识符往前移动。
int putc(int char, FILE *stream)把参数 char 指定的(无符号)字符写入到指定的流 stream 中,并把位置标识符往前移动。
int fgetc(FILE *stream)从指定的流 stream 获取下一个(无符号)字符,并把位置标识符往前移动。
int fputc(int char, FILE *stream)把参数 char 指定的(无符号)字符写入到指定的流 stream 中,并把位置标识符往前移动。

小结:

  1. getchar() 函数从 stdin获取数据;putchar() 函数将数据输出到 stdout 中;
  2. getc() 函数从指定文件中获取数据;putc() 函数将数据写入到指定的文件中;
  3. getchar() 与 getc() 的区别在于获取的数据来源不一致,我们可以使用 getc(stdin) 来实现 getchar() 同样的效果;通过查看 stdio.h 文件,我们可以知道 stdin 是一个指向 __stdinp 的文件指针;
  4. getc() 与 fgetc() 函数,他们的实现效果是一样的,不同点在于 getc() 是宏定义,而 fgetc() 是函数;

2.3 gets() 与 puts() 、fgets() 与 fputs() 函数

函数声明作用
char *gets(char *str)1. 从 stdin 读取一行并存入 str 所指向的字符串中。
2. 当读取到换行符时,或者到达文件末尾时,该方法会返回。
3. 当读取到数据时,返回str;当读取到EOF时(即读取到文件末尾),返回NULL。
4. 函数读取到换行符时,会丢弃换行符,返回其余的字符。
int puts(const char *str)1. 将 str 所指向的字符串输出到 stdout,直到遇到空字符(空字符不输出)。
2. 在输出字符串时,会自动在其末尾添加一个换行符。
char *fgets(char *str, int n, FILE *stream)1. 从指定的流 stream 读取一行,并存入 str 所指向的字符串内。
2. 当读取 (n-1) 个字符时(留一个字符用于添加空字符),或者读取到换行符时,或者到达文件末尾时,该方法会返回。
3. 当读取到数据时,返回str;当读取到EOF时(即读取到文件末尾),返回NULL。
4. 函数读取到换行符时,不会丢弃换行符。
int fputs(const char *str, FILE *stream)1. 把字符串写入到指定的流 stream 中,但不包括空字符。
2. 不会在输出末尾添加换行符。

小结:

  1. gets() 与 fgets() 函数的区别:fget() 函数可以从流中读取指定长度的字符,而 get() 函数得读取一整行数据或读取到文件结尾EOF才会返回。
  2. gets() 参数 str 所指向的字符串不一定能装下一行的数据,存在缓冲区溢出的风险(即多余的字符超出了指定的目标空间)。

换行符的问题

  1. gets() 与 puts() 函数:gets() 会丢弃输入中的换行符,puts() 会在输出的末尾添加换行符。 (读删写补)
  2. fgets() 与 fputs() 函数:fgets() 会保留输入中的换行符,fputs() 不会在输出的末尾添加换行符。 (不删不补)

空字符的问题 (空字符是为了让程序直到字符串到哪里截止)

  1. gets() 函数读取整行的输入直到遇到换行符,然后会丢弃换行符,保留其余字符,并在字符末尾添加一个空字符,使之成为一个C字符串。
  2. fgets() 函数在读取输入直到第1个换行符后面,或读到文件结尾,或读取到 (n-1) 个字符,然后在末尾添加一个空字符,使之成为一个C字符串。

fgets() 函数传入 n,为什么只能读取 (n-1) 个字符?

  1. 为了在最后一位添加一个空字符,使之成为一个C字符串。保证在输出时知道字符串的截止为止。

示例:

#include <stdio.h>
#include <stdlib.h>

int main() {
	char line[81];
	while (gets(line)) { //gets()返回为NULL时,在条件中求值为0,因此不满足条件。
		puts(line);
	}
}

2.4 fseek() 与 ftell() 函数

函数声明作用
int fseek(FILE *stream, long int offset, int whence)设置流 stream 的文件位置为给定的偏移 offset;
参数 offset 意味着从给定的 whence 位置查找的字节数。
long int ftell(FILE *stream)返回给定流 stream 的当前文件位置。

fseek() 函数的 whence 参数几个常用常量:

常量描述
SEEK_SET文件的开头
SEEK_CUR文件指针的当前位置
SEEK_END文件的末尾

示例:

fseek(fp, 0L, SEEK_SET); //定位到文件开始位置
fseek(fp, 10L, SEEK_SET); //定位到文件的第10个字节
fseek(fp, 2L, SEEK_CUR);  //从文件当前位置前移2个字节
fseek(fp, 0L, SEEK_END);  //定位到文件结尾
fseek(fp, -10L, SEEK_END);  //从文件结尾回退10个字节

last = ftell(fp); //返回参数指向文件的当前位置到文件开始处的字节数。

2.5 文件结尾

在读取一个文件数据时,如何判断是否读取到文件结尾呢?

如果通过 getc() 函数读取文件时,如果读取到了文件结尾,该函数会返回一个 EOF,该值定义在 <stdio.h> 文件中。

示例

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char * argv[]) {
    // insert code here...
    int ch;
    FILE * fp = NULL;  //将指针指向一个NULL是个好习惯
    fp = fopen("Elson.txt", "r");
    if (fp == NULL) {  //校验文件是否打开成功
        exit(EXIT_FAILURE);
    }
    ch = getc(fp);
    while (ch != EOF) {
        putchar(ch);
        ch = getc(fp);
    }
    fclose(fp);
    
    return 0;
}


三、小结:文本压缩程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 40

int main(int argc, const char * argv[]) {
    FILE *fpin = NULL;
    FILE *fpout = NULL;
    int ch;
    char name[LEN];
    int count = 0;
    
    if (argc < 2) {
    	fprintf(stderr, "Usage: %s filename\n", argc[0]);
    	exit(EXIT_FAILURE);
    }
    // 打开文件
    if ((fpin = fopen(argc[1], "r") == NULL)) {
    	fprintf(stderr, "I couldn't open the file \"%s\"\n", argc[1]);
    	exit(EXIT_FAILURE);
    }
    strncpy(name, argc[1], LEN - 5); //拷贝文件名
	name[LEN - 5] = '\0';
	strcat(name, ".red"); //给文件添加后缀
	if ((fpout = fopen(name, "w") == NULL)) {
		fprintf(stderr, "Can't create output file.\n", argc[1]);
    	exit(3);
	}
	// 拷贝数据
	while ((ch = getc(fpin)) != EOF) {
		if (count++ % 3 == 0) { //每3个字符取1个字符。
			putc(ch, out);
		}
	}
	// 关闭文件流
	if (fclose(fpin) != 0 || fclose(fpout) != 0) {
		fprintf(stderr, "Error in closeing files\n");
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值