#edu023. 第 4 节 输入与输出

第 4 节 输入与输出

C++ 提供多种输入、输出方式,接下来进行一一介绍。

cin/cout 是 C++ 概念,是流控制输入输出,需要包含头文件 iostream 。 scanf/printfgetchar/putchar 是 C 的标准输入输出函数,需要包含头文件 stdio.h 或 cstdio .

1. 标准输入输出流 cin/cout

cin/cout 是流输入输出函数,需要包含 iostream 头文件,如果需要控制格式,还需要包含 iomanip 头文件。

即:

#include<iostream>
#include<iomanip>

流读取运算符 >>cin 结合在一起使用,可从键盘输入数据。

格式 11cin>>变量; 读入一个值,将值赋给变量。

例如:

int n;
cin>>n;

格式 22cin>>变量1>>变量2 ; 连续读入多个值。

例如:

int a,b,c;
cin>>a>>b>>c;  //等价于 cin>>a; cin>>b; cin>>c;

使用 cin 读入整型,遇到空格、换行会停止读入,当同时读入多个值时,用空格将两个整数分开。

流插入运算符 <<cout 结合在一起使用,可以输出数据。

格式 11cout<<表达式 ; 将表达式的值输出。

例如:

int a=5,b=3;
cout<<a+b;    //输出8

格式 22cout<<表达式1<<表达式2 ; 输出多个表达式的值。

例如:

int a=5,b=3;
cout<<a<<"+"<<b<<"="<<a+b;  //输出5+3=8

双引号 "+" 是字符串,会原样输出。

cout<<'\n';cout<<endl; 都能输出换行符,但cout<<endl 会刷新缓存区降低速度,这种情况下关闭流同步也不能加快输出速度,换行建议都使用 cout<<'\n' .

2. 格式化控制输入输出函数 scanf/printf

scanf/printf 是 C 的格式化控制输入输出函数,要包含头文件 stdio.h 或 cstdio .

scanf 是格式化输入函数,使用格式如下:

scanf("格式控制符",地址列表)

格式控制符指定了读入的数据类型,地址列表是读入变量的内存地址,利用 & 可以获得变量地址,例如 &a 就是变量 a 的内存地址。例如

int a,b;
scanf("%d%d",&a,&b);

%d 指定了读入类型是整型,第一个 %d 输入的值赋给 aa , 第二个 %d 输入的值赋给 bb.

格式控制符是由 % 和字符构成的,不同类型对应不同的格式控制符,常见类型对应的格式控制符如下表:

类型 格式控制符 说明
int %d 整型控制符 ,例如int a; scanf("%d",&a);
long long %lld 长整型控制符,例如 long long a; scanf("%lld",&a);
float %f 单精度浮点数,例如 float x; scanf("%f",&x);
double %lf 双精度浮点数,例如 double x; scanf("%lf",&x);
long double %Lf 长双精度浮点数,例如 long double x; scanf("%Lf",&x);
char %c 单个字符,例如 char ch; scanf("%c",&ch);
字符数组 %s 字符串输入,例如 char ch[21]; scanf("%s",ch);
注意字符数组名就是地址,因此不用取地址

printf 是格式化输出函数,使用格式如下

printf("格式控制符",输出列表);

格式控制符由 % 和特定字符构成,指定了输出的类型。不同的类型对应不同的格式控制字符,如 scanf 表格。

双引号中其他普通字符,会原样输出,用于提示或分隔。

输出时,对于 %d、%f、%s 有不同的控制方式:

整型控制:

%d
参数 说明 实例
%d 以整型输出,占实际宽度 int a=123;printf("a=%d",a);
//输出 a=123
%md 指定输出宽度占m位,不足m位在前面补空格,超过m以实际宽度输出 int a=123;printf("a=%5d",a);
//输出a= 123
%-md 含义同上,在后面补空格,左对齐 int a=123;printf("a=%-5d.",a);
//输出a=123 .
%0md 指定输出宽度占m位,不足m位在前面补0 int a=123;printf("a=%05d",a);
//输出a=00123

实数的格式控制:

由于 %f 对应是单精度,我们以 %lf 为例:

