读写文件
文件的顺序读写
- 按照字符读写文件: fgetc() , fputc()
- 按照行读写文件: fputs() , fgets()
- 按照块读写文件: fread() , fwirte()
- 按照格式化读写文件: fprintf() , fscanf()
- 按照随机位置读写文件: fseek() , ftell() , rewind()
按照字符读写文件
写文件fputc
#include <stdio.h>
int fputc(int ch, FILE * stream);
功能:将ch转换为unsigned char后写⼊stream指定的文件中
参数:
ch
:需要写⼊文件的字符stream
:文件指针
返回值:
- 成功:成功写⼊文件的字符
- 失败:返回 -1
char buf[] = "this is a test for fputc";
int i = 0;
int n = strlen(buf);
for (i = 0; i < n; i++){
//往文件fp写⼊字符buf[i]
int ch = fputc(buf[i], fp);
printf("ch = %c\n", ch);
}
文件结尾
在C语⾔中,EOF表⽰文件结束符(end of file)。在while循环中以EOF
作为文件结束标志,这种以EOF
作为文件结束标志的文件,必须是文本文件。 在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF
作为文件结束标志。
#define EOF (-1)
当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。
#include <stdio.h>
int feof(FILE * stream);
功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容 (上一个内容)。
参数:
stream
:文件指针
返回值:
- 非0值:已经到文件结尾
- 0 :没有到文件结尾
命令行中输入EOF
windows:⾸先在最后一行结束后(此时未换行)输⼊ENTER键,新起一行,再输⼊ctrl+z,再输⼊ENTER键即可。
Linux:直接按CTRL+D
读文件fgetc
#include <stdio.h>
int fgetc(FILE * stream);
功能:从stream指定的文件中读取一个字符
参数:
stream
:文件指针
返回值:
- 成功:返回读取到的字符
- 失败:
-1
char ch;
#if 0
while ((ch = fgetc(fp)) != EOF){
printf("%c", ch);
}
printf("\n");
#endif
//文件没有结束,则执行循环
while (!feof(fp)) {
ch = fgetc(fp);
printf("%c", ch);
}
printf("\n");
按照行读写文件
写文件fputs
#include <stdio.h>
int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写⼊到stream指定的文件中,字符串结束符 '\0
' 不写⼊文件。
参数:
str
:字符串stream
:文件指针
返回值:
- 成功:
0
- 失败:
-1
char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };
int i = 0;
int n = 3;
for (i = 0; i < n; i++){
int len = fputs(buf[i], fp);
printf("len = %d\n", len);
}
读文件fgets
#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);
功能:从stream指定的文件内读⼊字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1
个字符为⽌,最后会⾃动加上字符 '\0
' 作为字符串结束。
参数:
str
:字符串size
:指定最⼤读取字符串的长度(size - 1
)stream
:文件指针
返回值:
- 成功:成功读取的字符串
- 读到文件尾或出错:
NULL
char buf[100] = 0;
//文件没有结束
while (!feof(fp)) {
memset(buf, 0, sizeof(buf));
char *p = fgets(buf, sizeof(buf), fp);
if (p != NULL){
printf("buf = %s", buf);
}
}
强化训练
一、实现vi及cat命令
实现vi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buf[1024] = {0};
FILE *fd = fopen(argv[1], "w+");
while (1) {
memset(buf, 0x00, 1024);
fgets(buf, 1024, stdin);
if (buf[0] == ':' && buf[1] == 'w' && buf[2] == 'q' && buf[3] == '\n') { break; }
fputs(buf, fd);
}
fclose(fd);
return 0;
}
实现cat
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE *fd = fopen(argv[1], "r");
char buf[1024];
while (1) {
fgets(buf, 1024, fd);
if (feof(fd)) { break; }
fputs(buf, stdout);
}
fclose(fd);
return 0;
}
二、文件版四则运算
有个文件⼤⼩不确定,每行内容都是一个四则运算表达式,还没有算出结果,写一个程序,⾃动算出其结果后修改文件。
实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int randi(void) { return rand() % 100; }
char randc(void) {
int a = rand() % 4;
if (a == 0) {
return '+';
} else if (a == 1) {
return '-';
} else if (a == 2) {
return '*';
} else {
return '/';
}
}
int writ(FILE *fd) {
for (int i = 0; i < 10; i++) {
int ret1 = randi();
int ret2 = randi();
if (ret1 == 0) { ret1 += randi(); }
if (ret2 == 0) {
ret2 += randi();
}
fprintf(fd, "%d%c%d=\n", ret1, randc(), ret2);
}
return 0;
}
float ret(char ch, int a, int b, float c) {
switch (ch) {
case '+':
c = a + b;
break;
case '-':
c = a - b;
break;
case '*':
c = a * b;
break;
case '/':
c = ((float) a) / b;
break;
}
return c;
}
void read(FILE *fd) {
char buf[1024] = {0};
char buf2[1024] = {0};
char buf3[4096] = {0};
int a, b;
float c;
char ch;
rewind(fd);
while (1) {
fgets(buf, 1024, fd);
if (feof(fd)) { break; }
sscanf(buf, "%d%c%d=\n", &a, &ch, &b);
c = ret(ch, a, b, c);
sprintf(buf2, "%2d%c%2d=%.2f\n", a, ch, b, c);
strcat(buf3, buf2);
}
rewind(fd);
fputs(buf3, fd);
}
int main(int argc, char *argv[]) {
srand((unsigned int) time(NULL));
FILE *fd = fopen("02.txt", "w+");
if (fd == NULL) { return -1; }
writ(fd);
read(fd);
fclose(fd);
return 0;
}
按照格式化读写文件
写文件fprintf
#include <stdio.h>
int fprintf(FILE * stream, const char * format, ...);
功能:根据参数 format 字符串来转换并格式化数据,然后将结果输出到 stream 指定的文件中,指定出现字符串结束符 '\0
' 为⽌。
参数:
stream
:已经打开的文件format
:字符串格式,用法和printf()
一样
返回值:
- 成功:实际写⼊文件的字符个数
- 失败:
-1
fprintf(fp, "%d %d %d\n", 1, 2, 3);
读文件fscanf
#include <stdio.h>
int fscanf(FILE * stream, const char * format, ...);
功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:
stream
:已经打开的文件format
:字符串格式,用法和scanf()
一样
返回值:
- 成功:参数数目,成功转换的值的个数
- 失败:
-1
int a = 0;
int b = 0;
int c = 0;
fscanf(fp, "%d %d %d\n", &a, &b, &c);
printf("a = %d, b = %d, c = %d\n", a, b, c);
强化训练
文件版排序
实现
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int randi(void);
void make(void);
void sort(void);
int main(int argc, char *argv[]){
srand((unsigned int)time(NULL));
if (strcmp(argv[1], "make") == 0) {
make();
} else if (strcmp(argv[1], "sort") == 0) {
sort();
}
return 0;
}
int randi(void){
return rand() % 100;
}
void make(void){
FILE *fp = fopen("04test.txt", "w+");
if (fp == NULL) {
puts("随机数生成失败");
perror("open");
} else {
for (int i = 0; i < 10; ++i) {
fprintf(fp, "%d ", randi());
}
fclose(fp);
}
}
void sort(void) {
int array[10] = {0};
int index = 0;
FILE *fp = fopen("04test.txt", "r");
if (fp == NULL) {
puts("读取随机数文件失败");
perror("open");
} else {
while(!feof(fp)){
fscanf(fp, "%d ", &array[index]);
index++;
}
fclose(fp);
}
printf("排序前:");
for (int i = 0; i < 10; ++i){
printf("%d ", array[i]);
}
puts("");
int tmp = 0;
for(int i = 0; i < 10; ++i){
for(int j = 0; j < 10-i-1; ++j){
if(array[j] > array[j+1]){
tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
printf("排序后:");
for (int i = 0; i < 10; ++i){
printf("%d ", array[i]);
}
puts("");
}
按照块读写文件
写文件fwrite
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以数据块的⽅式给文件写⼊内容。
参数:
ptr
:准备写⼊文件数据的地址size
:size_t
为unsigned int
类型,此参数指定写⼊文件内容的块数据⼤⼩nmemb
:写⼊文件的块数,写⼊文件数据总⼤⼩为:size * nmemb
stream
:已经打开的文件指针
返回值:
- 成功:实际成功写⼊文件数据的块数目,此值和 nmemb 相等
- 失败:
0
typedef struct Stu{
char name[50];
int id;
}Stu;
Stu s[3];
int i = 0;
for (i = 0; i < 3; i++){
sprintf(s[i].name, "stu%d%d%d", i, i, i);
s[i].id = i + 1;
}
int ret = fwrite(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
读文件fread
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以数据块的⽅式从文件中读取内容。
参数:
ptr
:存放读取出来数据的内存空间size
: size_t 为 unsigned int类型,此参数指定读取文件内容的块数据⼤⼩nmemb
:读取文件的块数,读取文件数据总⼤⼩为:size * nmembstream
:已经打开的文件指针
返回值:
- 成功:实际成功读取到内容的块数,如果此值比nmemb⼩,但⼤于0,说明读到文件的结尾
- 失败:
0
0
: 表⽰读到文件结尾。(feof()
)
typedef struct Stu{
char name[50];
int id;
}Stu;
Stu s[3];
int ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
int i = 0;
for (i = 0; i < 3; i++){
printf("s = %s, %d\n", s[].id);
}
强化训练
大文件拷贝
实现
文件的随机读写
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:移动文件流(文件光标)的读写位置。
参数:
stream
:已经打开的文件指针offset
:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正 数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动 的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写 ⼊时将增⼤文件尺⼨。 whence :其取值如下:SEEK_SET
:从文件开头移动offset个字节SEEK_CUR
:从当前位置移动offset个字节SEEK_END
:从文件末尾移动offset个字节
返回值:
- 成功:
0
- 失败:
-1
#include <stdio.h>
long ftell(FILE *stream);
功能:获取文件流(文件光标)的读写位置。
参数:
stream
:已经打开的文件指针
返回值:
- 成功:当前文件流(文件光标)的读写位置
- 失败:
-1
#include <stdio.h>
void rewind(FILE *stream);
功能:把文件流(文件光标)的读写位置移动到文件开头。
参数:
stream
:已经打开的文件指针
返回值:⽆返回值
typedef struct Stu{
char name[50];
int id;
}Stu;
//假如已经往文件写⼊3个结构体
//fwrite(s, sizeof(Stu), 3, fp);
Stu s[3];
Stu tmp;
int ret = 0;
//文件光标读写位置从开头往右移动2个结构体的位置
fseek(fp, 2 * sizeof(Stu), SEEK_SET);
//读第3个结构体
ret = fread(&tmp, sizeof(Stu), 1, fp);
if (ret == 1){
printf("[tmp]%s, %d\n", tmp.name, tmp.id);
}
//把文件光标移动到文件开头
//fseek(fp, 0, SEEK_SET);
rewind(fp);
ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);
int i = 0;
for (i = 0; i < 3; i++){
printf("s === %s, %d\n", s[i].name, s[i].id);
}