C/C++学习之路_九:文件操作


目录

  1. 概述
  2. 文件的顺序读写
  3. 文件的随机读写
  4. windows和linux文本
  5. 获取文件状态
  6. 删除文件、重命名文件
  7. 文件缓冲区

1. 概述

1. 磁盘文件和设备文件

  1. 磁盘文件:指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。
  2. 设备文件:在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。

2. 磁盘文件的分类

  1. 计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储。
  2. 从用户或者操作系统使用的角度(逻辑上)把文件分为:
    1. 文本文件:基于字符编码的文件
    2. 二进制文件:基于值编码的文件

3. 文本文件和二进制文件

  1. 文本文件

    1. 基于字符编码,常见编码有ASCII、UNICODE等
    2. 一般可以使用文本编辑器直接打开
    3. 数5678的以ASCII存储形式(ASCII码)为:00110101 00110110 00110111 00111000
  2. 二进制文件

    1. 基于值编码,自己根据具体应用,指定某个值是什么意思
    2. 把内存中的数据按其在内存中的存储形式原样输出到磁盘上
    3. 数5678的存储形式(二进制码)为:
    4. 00010110 00101110

2 文件的打开和关闭

1. 文件指针

  1. 在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。
typedef struct
{short           level; //缓冲区"满"或者"空"的程度 unsigned        flags; //文件状态标志 char            fd;        //文件描述符unsigned char   hold;    //如无缓冲区不读取字符short           bsize;  //缓冲区的大小unsigned char   *buffer;//数据缓冲区的位置 unsigned        ar;   //指针,当前的指向 unsigned        istemp;  //临时文件,指示器short           token; //用于有效性的检查
}FILE;
  1. FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。

  2. 声明FILE结构体类型的信息包含在头文件“stdio.h”中,一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。通过文件指针就可对它所指的文件进行各种操作。

  3. C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:

    1. stdin: 标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
    2. stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
    3. stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。

2. 文件的打开

  1. 任何文件使用之前必须打开:
#include <stdio.h>
FILE * fopen(const char * filename, const char * mode);
功能:打开文件
参数:filename:需要打开的文件名,根据需要加上路径mode:打开文件的模式设置
返回值:成功:文件指针失败:NULL
  1. 第一个参数的几种形式:
 FILE *fp_passwd = NULL;//相对路径://打开当前目录passdw文件:源文件(源程序)所在目录FILE *fp_passwd = fopen("passwd.txt", "r");//打开当前目录(test)下passwd.txt文件fp_passwd = fopen("./test/passwd.txt", "r");//打开当前目录上一级目录(相对当前目录)passwd.txt文件fp_passwd = fopen("../passwd.txt", "r");//绝对路径, 打开C盘test目录下一个叫passwd.txt文件fp_passwd = fopen("c:/test/passwd.txt","r");
  1. 第二个参数的几种形式(打开文件的方式):
打开模式 含义
r或rb 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)
w或wb 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
a或ab 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件
r+或rb+ 以可读、可写的方式打开文件(不创建新文件)
w+或wb+ 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
a+或ab+ 以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件
  1. 注意:

    1. b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
    2. Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
    3. 在Windows平台下,以“文本”方式打开文件,不加b:
    4. 当读取文件的时候,系统会将所有的 “\r\n” 转换成 “\n”
    5. 当写入文件的时候,系统会将 “\n” 转换成 “\r\n” 写入
    6. 以"二进制"方式打开文件,则读写都不会进行这样的转换
    7. 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出
int main() {FILE *fp = NULL;fp = fopen("./test.txt", "w");if (fp == NULL) {perror("open fail!");return -1;}return 0;
}

3. 文件的关闭

  1. 任何文件在使用后应该关闭:

    1. 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
    2. 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
    3. 如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。
#include <stdio.h>
int fclose(FILE * stream);
功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
参数:stream:文件指针
返回值:成功:0失败:-1FILE * fp = NULL;fp = fopen("abc.txt", "r");fclose(fp);

2. 文件的顺序读写

1. 按照字符读写文件

1. 写文件

#include <stdio.h>
int fputc(int ch, FILE * stream);
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:ch:需要写入文件的字符stream:文件指针
返回值:成功:成功写入文件的字符失败:返回-1
int main() {FILE *fp = NULL;fp = fopen("./test.txt", "w");if (fp == NULL) {perror("open fail!");return -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);}fclose(fp);return 0;
}

2. 文件结尾

  1. 在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。
  2. 在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
#define EOF     (-1)
  1. 当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。
  2. 为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。
