C++输入输出函数和底层原理详解

C++:带类的C

也就是说,C语言中的解决方案放到C++中仍然可以使用

一,C语言的输入输出方式:

C语言的标准输入输出函数,需要包含头文件<stdio.h>

在C++中,只需要包含头文件<iostream>,就可以使用C中的输入输出函数

0. stdinstdout

0.1. stdin输入流

stdin输入流,是一个文件描述符(Linux)或者文件句柄(Windows)。其对应着输入区域,通常指键盘设备的输入。在绝大多数时候stdin可以被直接认为是键盘缓冲区。由于Unix的I/O重定向,可以将文件流重定向到任意文件和设备中,所以这里把stdin视为一个对应输入的文件。

stdin行缓冲I/O。在使用键盘键入字符的时候,键入的字符会被放到键盘自身的缓存中(键盘硬件设备中的一部分)。在遇到换行符后,操作系统才会进行同步,将键盘缓存中的字符读入到stdin对应的内存中,叫做输入缓存区。所有从stdin读取数据的函数,都是从电脑内存中的输入缓存区读入数据。

0.2. stdout输出流

stdout输出流。对应输出区域,通常指屏幕设备的输出,被认为是屏幕缓冲区stdout也是行缓冲I/O。用户打印数据时,数据首先从用户程度到stdout所对应的输出缓冲区中。而只有缓存遇到了换行符\n缓存满了缓存被刷新了stdout关闭等情况,数据才会从stdout输出缓冲区到终端显示

0.3. stderr错误输出流

stderr是标准错误流,是无缓冲的,会立即输出到文件或输出设备上。

1. 以键盘键入的输入输出函数

1.1. 输入函数

(1) scanf()函数

语法:int scanf(const char *format, ...);

  • 成功,返回成功匹配和赋值的个数。如果到达文件末尾或发生错误,则返回EOF

**目的:**从标准输入流stdin中读取输入,并按照提供的format来浏览输入

**操作:**打印引号内字符串 ,将格式说明符替换成对应变量

(2) gets()函数

语法:char *gets(char *s);

作用:

  • 从stdin读取一行到s所指向的缓冲区,直到一个终止符\n或EOF

  • 以enter结束,也就是以\n结束

    gets()函数读取到换行符\n停止读取,但并不会将\n读取到字符中,而会在字符串末尾添加\0字符

  • 可以用空格分割字符串

    • 也就是说可以接受空格并输出

存在问题:

`gets()`没有绑定检查,字符数组在声明时,已经设定长度为nums,但,`gets()`函数会一直获得字符,存入字符数组,一直到enter结束,而不是nums次,会照成缓冲区溢出错误

解决方法:

  • 使用fgets()函数避免这种情况——推荐使用这个函数,而不是gets()

    语法:char s[nums];

    fgets(s,nums,stdin)

    • s:数组首地址
    • nums:数组长度
    • stdin:输入流,表示从键盘输入数据
(3) getchar()函数

语法:int getchar(void);

**作用:**从屏幕上读取下一个可用字符,并将其返回为一个整数

**注意:**单次只会读取一个单一的字符

1.2. 输出函数

输出操作一般没有针对空格、制表符的特殊行为,只有换行符\n需要特殊对待。

(1) printf()函数

语法:int scanf(const char *format, ...);

**目的:**把输出写入到标准输出流stdout中,根据提供的format产生输出

**操作:**获取变量名所在的地址,将从标准输入获得的字符串存储在对应该地址中

补充:printf,意思是print formatted,也就是格式化打印。但如目的中所说,printf()的功能其实是把字符串写入到stdout文件缓存,而只有缓存遇到了换行符\n缓存满了缓存被刷新了stdout关闭等情况,才会触发真正的打印输出。

  • 缓存遇到了换行符\n

  • 缓存满了

    缓存满了会自动清空,所以本质上也是缓存被刷新

  • 缓存被刷新了

    • 程序结束时,会自动清空缓冲区
    • 使用fflush()函数,一般这个函数是用来刷新输出(stdout)缓存的
  • stdout被关闭

所以,平常使用的printf()函数,之所以不加\n数据也会被打印的原因是,程序结束时缓冲区刷新,从而输出缓冲区中的数据会发送到显示设备上被打印。

(2) puts()函数——自动增加换行符

语法:int puts(const char *s)

作用:把字符串s和一个尾随换行符写入到stdout,并返回打印的字符数

注意:

  • puts()函数总会在最后追加一个换行符\n
  • puts()函数会将一个字符串写入到stdout中,一直到遇到空字符才结束,但不会将空字符写入。
  • 如果成功,会返回打印的字符数,所以等于打印的字符串长度加一,那个加一就是最后追加的换行符\n