%lf
参数 说明 实例
%lf 按实数格式输出,整数部分按实际位数输出,默认保留6位小数 double p=3.14159265358; printf("p=%lf",p);
//输出p=3.141593
%m.nlf 总位数m(含小数点),其中有n位小数 double p=3.14159265358; printf("p=%5.2lf",p);
//输出p= 3.14
%-m.nlf 同上,左对齐 double p=3.14159265358; printf("p=%-5.2lf.",p);
//输出p=3.14 .

3. getchar/putchar

getchar() 用于读入单个字符,来源头文件 <stdio.h> ,它的返回值是读取的字符的 ASCII 码值。如果读取失败或到达文件末尾,getchar() 会返回常量 EOF(End Of File),其实际值为 -1 .

  1. 通常把输入的字符赋予一个字符变量,构成赋值语句。例如:
 char  ch;
 ch=getchar();
  1. getchar 函数只能接受单个字符,输入数字也按字符处理。
  2. 输入多于一个字符时,只接收第一个字符。
  3. 如果在程序中连续有两个以上getchar()函数,应该一次性输入所需字符,最后再按回车键,否则会把回车作为一个字符传给后面的getchar()函数。

putchar() 函数是字符输出函数,功能是输出单个字符,使用格式:putchar(字符) , 例如:

char ch='a'; 
putchar(ch);

getchar/putchar 只能输入输出单个字符,但是由于速度较快,往往配合循环,进行快读,关于快读部分,可以参考文档“输入输出速度比较” 。

4. 输入输出速度比较

在默认情况下,cin/cout 是极为迟缓的读入/输出方式,而 scanf/printf 比 cin/cout 快得多。

当输入输出数据规模达到 10510^5cin/cout 较慢,就要考虑读写加速。

  1. 方法一:使用 scanf/printf

    头文件包含 , 使用 scanf/printf 进行读写。

  2. 方法二:关闭流同步

    关闭流同步后,cin/coutscanf/printf 速度相当。 在主函数 main() 添加关闭流同步代码,解除 cin,cout 绑定。

    //...
    int main()
    {
    	ios::sync_with_stdio(0);  //关闭流同步
    	cin.tie(0);      //解除绑定
    	//...
    	
    	return 0;
    }
    

    如果搭配上文件读写,主函数核心代码如下:

    //...
    int main()
    {
    	freopen("输入文件名","r",stdin);
    	freopen("输出文件名","w",stdout);
    	ios::sync_with_stdio(0);
    	cin.tie(0);
    	//...
    	
    	return 0;
    }
    

    注意: (1)关闭流同步后,不要和 scanf/getchar C读入方式混用。 (2)也不要使用 endl 换行,使用 “\n” 换行。(3)有文件读写,不能关闭文件。

  3. 方法三:使用 getchar()/putchar() 进行快读快写

    可以自定义读写函数,利用 getchar/putchar 进行快读快写。

    快读:

      int read() {
      int x = 0, w = 1;
      char ch = 0;
      while (ch < '0' || ch > '9') {  // ch 不是数字时
        if (ch == '-') w = -1;        // 判断是否为负
        ch = getchar();               // 继续读入
      }
      while (ch >= '0' && ch <= '9') {  // ch 是数字时
        x = x * 10 + (ch - '0');  // 将新读入的数字「加」在 x 的后面
        // x 是 int 类型,char 类型的 ch 和 '0' 会被自动转为其对应的
        // ASCII 码,相当于将 ch 转化为对应数字
        // 此处也可以使用 (x<<3)+(x<<1) 的写法来代替 x*10
        ch = getchar();  // 继续读入
      }
      return x * w;  // 数字 * 正负号 = 实际数值
    }
    

    读入的时候直接调用函数就可以了,例如读入 num 可写为 num=read();

    利用 putchar() 进行快写。

    void write(int x) {
     if (x < 0) {  // 判负 + 输出负号 + 变原数为正数
       x = -x;
       putchar('-');
     }
     if (x > 9) write(x / 10);  // 递归,将除最后一位外的其他部分放到递归中输出
     putchar(x % 10 + '0');  // 已经输出(递归)完 x 末位前的所有数字,输出末位
    }
    
    

    输出 num 可写为 write(num);