#include <stdio.h>
int feof(FILE * stream);
功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)。
参数:stream:文件指针
返回值:非0值:已经到文件结尾0:没有到文件结尾

3. 读文件

#include <stdio.h>
int fgetc(FILE * stream);
功能:从stream指定的文件中读取一个字符
参数:stream:文件指针
返回值:成功:返回读取到的字符失败:-1
    char ch;/*while ((ch = fgetc(fp))!=EOF){printf("%c",ch);}printf("\n");*/while (!feof(fp)){ch = fgetc(fp);printf("%c",ch);}printf("\n");

2. 按照行读写文件

1. 写文件

#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);}

2. 读文件

#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);}}

3. 按照格式化文件

1. 写文件

#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);

2. 读文件

#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);

4. 按照块读写文件

1. 写文件

#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 * nmembstream:已经打开的文件指针
返回值:成功:实际成功写入文件数据的块数目,此值和 nmemb 相等失败:0
typedef struct Stu {char name[50];int id;
} Stu;int Write() {FILE *fp = NULL;fp = fopen("./test.txt", "w");if (fp == NULL) {perror("open fail!");return -1;}Stu s[3];for (int 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);fclose(fp);return 0;
}

2. 读文件

#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
    Stu s[3];int ret = fread(s, sizeof(Stu), 3, fp);printf("ret=%d\n", ret);for (int i = 0; i < 3; i++) {printf("s = %s, %d", s[i].name, s[i].id);}

4. 文件的随机读写

#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:已经打开的文件指针
返回值:无返回值
    FILE *fp = NULL;fp = fopen("./test.txt", "r");if (fp == NULL) {perror("open fail!");return -1;}Stu s[3];Stu tmp;int ret = 0;fseek(fp, 2 * sizeof(Stu), SEEK_SET);ret = fread(&tmp, sizeof(Stu), 1, fp);if (ret == 1) {printf("[tmp]%s, %d\n", tmp.name, tmp.id);}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);}

5. windows和linux文本

  1. b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
  2. Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
  3. 在Windows平台下,以“文本”方式打开文件,不加b:
  4. 当读取文件的时候,系统会将所有的 “\r\n” 转换成 “\n”
  5. 当写入文件的时候,系统会将 “\n” 转换成 “\r\n” 写入
  6. 以"二进制"方式打开文件,则读\写都不会进行这样的转换
  7. 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出
  8. 判断文本文件是Linux格式还是Windows格式:
int main(int argc, char **args) {if (argc < 2)return 0;FILE *p = fopen(args[1], "rb");if (!p)return 0;char a[1024] = {0};fgets(a, sizeof(a), p);int len = 0;while (a[len]) {if (a[len] == '\n') {if (a[len - 1] == '\r') {printf("windows file\n");} else {printf("linux file\n");}}len++;}fclose(p);return 0;
}

6. 获取文件状态

#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
功能:获取文件状态信息
参数:
path:文件名
buf:保存文件信息的结构体
返回值:
成功:0
失败-1struct stat {dev_t         st_dev;         //文件的设备编号ino_t         st_ino;          //节点mode_t        st_mode;   //文件的类型和存取的权限nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1uid_t         st_uid;         //用户IDgid_t         st_gid;         //组IDdev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号off_t         st_size;        //文件字节数(文件大小)unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)unsigned long st_blocks;    //块数time_t        st_atime;     //最后一次访问时间time_t        st_mtime;    //最后一次修改时间time_t        st_ctime;     //最后一次改变时间(指属性)
};

7. 删除文件、重命名文件

#include <stdio.h>
int remove(const char *pathname);
功能:删除文件
参数:pathname:文件名
返回值:成功:0失败:-1#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
功能:把oldpath的文件名改为newpath
参数:
oldpath:旧文件名
newpath:新文件名
返回值:
成功:0
失败: - 1

8. 文件缓冲区

1. 文件缓冲区

  1. ANSI C标准采用“缓冲文件系统”处理数据文件。
  2. 所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。
  3. 如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量) 。

2. 磁盘文件的存取

  1. 磁盘文件,一般保存在硬盘、U盘等掉电不丢失的磁盘设备中,在需要时调入内存
  2. 在内存中对文件进行编辑处理后,保存到磁盘中
  3. 程序与磁盘之间交互,不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率

3. 更新缓冲区

#include <stdio.h>
int fflush(FILE *stream);
功能:更新缓冲区,让缓冲区的数据立马写到文件中。
参数:
stream:文件指针
返回值:
成功:0
失败:-1