(3) putchar()函数

语法:int putchar(int a);

**作用:**把字符输出到屏幕上,并返回相同的字符

注意:单词只会输出一个单一字符

补充函数:fflush()函数

语法:int fflush(FILE *stream)

  • 成功,返回零值
  • 失败,返回EOF,且设置错误标识符feof

**作用:**强制刷新缓冲区,将缓冲区中的数据立即写到stream指向的文件(或设备)。

  • 其参数可以为stdout,也可以为文件指针,但不可以是stdin,可能会发生未知错误

  • 调用该函数,刷新缓冲区,输出缓冲区中的数据会被传输到设备上打印输出

2. 文件相关的输入输出函数

2.0. 文件操作前言

两种文件类型:

  • 文本文件.txt:纯文本格式存储,最小维护工作,易于阅读,最小安全性,占用更大存储空间
  • 二进制文件.bin:容纳更多数据,不易读取,更高安全性

文件操作:

  1. 声明文件

    File *fp;

  2. 打开/创建——fopen()函数

    作用:创建一个新的文件或者打开一个已有的文件

    语法:FILE *fopen(const char *filename, const char *mode);

    fopen_s

    mode:

    • r:只读,打开一个存在的文本文件,如果文件不存在,fopen()函数返回NULL
    • w:只写文件,如果文件存在,则将文件截断为0 ,从头写,如果文件不存在则创建一个新文件
    • a:追加写文件,如果文件不存在则创建一个新文件
    • r+:读写文件,先读后写,从起始位置上开始写入文件,如果文件不存在,fopen()函数返回NULL
    • w+:读写文件,先写后读,如果文件存在,则将文件截断为0 ,从头写。如果文件不存在则创建一个新文件
    • a+:读写文件。从头开始读,追加的模式写入。如果文件不存在则创建一个新文件
    • 二进制文件:rb, wb, ab, rb+, wb+, ab+

    注意:

    • w模式新建文件,只有路径正确且存在,而文件不存在,才会新建文件夹,也就是说

      1. 路径存在,文件存在:文件截断为零,重新写入
      2. 路径存在,文件不存在:创建一个新文件
      3. 路径不存在,不会新建文件,而是返回NULL
    • r+模式,是读取文件后,从文件指针处开始写入文件

      ww+模式,是直接将文件截断为零,重新写入文件

      aa+模式,则只会从文件末尾开始追加,无论文件指针在什么位置

      也就是说,如果想在文件中插入内容,只有r+模式能够实现,需要使用fseek()函数将文件指针偏移到需要插入的位置

    • rb+r+b是等价的

  3. 关闭文件

    作用:清空缓存区数据,关闭文件,并释放该文件的所有内存

    语法:int fclose(FILE *fp);

    • 成功关闭文件,返回零
    • 关闭文件发生错误,返回EOF
  4. 读取

  5. 写入

2.1. 输入函数

(1)fscanf()函数

**作用:**从文件中读取字符串,遇到第一个空格或者换行符时停止读取

语法:int fscanf(FILE *fp, const char * format, 变量地址)

scanf()函数相比,参数中多了一个fp文件指针,但其余都一样

(2)fgets()函数——推荐使用的输入函数

作用:fp指向的输入流中读取n-1个字符。把读入的字符串复制到缓冲区buf,并在最后追加一个**NULL**字符来终止字符串。

语法:char *fgets(char *buf, int n, FILE *fp);

如果在读取最后一个字符(第n-1个字符)前,就已经遇到\n或者是文件末尾EOF,则只会返回读取到的字符,包括换行符

结束的三个条件:

  • 读取n-1个字符,返回n-1个字符和一个NULL字符
  • 读取到换行符\n,返回所有读取的字符,包括换行符
  • 读取到文章末位EOF,返回所有读取的字符
(3)fgetc()函数

**作用:**从fp指向的输入文件中读取一个字符,

语法:int fgetc(FILE *fp);

  • 成功,返回读取的字符
  • 失败,返回EOF
(4)fread()函数

**作用:**从给定流 stream 读取数据到 ptr 所指向的数组中

语法:

size_t fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *stream);
  • ptr:指向一块大小为size_of_elements * number_of_elements的内存空间,将要读入的数据存放在这块空间中
  • size_of_elements:要读取的每个元素的大小,以字节为单位
  • number_of_elements:元素的个数,每个元素的大小为size_of_element字节
  • stream:指向FILE对象的指针,指定了一个输入流

2.2. 输出函数

(1)fprintf()函数

**作用:**把字符串写入到文件中

