文件IO之标准IO
标准IO
我们是知道系统IO是挺好用。但是在不同操作系统下面,对文件一些管理和API接口都是不一样的。同一 个文件,在不同的操作系统下面, 操作文件的代码其实不一样的。C/C++几乎支持所有的操作系统、使 用C/C++操作文件(系统IO)可移植性非常差。于是C/C++语言标准委员会指定/统一文件操作的接口: 标准IO
标准IO是ANSI C建立的一个标准I/O模型,不依赖系统内核,因此具有良好的移植性。它提供了一系列用 于文件输入输出的函数。
标准IO是建立在系统调用IO之上的高级抽象。标准IO通过封装系统调用IO的底层细节,提供了一套更为 简便和易用的接口,这些函数在内部通过缓冲机制来优化性能,并减少直接对系统调用的次数。
标准IO通过缓冲机制来提高I/O操作的效率。**缓冲是一种技术,**它通过在内存中开辟一块区域(称为缓冲 区)来暂时存放输入或输出的数据,以减少对底层设备(如硬盘、终端等)的直接访问次数,从而提高 I/O操作的效率
缓冲的优点
-
提高性能
-
缓冲可以减少系统调用的次数。每次系统调用都会带来一定的开销。通过缓冲,可以将多个小
的I/O请求合并成较大的请求,从而减少系统调用的次数,提高性能。
-
缓冲还可以减少磁盘I/O的次数。磁盘I/O操作比内存操作慢得多,通过缓冲可以将多个写操作
合并到一次磁盘I/O中,从而减少磁盘I/O的次数,提高数据写入的速度。
-
-
平滑I/O需求峰值
-
缓冲可以用来平滑I/O需求峰值。当I/O设备(如打印机、磁盘等)的负载较高时,通过缓冲可
以暂时存储待处理的数据,等待设备负载降低后再进行处理,从而避免数据丢失或处理延迟。
-
-
提高数据一致性
-
在某些情况下,如数据库操作中,缓冲可以用来确保数据的一致性。通过先将数据写入缓冲
区,再统一刷新到磁盘上,可以避免因系统崩溃等原因导致的数据不一致问题。
-
-
简化编程
- 标准IO库提供了丰富的缓冲机制,使得程序员在进行I/O操作时无需关心底层的缓冲细节,从 而简化了编程工作。
缓冲分为三种类型:全缓冲、行缓冲和无缓冲。用户可以通过setbuf 、 setvbuf 或 setbuffer
来设置文件流的缓冲类型。
全缓冲:当缓冲区满时,或者当调用fflush
函数、文件被关闭、或程序正常结束时,缓冲区的内容会被全部写入到文件中。
行缓冲:当遇到换行符(\n)时,或者当缓冲区满时,或者当调用 fflush
函数、文件被关闭、或程序 正常结束时,缓冲区的内容会被写入到文件中。( printf :行缓冲
)
无缓冲:每次读写操作都直接对文件进行,不使用缓冲区。
编译器怎么区分标准IO应该调用Linux的系统还是还是Window的系统IO呢?
每个操作系统定义一个宏,在使用条件编译(#ifdef #endif
,不同的操纵系统执行不同的宏
例如:
- Windows:通常会定义
_WIN32
或_WIN64
宏。 - Linux:通常会定义
__linux__
宏。 - macOS:通常会定义
__APPLE__
和__MACH__
宏
1 标准IO流
在Linux中,IO流(Input/Output Stream)指的是一种用于数据输入和输出的机制。这里的“流”可以理
解为一种连续的数据序列,可以是字节流或字符流,用于在程序的不同部分之间、程序与外设(如磁
盘、网络等)之间传输数据。
Linux中的IO操作主要涉及对文件的读写操作,而文件在Linux中被视为一种特殊的资源,可以是磁盘上
的普通文件、目录、字符设备(如终端)、块设备(如硬盘)等。因此,IO流在Linux中主要指的是对
这些文件资源的读写操作流。
Linux中的IO流可以分为以下几种类型:
-
标准IO流:
在标准IO库下面,系统会为程序自动打开三个标准IO流,它们分别是:
-
标准输入流(
FILE *stdin
):- 定义在
<stdio.h>
头文件中,通常指向**标准输入设备(**如键盘)。 - 用于从标准输入设备读取数据。
- 在UNIX和类UNIX系统中,其文件描述符为
STDIN_FILENO
(通常为0)(例如scanf
函数)
- 定义在
-
*标准输出流( FILE stdout)
- 同样定义在
<stdio.h>
头文件中,通常指向标准输出设备(如显示器或终端)。 - 用于向标准输出设备输出(写入)数据。
- 在
UNIX和类UNIX
系统中,其文件描述符为STDOUT_FILENO(
通常为1)。( 例如printf
函数)
- 同样定义在
-
*标准错误输出流( FILE stderr):
- 也定义在<stdio.h> 头文件中,用于向标准错误输出设备输出数据(通常也是显示器或终端,但可以与 标准输出分开显示,以便于区分错误信息)。
- 在UNIX和类UNIX系统中,其文件描述符为
STDERR_FILENO
(通常为2)。(例如perror
函数 )
-
-
网络IO流:
- 网络IO流是指在网络通信过程中,数据在客户端和服务器之间传输的流。在Linux中,网络IO流通常是
通过套接字(Socket)编程接口来实现的。
网络IO流涉及到数据的发送和接收操作,这些操作可以通过系统调用(如
send
、recv
等或更高级的网络编程库(如libevent、libev
等)来完成。
- 网络IO流是指在网络通信过程中,数据在客户端和服务器之间传输的流。在Linux中,网络IO流通常是
-
其他类型的IO流:
除了其他三种常见的IO流之外,Linux还支持其他类型的IO流,如管道(Pipe)、命名管道(Named
Pipe)、消息队列(Message Queue)等。这些IO流提供了不同的数据传输方式和同步机制,以满足不
同场景下的需求。 -
文件IO流:
文件IO流是通过Linux系统调用(如 open 、 read 、 write 、 close 等
)直接对文件进行操作的一种机 制。与标准IO流不同,文件IO流不经过C语言标准库的缓冲机制,因此每次调用系统调用都会直接操作 底层设备(比较慢)。
文件IO流提供了更底层的控制能力**,允许程序员直接管理缓冲区的大小、位置等参数**,从而实现更高效 的IO操作。 在Linux中,文件类型主要可以分为两大类,即普通(文本)文件和二进制文件(在此分类中,我们排除 设备文件等特殊类型)。
-
文本文件:无组织、无格式的文件、以字符的ASCII来解析文件。
- 例如
.cpp、.c、.txt、.cs、.html、.java、.py...
文件
- 例如
-
**二进制文件:**有特定格式的文件
- 需要按照特定的文件格式解析
- 某一些字节可能代表特殊含义
- 例如
.exe、.out、.apk、.jpg、.mp4、.doc、.xls...
文件
1.1 打开文件流
fopen
函数是C语言标准库(stdio.h
提供的一个用于打开文件的函数。这个函数通过指定文件名和模 式(如只读、只写、读写等),来打开文件,并返回一个指向 FILE
文件操作,如读写文件
fopen
函数的原型如下:
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
- 参数
path
参数指定了要打开文件的路径和名称。
mode
参数指定了文件的打开模式
"r"
表示只读打开,
"w"
表示只写打开(如果文件已存在则覆盖),
"a"
表示追加写入(文件不存在则创建)
"r+"
读写模式。文件必须存在。文件指针将放在文件的开头。允许读取和写入操作。
"w+"
读写模式。如果文件存在,其长度将被截断为零(即文件内容会丢失)。如果文件不存在,则创
建新文件。文件指针将放在文件的开头。允许读取和写入操作。
"a+"
读写模式。如果文件存在,写入的数据将被追加到文件末尾。如果文件不存在,则创建新文件以
写入数据
如果是二进制文件还可以在上述模式前添加字符 “b”,如"rb" , "r+b"
- 返回值
- 如果文件成功打开,
fopen
函数将返回一个指向FLLE
结构体的指针。 - 如果文件打开失败(例如,由于权限问题或路径不存在),则返回
NULL
; FILE
结构体是C标准I/O库中定义的一个结构体类型,用于表示一个流(stream)。通过这个指针,程 序可以访问和操作打开的文件。
- 如果文件成功打开,
1.2 关闭文件流
fclose
函数是 C 语言标准输入输出库(stdio.h)中的一个函数,用于关闭一个打开的文件。当文件不
再需要被访问时,应该使用fclose
函数来关闭它。关闭文件是一个好习惯,因为它可以释放文件相关
的资源,并确保所有缓冲的输出都被正确地写入文件中。
函数原型如下:
int fclose(FILE *stream);
参数:
FILE *stream
是指向 FILE
对象的指针,该对象标识了要关闭的文件。这个指针是之前通过调用如 freopen 或 fdopen
等函数打开文件时返回的。
返回值:
如果成功关闭文件,fclose
函数返回 0。如果发生错误,则返回 EOF
(通常定义为 -1,但这是一个 宏,在不同的环境中可能有所不同)。然而,请注意,尽管 fclose
在关闭文件时可能会遇到错误(如 写错误),但它在大多数情况下都能成功关闭文件,即使返回了EOF
示例
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fd = fopen("text.txt", "a+");// 失败if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");// 关闭文件fclose(fd);return 0;
}
fclose在文件关闭的时候,会同步缓冲区的内容到硬件中
1.3 文件流读取
- fgets函数
fgets
函数是 C 语言标准输入输出库(stdio.h
)中的一个函数,用于从指定的文件流(或标准输入, 即键盘输入)中读取一行文本。这个函数会读取直到遇到**换行符( '\n'
)、文件结束符(EOF
)或已读 取了指定数量的字符(**不包括最后的空字符 '\0'
,该函数会自动在字符串末尾添加这个空字符作为字 符串的结束标志)为止的文本。
函数原型如下:
char *fgets(char *str, int n, FILE *stream);
-
参数
-
str
是一个指向字符数组的指针,该数组将存储从文件中读取的字符串。 -
n
是要读取的最大字符数(包括最后的空字符stream
是一个指向'\0'
)。因此,如果希望字符串包含最多n-1
个字符, 应该将这个值作为n
的参数。 -
FILE
对象的指针,该对象指定了要从中读取数据的文件流。如果stream 是 stdin
,则函数将从标准输入读取数据。
-
-
返回值
-
如果成功,
fgets
会返回指向str
的指针。 -
如果到达文件末尾或发生错误,则返回 NULL 。
-
需要注意的是,即使成功读取了换行符,它也会被存储在字符串中,并且会在换行符之后添加一个空字 符
'\0'
来结束字符串。
-
示例
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fd = fopen("text.txt", "a+");// 失败if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");// /为存储缓冲区,用于临时存储读取的文件内容。char buffer[256] = {0};// 从文件读取fgets(buffer, sizeof(buffer) - 1, fd);printf("%s", buffer);// 从标准输入读取fgets(buffer, sizeof(buffer) - 1, stdin);printf("%s", buffer);// 关闭文件fclose(fd);return 0;
}只读取一行文本
- fread函数
fread
函数是 C 语言标准输入输出库(stdio.h
)中用于从文件流(或其他输入流)中读取数据块的函 数。与 fgets
不同,fread
不会解析数据,而是按照指定的字节数直接从流中读取数据到内存缓冲区 中。这使得 fread
非常适合于读取二进制文件或需要精确控制读取数据大小的情况。
函数原型如下:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
-
- 参数
ptr
是指向内存缓冲区的指针,该缓冲区用于存储从文件流中读取的数据。size
是每个数据项的大小(以字节为单位)。nmemb
是要读取的数据项的数量。stream
是指向 FILE 对象的指针,该对象指定了要从中读取数据的文件流。
- 返回值
fread
函数返回成功读取的数据项的数量。如果遇到文件末尾或发生错误,则可能返回小于nmemb
的
值。在成功读取的情况下,返回的值乘以size
等于实际读取的字节数。
- 参数
例子
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fd = fopen("text.txt", "a+");// 失败if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");// /为存储缓冲区,用于临时存储读取的文件内容。char buffer[256] = {0};// 从文件读取fread(buffer,1 ,100, fd);printf("%s", buffer);// 关闭文件fclose(fd);return 0;
}
读取文件全部内容
-
fcanf函数
fscanf
函数是C语言标准输入输出库(stdio.h
)中的一个重要函数,用于从文件流中读取格式化的输 入数据。fscanf
函数根据指定的格式字符串从文件流中读取数据,并将读取到的数据存储到提供的变量中。它会在遇到空格、制表符或换行符时停止读取当前字段。函数原型如下:
int fscanf(FILE *stream, const char *format, [argument...]);
-
参数(和scanf函数参数一样)
-
stream
:指向FILE对象的指针,该FILE对象标识了要从中读取数据的文件流。 -
format
:一个字符串,定义了要读取的数据的类型和格式。它包含格式说明符、空格字符、非空字符等,用于指定如何解析输入流中的数据。 -
[argument...]
:可变参数列表,用于存储从文件中读取的数据。这些参数的类型和数量应该与format
字符串中指定的格式说明符相匹配(要是地址)。
-
-
返回值
fscanf
函数返回成功匹配并赋值的数据项数。如果到达文件末尾或发生读取错误,则返回EOF
(通常 是-1)。因此,可以通过检查返回值来判断是否成功读取了预期数量的数据项。
-
例子
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fd = fopen("text.txt", "a+");// 失败if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");// // 定义存储日期的变量int year;int month;int day;// 循环从文件中读取日期数据while (fscanf(fd, "%d:%d:%d", &year, &month, &day) == 3){// 格式化输出读取的日期printf("读取的数据是:%04d:%02d:%02d\n", year, month, day);}// 关闭文件fclose(fd);return 0;
}
//根据指定的格式字符串从文件流中读取数据
结果是
打开成功
读取的数据是:2025:05:06
读取的数据是:2025:05:07
读取的数据是:2025:05:08
读取的数据是:2025:05:09
读取的数据是:2025:11:12
1.4 文件流写入
-
fputs函数
函数原型如下:
int fputs(const char *s, FILE *stream)
- 参数:
- s:指向要写入文件的字符串的指针。
- stream:指向 FILE 对象的指针,该对象标识了要写入的文件流。
- 返回值
- 如果成功,
fputs
返回非负值。 - 如果发生错误,则返回
EOF
。 - 但是,请注意,在大多数情况下,
fputs
不会在遇到错误时立即返回EOF
,除非底层 I/O 操作失败 (例如,磁盘空间不足)。
- 如果成功,
- 参数:
示例
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fd = fopen("text.txt", "w+");// 失败if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");if(fputs("hello world!\n", fd) == EOF){perror("fputs");}printf("%s\n", "写入成功" );// 关闭文件fclose(fd);return 0;
}
EOF是什么类型?
EOF
是一个宏,它在 C 语言中定义在 <stdio.h>
头文件中。EOF
代表**“End Of File”(文件结束**),它是一个整型值(int),通常被定义为 -1。
- fwrite函数
fwrite
函数是 C 语言标准输入输出库(stdio.h
)中的一个函数,用于向文件写入数据块(数组中的元 素)。与 fputs
不同,,fwrite
不关心数据的具体内容(如字符串或格式化文本),而是将内存中的一 块连续区域(通常是数组)直接写入到文件中。这使得rwrite
特别适合于二进制文件的写入 ,但也可以用于文本文件的写入。
使用fwrite
时,你需要指定每个数据项的大小( size
)和要写入的数据项数(nmemb
)( 才能函数正确地计算要写入的总字节数。
函数原型如下:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
-
参数
-
ptr
:指向要写入的数据块的指针。 -
size
:每个数据项的大小,以字节为单位。 -
nmemb
:数据项的个数。 -
stream
:指向FILE
对象的指针,该对象标识了要写入的数据的目标文件流。
-
-
返回值
-
成功时,返回写入的完整项数(即
nmemb
的值,如果所有项都被成功写入)。 -
如果发生错误或到达文件末尾之前写入的项数少于
nmemb
,则可能返回一个小于 -
如果
size 或 nmemb 为 0
,则函数不执行任何操作,并返回 0。
-
示例
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fd = fopen("text.txt", "a+");// 失败if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");int number[5] = {1, 2, 3, 4, 5};// size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);fwrite(number, sizeof(int), sizeof(number)/sizeof(int), fd);// 关闭文件fclose(fd);fd = fopen("text.txt", "r+");if(fd == nullptr){perror("fopen");return -1;}printf("%s\n", "第二次打开成功");fread(number, sizeof(int), sizeof(number)/sizeof(int), fd);for(int i = 0; i < 5; i++){printf("%d ",number[i]);}printf("\n");// 关闭文件fclose(fd);return 0;
}
-
为什么fwrite写入的内容系统使用二进制保存了
write
写入的内容本身是原始的字节序列。系统不会自动在文件末尾添加一个EOF
(文件结束符)作为结尾。实际上,EOF
并不是一个实际存储在文件中的特殊字符,而是在文件读取操作中,由程序或标准库函数生成的一个信号,用于指示文件的结束。- 由于
fwrite
不会对写入的数据进行文本模式的解析和处理(如换行符转换等),因此写入的内容会被系统以二进制形式保存。当文件被读取时,系统会根据读取方式(如文本模式或二进制模式)来解析这些原始字节序列
-
fprintf 函数
fprintf 函数
是 C 语言标准输入输出库(stdio.h)中的一个函数,用于向文件流写入格式化的数据。
函数原型如下:
int fprintf(FILE *stream, const char *format, ...);
- 参数
stream
:指向 FILE 对象的指针,该对象标识了要写入的文件流。
format
:一个格式字符串,指定了后续参数如何被格式化和插入到输出流中。这个字符串可以包含普 通字符(这些字符会被直接复制到输出流中),以及格式说明符(这些说明符被后续参数的值替换)。
...
:表示可变数量的额外参数,这些参数将根据 format
字符串中的格式说明符进行格式化,并插入到输出流中。
- 返回值
成功时,format
字符串中的格式说明符进行格式化(不包括末尾的空字符 ‘\0’)。
如果发生错误,则返回负值。
例子
#include <stdio.h>int main()
{// FILE类型的指针接受fopen的返回植FILE *fp = fopen("text.txt", "a+");// 失败if(fp == nullptr){perror("fopen");return -1;}printf("%s\n", "打开成功");int number = 123;float pi = 3.1415926;const char *text = "sdkklsfs";// 向文件写入格式化数据fprintf(fp,"%d\n", number);fprintf(fp,"%f\n", pi);fprintf(fp,"%s\n", text);fclose(fp);return 0;
}
2.缓冲区操作
2.1 冲洗一个文件流
冲洗流: fflush
主要作用就是同步文件。(把缓冲区中的内容同步中硬件中)
标准IO带缓冲的IO,读写时候针对与缓冲区,并不是每次都是操作硬件
有的时候多线程编程的时候会影响到程序结果。
函数原型
#include <stdio.h>
int fflush(FILE *stream);
描述:
强制给所在的输出流或是更新流(stream)上,写入在用户空间缓冲的所有数据,使用底层写 流的打开状态是不受影响的。 如果stream
是NULL,fflush
刷新所有打开的流。
- 参数
- @stream 需要刷新的文件流指针
- 返回值
- 成功返回0
- 失败返回EOF,同时errno被设置
示例
#include <iostream>
using namespace std;
int main()
{FILE *fp =fopen("text.txt", "r+");if(fp == nullptr){perror("文件打开失败");return -1;}cout << "打开文件成功" << endl;// fputs("12345670123", fp);fwrite("2233", 1,10, fp);fflush(fp);//冲刷指定的文件流,会同步缓存区的内容到硬件getchar();fclose(fp);return 0;}
-
为什么要定义变量或者动态内存分配要使用初始化
-
当你定义变量或分配动态内存时,系统会分配一块内存空间给你。在这之前,这块内存可能存储着之前程序留下的旧数据(脏数据)。
-
如果不初始化,变量或动态分配的内存可能包含未知的垃圾值。这些垃圾值是不可预测的,使用它们可能导致程序行为异常。
-
-
缓冲区和文件同步后,缓冲区的内容通常不会被自动清空。这意味着如果你继续写入数据,新的数据会追加到缓冲区的末尾 ,只有关闭文件或者重新分配缓冲区缓冲区被覆盖或重置
2.2缓冲区策略
标准IO缓冲区有三种类型:
-
行缓冲:缓冲区数据只要达到一行了就会同步到外设(硬盘)上面。假设一行最多80字节,当一行
满了80字节或者遇到\n的时候就会把缓冲区的数据同步到外设上。
-
printf
:行缓冲 -
当一行数据满了就同步。
-
一行没满,但是遇到换行符也同步。
-
当一行没满,也没有换行符,程序在正常退出的时候也会同步。
-
-
全缓冲:只有当缓冲区的数据满了之后,才会同步到外设上面。
-
无缓冲:只要缓冲区有一个字节,就会同步到外设上。
函数原型
#include <stdio.h>void setbuf(FILE *stream, char *buf);void setbuffer(FILE *stream, char *buf, size_t size);void setlinebuf(FILE *stream);// 上面三个函数的功能它都有int setvbuf(FILE *stream, char *buf, int mode, size_t size);//用它就行
描述:
设置缓冲策略的。 **有三种类型的缓冲策略,它们是无缓冲,块缓冲和行缓冲**。当输出流无缓冲时,信息在写 的同时出现于目标文件或终端上;当是块缓冲时,字符被暂存,然后一起写入;当是行缓 冲时,字符被暂存,直到要输出一个新行符,或者从任何与终端设备连接的流中 (典型的 是 `stdin`) 读取输入时才输出。
函数 fflush(3
可以用来强制提前输出。(参见 fclose(3)
) 通常所有文件都是块缓冲的。当文件 I/O 操作在文件上发生时,将调用 malloc(3)
,获 得一个缓冲。 如果流指向一个终端 (通常 stdout
都是这样),那么它是行缓冲的。标准 错误流 stderr
默认总是无缓冲的。
-
参数:
-
@stream 需要设置缓冲区策略的文件流指针
-
@buf 缓冲区指针,如果使用系统缓冲区,那么填null \
注意!!!!!!!!!!!!!!!!!!!!!! buf必须要是在文件流关闭之前,以及程序结束之前它的空间都应该要有效(也就是从文 件流开始到程序结束该段空间都不能被释放)
-
@mode 缓冲策略 参数mode 必须是以下宏值
-
_IONBF 无缓冲
-
_IOLBF 行缓冲
-
_IOFBF 全缓冲
-
-
@size 缓冲区的大小,如果使用系统缓冲区,那么填0
-
-
返回值:
-
成功返回0
-
失败可能返回任意值
-
例子
#include <iostream>
using namespace std;
int main()
{// setvbuf(stdout, buf, _IONBF, sizeof(buf) - 1);//无缓冲printf("sdad6f66sfssss");while(1);//卡住return 0;}
输出
sdad6f66sfssss有多少输出多少
#include <iostream>
using namespace std;
int main()
{char buf[1024] = {0};//设置缓冲区setvbuf(stdout, buf, _IOLBF, sizeof(buf) - 1);//行缓冲printf("sdad6f66sfssss");cout << endl << buf ;while(1);//卡住return 0;}
输出
sdad6f66sfssss
sdad6f66sfssss
3 定位文件流
3.1 设置光标位置
上面有提到fread和fwrite
…只是说明要从哪个文件读写,读写多少个,并没有指明,从哪个位置开 始。标准IO会为每一个打开的文件流,保存一个“文件偏移量”
一般来说,每次读写之前,先确定光标(定位流)。
fseek
就是用来定位文件流的光标的。
- 函数原型
#include <stdio.h>int fseek(FILE *stream, long offset, int whence);
-
作用:
fsee
k就是用来定位文件流的光标的。-
stream:
-
需要进行定位流的,文件流指针
-
offset:
偏移量,结合第三个参数。
-
负:表示向前偏移
-
正:表示向后偏移
-
-
whence:
-
SEEK_SET 基于文件开头定位 新光标的位置=文件开头+offset(>=0)
-
SEEK_CUR 基于当前位置定位 新光标的位置=当前位置+offset(可正可负)
-
SEEK_END 基于文件末尾定位 新光标的位置=文件末尾+offset(可正可负)
-
-
-
返回值:
- 成功返回实际偏移的数量,
- 失败返回-1,同时errno被设置
示例
include <iostream>
using namespace std;
int main()
{FILE *ftpr = fopen("text.txt","r");if(ftpr == nullptr){perror("打开失败");}char buf[1024] = {0};// 光标偏移fseek(ftpr, 12, SEEK_SET);// 读取文件fread(buf, 1 , 100 ,ftpr);cout << "内容:" << endl << buf << endl;return 0;}
3.2 获取光标偏移量
获取此时光标的偏移量是多少(也可以理解为此时已经从文件头移动了多少个字节的数据)
- 函数原型
#include <stdio.h>long ftell(FILE *stream);
- 作用:
ftell
是用来计算当前光标到文件头的距离(有多少个字节)
-
参数
stream
: 需要统计开头到当前位置的字节数的,文件流指针。
-
返回值:
- 成功返回文件头到当前位置的字节数,
- 失败返回-1,同时errno被设置
#include <iostream>
using namespace std;
int main()
{FILE *ftpr = fopen("text.txt","r");if(ftpr == nullptr){perror("打开失败");}char buf[1024] = {0};// 光标偏移fseek(ftpr, 16 , SEEK_SET);// 读取文件fread(buf, 1 , 100 ,ftpr);if(ftell(ftpr) == -1){perror;("ftell");return -1;}cout << "偏移" << ftell(ftpr) << endl;cout << "内容:" << endl << buf << endl;fclose(ftpr);return 0;}
3.3 重置光标位置
- 函数原型
#include <stdio.h>void rewind(FILE *stream);
- 作用:
将光标定位到文件头
stream: 需要将光标定位到文件头,文件流指针
使用该函数等价fseek(stream,0,SEEK_SET)
一样
#include <iostream>
using namespace std;
int main()
{FILE *ftpr = fopen("text.txt","r");if(ftpr == nullptr){perror("打开失败");}char buf[1024] = {0};// 光标偏移fseek(ftpr, 16 , SEEK_SET);// 读取文件fread(buf, 1 , 100 ,ftpr);// 重置光标位置if(ftell(ftpr) == -1){perror;("ftell");return -1;}rewind(ftpr);cout << "偏移" << ftell(ftpr) << endl;cout << "内容:" << endl << buf << endl;fclose(ftpr);return 0;}
4 文件出错/文件结束标志
EOF
:End Of File
文件结束的标志
标准IO库中,在读文件内容时候,如果读取到了末尾,会往缓冲区填入一个EOF
(二进制:1111)
- 函数原型
#include <stdio.h>void clearerr(FILE *stream);
-
描述:
- 清除stream指向的流中文件结束标志和出错标志的
-
参数
- stream: 需要清除文件结束标志和出错标志的stream流
-
函数原型
int feof(FILE *stream);
-
描述:
- 测试指向流中的文件结束标志,如果已设置就返回
- 用于判断文件指针是否已经到达文件末尾(
EOF)
-
参数:stream
- 需要测试文件结束标志的流
-
返回值: return:
- 如果已设置就返回非0
- 否则,返回 0
函数原型
int ferror(FILE *stream);int fileno(FILE *stream);
示例
#include <iostream>
using namespace std;
int main()
{char buf[1] = {0};FILE *ftpr = fopen("text.txt","r");if(ftpr == nullptr){perror("打开失败");}clearerr(ftpr);while(!feof(ftpr)){fread(buf,1 ,1 ,ftpr) ;cout << buf;}cout << endl;fclose(ftpr);return 0;}
5.格式化的输入输出
格式化:按照特定格式进行输入/输出
分为两类参数:
-
格式化字符串
-
告知用户按照哪种格式进行输入/输出,怎么输入/输出。
-
格式化字符串中字符分为三类:
-
非转义字符/普通字符
- A B C … 1 2 3… ! ? , … 必须要按格式化中的样子输入。精准输入
-
格式化字符
-
%d %s %c %f %o … 占位符,表示接收一个指定类型的数据
-
%d :表示接收一个整型数据
-
%s :表示接收一个字符串数据
-
%c :表示接收一个字符数据
-
%f :表示接收一个浮点数
-
%o :表示接收一个整型并以八进制形式打印
-
-
-
转义字符
-
\n \t \a \x89 \0777 … 通过反斜杠转义的不是原含义的字符
-
空白字符:\n \t \a \r …
-
非空白字符:\x89 \0777 …
-
-
-
-
参数
- 要与格式化字符串中占位字符的类型和数量要一致
注意:格式化字符串中的每一个格式化字符,就对应一个地址,所以在输入的时候,参数要与格式化字 符的类型和数量相匹配,然后需要提供地址。
5.1 格式化输入
scanf/sscanf/fscanf...
scanf
什么时候结束输入?
-
scanf
从stdin
的缓冲区中获取输入-
当该输入的都输入完了。
-
scanf("abcd%d %cbcd",&a,&c);
-
abcd123Abcd
-
-
用户输入失败
-
scanf(abcd%d %cbcd",&a,&c);
-
ABCD // scanf就会停止匹配,结束
scanf
返回成功匹配到的变量个数。
-
-
-
fscanf函数
-
函数原型:
#include <stdio.h>int fscanf(FILE *stream, const char *format, ...);
- 作用: 从指定的来源,接受输入数据
- 参数
- stream: 输入源,文件流指针
- format: 格式化字符串
- …: 可变参数
#include <stdio.h>int fscanf(FILE *stream, const char *format, ...);/*
作用:
从指定的来源,接受输入数据
stream:
输入源,文件流指针
format:
格式化字符串
...:
可变参数
*/eg:
FILE *fp = fopen("1.txt"."r");int r a;char c;fscanf(fp,"%d%d%c",&r,&a,&c);
#include <iostream>
using namespace std;
int main()
{FILE *ftpr = fopen("2.txt","r+");if(ftpr == nullptr){perror("打开失败");}char name[20] = {0}; // 声明一个字符数组来存储姓名int age = 0;char sex[10] = {0}; // 声明一个字符数组来存储性别fscanf(ftpr,"name:%s age:%d sex:%s",name, &age, sex);cout << "name:" << name <<"age:" << age << "sex:" << sex << endl;}eg:
FILE *fp = fopen("1.txt"."r");int r a;char c;fscanf(fp,"%d%d%c",&r,&a,&c);
fscanf
读取文件内容的时候是不会主动偏移光标的。
- sscanf函数
#include <stdio.h>int sscanf(const char *str, const char *format, ...);
-
作用:
- 从指定的来源,接受输入数据
-
参数
- str:输入源
- format:格式化字符串
- …: 可变参数
eg:eg:
const char *str = "123456asdsa4564asd";int r a;char c;sscanf(str,"%d%d%c",&r,&a,&c);
5.2 格式化输出
fprintf函数
#include <stdio.h>int fprintf(FILE *stream, const char *format, ...);
eg:
FIEL *fp = open("1.txt","r+");int a = 1,char c = 'a';fprintf(fp,"abc%d123%c\n",a,c):
- 参数
- stream: 需要输出到的文件流指针
- format: 输出格式字符串
- …: 参数
sprintf函数
#include <stdio.h>int sprintf(char *str, const char *format, ...);
-
函数原型
-
str:需要输出到的字符指针。
-
format:输出格式字符串
-
…: 可变 参数**
-
char buf[256]={0};int a = 1;char c = 'a';sprintf(buf,"%d-----%c",a,c);
示例
#include <iostream>
using namespace std;
int main()
{FILE *ftpr = fopen("3.txt","a+");if(ftpr == nullptr){perror("打开失败");}const char *name1 = " lisi";int age1 = 20;const char *sex1 = "woman";fprintf(ftpr,"name:%s:\nage:%d :\nsex:%s:\n", name1, age1, sex1);fclose(ftpr);return 0;}
6.关于C++中的标准库的使用
#include <fstream>ofstream
//文件写操作 内存写入存储设备
ifstream
fstream
//文件读操作,存储设备读区到内存中
//读写操作,对打开的文件可进行读写操作
在fs tream
类中,成员函数 open()
实现打开文件的操作,从而将数据流和文件进行关联,通过 ofstream,ifstream,fstream
对象进行对文件的读写操作
函数:open()public member functionvoid open (const char * filename,ios_base::openmode mode = ios_base::in |
ios_base::out );void open(const wchar_t *_Filename,ios_base::openmode mode= ios_base::in |
ios_base::out,int prot = ios_base::_Openprot);
参数: filename 操作文件名mode 打开文件的方式prot 打开文件的属性 //基本很少用到,在查看资料时,发现有两种方式
ios::in 为输入(读)而打开文件
ios::out 为输出(写)而打开文件
ios::ate 初始位置:文件尾
ios::app 所有输出附加在文件末尾
ios::trunc 如果文件已存在则先删除该文件
ios::binary 二进制方式作业:
**将标准IO文件操作封装成一个类。**