C/C++学习之路_九:文件操作相关推荐

  1. C/C++学习之路_七: 内存管理

    C/C++学习之路_七: 内存管理 目录 作用域 内存布局 内存分区代码分析 1. 作用域 C语言变量的作用域分为: 代码块作用域(代码块是{}之间的一段代码) 函数作用域 文件作用域 1. 局部变量 ...

  2. C/C++学习之路_六: 指针

    C/C++学习之路_第六章: 指针 目录 概述 指针基础知识 指针和数组 多级指针 指针和函数 指针和字符串 指针小结 1. 概述 1. 内存 内存含义: 存储器:计算机的组成中,用来存储程序和数据, ...

  3. C/C++学习之路_八: 复合类型

    C/C++学习之路_八: 复合类型 目录 结构体 共用体(联合体) 枚举 typedef 1. 结构体 1. 概述 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年 ...

  4. Python学习系列(五)(文件操作及其字典)

    Python学习系列(五)(文件操作及其字典) Python学习系列(四)(列表及其函数) 一.文件操作 1,读文件      在以'r'读模式打开文件以后可以调用read函数一次性将文件内容全部读出 ...

  5. python history文件_【python之路19】文件操作

    一.打开文件 文件句柄 = open('文件路径', '模式') 打开文件时,需要指定文件路径和以何等方式打开文件,打开后,即可获取该文件句柄,日后通过此文件句柄对该文件操作. 打开文件的模式有: r ...

  6. Hive学习之路(二):Hive表操作详讲

    操作内容简介 一.操作前的准备 二.Hive表操作详讲 1. 创建数据库 2. 查看所有数据库/表 3. 在Hive上直接操作HDFS 4. 在Hive上直接执行终端命令 5. 创建数据表/查看表的信 ...

  7. Golang 学习笔记(08)—— 文件操作

    path 在path包中封装了一些路径相关的操作,在开始接触文件操作之前,我们先看看路径的相关函数.在Linux中,路径的格式为/user/bin路径中分隔符是/:Windows中的路径格式为c:\W ...

  8. python学习 day8_容器的方法文件操作

    python学习day8 列表的相关函数 增 1.append 向列表的末尾添加新的元素 2.insert 在指定索引之前插入元素 3.extend 迭代追加所有元素 删 1.pop 通过指定索引删除 ...

  9. python 系统学习笔记(八)---文件操作

    模式 描述 r 以读方式打开文件,可读取文件信息. w 以写方式打开文件,可向文件写入信息.如文件存在,则清空该文件,再写入新内容 a 以追加模式打开文件(即一打开文件,文件指针自动移到文件末尾),如 ...

最新文章

  1. python怎么开发安卓程序_怎样用python开发安卓app-到底如何使用Python开发Android程序.txt...
  2. c语言中说取消标识符是,2019年全国计算机二级C语言考试考点解析(3)
  3. idea好用的快捷键
  4. python笔记之scrapy
  5. 登录时本地保存账号密码及关闭ARC的方法
  6. 山寨版项目管理经验小结
  7. 为什么你应该用Yarn而不是Npm来管理你的项目依赖?
  8. C++ 数组与指针详解之终极无惑
  9. Vue中路由管理器Vue Router使用介绍(三)
  10. 从零实现循环神经网络
  11. jsp中运用html语言,JSP技术-第2章 HTML语言.doc
  12. 生成随机不重复数列表(C#)
  13. git提交代码时遇到代码库有更新以及本地有更新的解决方法
  14. 【概率论】极大似然估计和最大后验估计
  15. 梯形面积php,梯形的面积公式是什么
  16. vue+ele 使用及demo
  17. VirtualBox中不能正常使用OpneGL的问题
  18. CDH安装指南——酒仙网技术
  19. 安卓投屏神器scrcpy
  20. 华硕N55SF 折腾记

热门文章

  1. 7z制作自解压安装包
  2. Linux下查看CPU型号,内存大小,硬盘空间命令
  3. 10.10SSD安装盘clover 下开启trim功能
  4. 独立思考者模型:寻找潜藏在表象背后的真相 探寻真相的方法
  5. Python高级运维开发2016年7月第14期隆重开课
  6. SQL Server-流程控制 7,Return 语句
  7. CodeForces - 1523E Crypto Lights(组合数学+推公式)
  8. CodeForces - 817F Graph and String(dfs判二分图)
  9. 牛客 - 牛牛与牛妹的约会(贪心)
  10. POJ - 1958 Strange Towers of Hanoi(线性dp)