语法:int fprintf(FILE *fp, const char * format, ...)

或者,int fprintf(FILE *fp, string str)

(2)fputs()函数

**作用:**把字符串s写入到fp所指向的输入流中

语法:int fputs(const char *s, FILE *fp);

  • 成功,返回一个非负值
  • 失败,返回EOF

这个输入流,可以是文件流,也可以是stdoutstderr等。

(3)fputc()函数

**作用:**把参数c写入到fp指向的输入流中

语法:int fputc(int c, FILE *fp);

  • 成功,返回写入字符
  • 失败,返回EOF
(4)fwrite()函数

**作用:**将ptr所指向的数组中的数据写入到给定流stream

语法:

size_t fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *stream);
  • ptr:指向要被写入的元素数组的指针
  • size_of_elements:要被写入的每个元素的大小,单位为字节
  • number_of_elements:要被写入的元素的个数,每个元素的大小为size_of_elements字节
  • stream:指向FILE对象的指针,指定了一个输出流

二,C++的输入输出

1. <iostream>标准库

定义了cincoutcerrclog对象

1.1. 标准输入流cin

**描述:**是isotream的一个实例,附属到标准输入设备,通常是键盘。全称为std::cin

基础语法:cin >> 变量名 >>[变量2 [>> ...]];

  • >>:流提取运算符

cincout效率优化代码:

std::ios::sync_with_stdio(false);
std::cin.tie(0);

另一种使用方法:接收一个字符串,遇到空格Tab回车都结束

#include<iostream>
using namespace std;
int main(){
    char a[20];
    cin >> a;
    cout << a << '\n';
}
1.1.1 cin.get()函数
  • cin.get(字符变量名)用来接收字符

    #include<iostream>
    using namespace std;
    int main(){
        char ch;
        cin.get(ch)//只能获取一个字符
        //ch = cin.get(); //或者这个
        cout<<ch<<endl;
    }
    
  • cin.get(字符数组名, 接收字符数)用来接收一行字符串,包括空格

    #include<iostream>
    using namespace std;
    int main(){
        char a[20];
        cin.get(a,20); // 类似于getline。可以输入多个单词,中间空格隔开
        cout << a << endl;
    }
    
  • cin.get()用于舍弃输入流中的不需要的字符,或者舍弃回车,用来弥补cin.get(字符组名, 接收字符数)的不足

    #include<iostream>
    using namespace std;
    int main(){
        char a[20];
        cin.get(a,20);
        cin.get();
        cout << a << endl;
        cin.get(a,10);
        cout << a << endl;
        system("pause");
        return 0;
    }
    
1.1.2 cin.getline()函数

语法cin.getline(接收字符数组, 接收个数, 结束字符)

作用:接收一个字符串,包括空格

#include<iostream>
using namespace std;
int main(){
    char m[20];
    cin.getline(m, 5);
    cout << m << endl;
}

最后一位为\0,所以只能看到4个字符输出

2.2. 标准输出流cout

**描述:**是iostream类的一个实例,是行缓冲I/O,连接到标准输出设备,通常是显示屏。全称为std::cout

语法:cout << 变量1 [<< 变量2 [<< endl]];

  • <<:流插入运算符
  • endl:end line,输出时插入换行符并刷新流

通常认为使用cout对象进行输出要比printf进行输出慢的多,但事实却不一定如此,参考博客C++的cin/cout为什么比C语言的scanf/printf慢,在经过上述2.1.的优化后,将stdincin解绑后,其实二者速度已经差不多。
其次,根据网上一些网友的实验,endl也是拖累cout速度的一大元凶,因为endl会在添加换行符的同时刷新输出缓冲区,不断地刷新是cout效率低下的一个重要原因。(未验证)

2.3. 标准错误流cerr

描述:也叫非缓冲标准错误流,是 iostream 类的一个实例,附属到标准输出设备,通常是显示屏。顾名思义,cerr对象是非缓冲的,也就是说,每个流插入到cerr都会立刻发送到标准输出设备输出。

语法:cerr << 变量1 [<< 变量2 [<< endl]];

2.4. 标准日志流clog

描述:也叫缓冲标准错误流,是 iostream 类的一个实例,附属到标准输出设备,通常是显示屏。

语法:clog << 变量1 [<< 变量2 [<< endl]];

2.<string>标准库

1.1 getline()函数

作用:接受一个字符串,可以接受空格并输出

#include<iostream>
#include<string>
using namespace std;
int main(){
    string str;
    getline(cin, str);
    cout << str << endl;
}

类似于cin.getline()

1.2 gets()

继承C的gets()

1.3 getchar()

