文件操作说白了,就是把原来输在终端上的数据输到磁盘文件上保存。
其核心就是学习一些文件的函数,其他操作都和前面的一样。
打开、关闭文件
打开:
*FILE *fopen(const char *path, const char mode);
path :路径, path 参数可以是相对路径(…/fishc.txt)也可以是绝对路径(/home/FishC/fishc.txt),如果只给出文件名而不包含路径,则表示该文件在当前文件夹中;
mode:模式,常用模式有’r’(read),‘w’(write),‘a’(append),只读,从零开始写,在原有后面添加;
返回值:
- 如果文件打开成功,则返回一个指向打开的这个文件的文件指针;
- 如果文件打开失败,则返回 NULL 并设置 errno 为指定的错误。
关闭:
*int fclose(FILE fp);
返回值:
- 如果文件关闭成功,返回值是 0;
- 如果文件关闭失败,返回值是 EOF,并设置 errno 为指定的错误。( 磁盘已满、设备出错或者 I/O 错误均可能导致 fclose 函数调用失败)
- 只有调用了fclose函数才能将缓存区里的数据写入文件中
例子:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int ch;
if ((fp = fopen("hello.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
while ((ch = getc(fp)) != EOF)
{
putchar(ch);
}
fclose(fp);
return 0;
}
读取、写入字符
读取单个字符
注意:getc和fgetc,getc是宏操作,比较快,一般推荐这个;遇到i++这类的就用fgetc;
*int getc(FILE stream);
*int fgetc(FILE stream);
返回值:
- 该函数将读取到的 unsigned char 类型转换为 int 类型并返回;
- 如果文件结束或者遇到错误则返回 EOF。
写入单个字符
注意:putc和fputc,putc是宏操作,比较快,一般推荐这个;遇到i++这类的就用fputc;
*int putc(int c, FILE stream);
*int fputc(int c, FILE stream);
返回值:
- 如果函数没有错误,返回值是写入的字符;
- 如果函数发生错误,返回值是 EOF。
读取、写入字符串
读取字符串
*char *fgets(char *s, int size, FILE stream);
fgets 函数最多可以读取 size - 1 个字符,因为结尾处会自动添加一个字符串结束符 ‘\0’。当读取到换行符(’\n’)或文件结束符(EOF)时,表示结束读取(’\n’ 会被作为一个合法的字符读取)。
返回值:
- 如果函数调用成功,返回 s 参数指向的地址。
- 读到一半就结束了,则 eof 指示器被设置;如果还没读入任何字符就遇到这种 EOF,则 s 参数指向的位置保持原来的内容,函数返回 NULL。
- 如果在读取的过程中发生错误,则 error 指示器被设置,函数返回 NULL,但 s 参数指向的内容可能被改变。
#include <stdio.h>
#define MAX 1024
int main()
{
char str[MAX];
printf("请输入一个字符串:");
fgets(str, MAX, stdin);
printf("您输入的内容是:%s", str);
return 0;
}
写入字符串
*int fputs(const char *s, FILE stream);
返回值:
- 如果函数调用成功,返回一个非 0 值;
- 如果函数调用失败,返回 EOF。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int ch;
if ((fp = fopen("file.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fputs("I love FishC.com!\n", fp);
fclose(fp);
return 0;
}
格式化输出、输入文件内容
fscanf():将年月日从文件中格式化输出
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int year, month, day;
if ((fp = fopen("file.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fscanf(fp, "%d-%d-%d", &year, &month, &day);
printf("%d-%d-%d\n", year, month, day);
fclose(fp);
return 0;
}
fprintf() 将内容格式化输入文件内
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
if ((fp = fopen("file.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
fprintf(fp, "I love FishC.com!\n");
fprintf(fp, "Today is %d-%d-%d\n", 2017, 7, 16);
fclose(fp);
return 0;
}
二进制读写文件
fread、fwrite:以整个内存的形式读入,即以二进制的形式读入,cat文件显示的是乱码;
*size_t fread(void *ptr, size_t size, size_t nmemb, FILE stream);
*size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE stream);
ptr 内存块指针,该内存块的尺寸最小应该是 size * nmemb 个字节
size 指定要写入的每个元素的大小
nmemb 指定要写入的元素个数
stream 指向待读取文件的文件指针
返回值:
- 返回值是实际读取到的元素个数(nmemb);
- 如果返回值比 nmemb 参数的值小,表示可能读取到文件末尾或者有错误发生(可以使用 feof 函数或 ferror 函数进一步判断)。
例子:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char name[40];
struct Date date;
};
int main(void)
{
FILE *fp;
struct Book *book_for_write, *book_for_read;
// 为结构体分配堆内存空间
book_for_write = (struct Book *)malloc(sizeof (struct Book));
book_for_read = (struct Book *)malloc(sizeof (struct Book));
if (book_for_write == NULL || book_for_read == NULL)
{
printf("内存分配失败!\n");
exit(EXIT_SUCCESS);
}
// 填充结构体数据
strcpy(book_for_write->name, "《三体》");
book_for_write->date.year = 2017;
book_for_write->date.month = 11;
book_for_write->date.day = 11;
if ((fp = fopen("file.txt", "w")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_SUCCESS);
}
// 将整个结构体写入文件中
fwrite(book_for_write, sizeof(struct Book), 1, fp);
// 写入完成,关闭保存文件
fclose(fp);
// 重新打开文件,检测是否成功写入数据
if ((fp = fopen("file.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(EXIT_FAILURE);
}
// 在文件中读取结构体并打印到屏幕上
fread(book_for_read, sizeof(struct Book), 1, fp);
printf("书名:%s\n", book_for_read->name);
printf("出版日期:%d-%d-%d\n", book_for_read->date.year, book_for_read->date.month, book_for_read->date.day);
fclose(fp);
return 0;
}