继承C的getchar()

3. <fstream>标准库

文件操作:

三个数据类型:

数据类型描述
ofstream该数据类型表示输出文件流,用于创建文件并向文件写入信息。
ifstream该数据类型表示输入文件流,用于从文件读取信息。
fstream该数据类型通常表示文件流,且同时具有 ofstreamifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。
  1. 打开文件—— open()

    语法:void open(const char *filename, ios::openmode mode);

    • filename:指定要打开的文件的位置和名称
    • mode:定义文件被打开的模式

    mode:

    • ios::app:追加模式
    • ios::ate:文件打开后定位到文件末尾
    • ios::in:打开文件用于读取
    • ios::out:打开文件用于写入
    • ios::trunc:如果该文件已经存在,则将文件截断为0
  2. 关闭文件

    语法:void close();

    作用:关闭打开的文件流

    在C++程序结束时,会自动关闭刷新所有流,释放所有分配的内存,并关闭所有打开的文件

  3. 写入文件——输出操作

    cout一样,使用流插入运算符<<向文件写入信息,只不过这里使用的对象是ofstream对象和fstream对象

  4. 读取文件——输入操作

    cin一样,使用流提取运算符>>从文件读取信息,只不过这里使用的对象是ifstream对象和fstream对象

  5. 文件位置指针

    seekg:seek get,istream的成员函数

    seekp:seek put,ostream的成员函数

    作用:重新定位文件位置指针

    语法:(以"get"为例)

    seekg(long int n, voctor)

    • n:表示偏移字节量
    • voctor:查找方向
      • ios::beg:流的开头
      • ios::cur:文件指针的当前位置
      • ios::end:流的末尾

    比如:

    fileObject.seekg( n, ios::cur );
    

    把文件指针从指针所在位置向后偏移n个字节

4. <iomanip>

控制符作用
setbase(n)以n进制方式输出(n=8,10,16)
setfill(ch)设置字符填充,ch可以是字符常量或字符变量
setprecision(n)设置输出有效位数为n位
setw(n)设置字符宽度为n位,只对后一个有影响
setiosflags(ios::uppercase)以大写字母显示
setiosflags(ios::fixed)实现对小数点后的数字的控制
setiosflags(ios::scientific)以科学计数法显示
setiosflags(ios::showpoint)强制显示小数点
setiosflags(ios::showpos)强制显示正号
setiosflags(ios::left)设置输出左对齐
setiosflags(ios::right)设置输出右对齐
resetiosflags(…)终止括号中的输出格式

补充,这篇文章是广泛阅读了网上关于输入输出流、C语言输入输出、C++输入输出函数各种详解后自己写的笔记,忘了记录学习的网页网址了…只能说十分抱歉,以后会注意的

三 附录

常用的格式说明符号

转换说明输出
%d有符号 十进制 整数 int
%f十进制 浮点数 float
%lf十进制 浮点数 double
%c字符 char
%s字符串 char*
  • %d:int
  • %hd:short int
  • %li:long int
  • %lli:long long int
  • %u:unsigned int
  • %lu:unsigned long int
  • %llu:unsigned long long int
  • %f:float,十进制记数法
  • %lf:double
  • %Lf:long double
  • %c:char
  • %s:string
  • %o:unsigned int 八进制
  • %[x|X]:unsigned int 十六进制整数 0[f|F]
  • %[a|A]:浮点数、十六进制、[p|P]-记数法
  • %[e|E]:浮点数、[e|E]-记数法
  • %[g|G]:根据数字自动选择%f%[e|E]
    • 指数小于-4,或大于等于精度时:选择%[e|E]

如果获取字符时使用的格式说明符为%c,而打印字符时使用的格式说明符为%d,则会打印获取字符的ASCII码

printf()的常用修饰符

位置:在%与字母之间的位置,比如%-f

  • digit(s):字段宽度的最小值

  • .digit(s):精度

    • 对于%f, %e, %E,表示小数点右边打印的数字的位数
    • 对于%g, %G,表示有效数字的最大位数
    • 对于%s,打印字符的最大位数
    • 对于d,要打印数字的最小位数
    • 一般使用前导零填充字段宽度
    • .等价于.0
  • -:左对齐

  • +:显示正负号

  • :如果有符号的值为正,则会显示前导空格;为负则显示负号

  • #

  • 0:对于所有数字格式,用前导零填充字段宽度

    • 优先级低于-和指定精度

概念词汇

EOF:End Of File缩写,文件结束的标志,通常值为-1

fgetc()fputc()函数中,如果操作失败则返回EOF,则,这个时候的EOF代表Error Of File

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值