第23讲:C风格输入输出

《计算机程序设计》

苏醒

计算机学院计算机研究所编译系统研究室

课前准备

  • 在WSL Ubuntu中建立一个专门的目录用于课上练习,如~/course
$ mkdir ~/course
  • 打开VSCode并连接到WSL,打开此目录并新建文件,如~/course/main.cpp
  • 编辑main.cpp,随堂编程练习的代码请直接在此文件中编辑
main.cpp
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
  // ============= begin =============

  // =============  end  =============
  return 0;
}

温故:格式化标志位与IO操纵子

IO操纵子与相关的流状态/方法
操纵子 相关标志位/方法 描述
dec, oct, hex dec, oct, hex 设置数字进制
scientific, fixed, hexfloat, defaultfloat scientific, fixed 设置浮点数输出格式
left, right, internal left, right, internal 设置输出对齐方式
boolalpha, noboolalpha boolalpha 设置布尔数输入输出格式
showbase, noshowbase showbase 显示整数的进制前缀
showpoint, noshowpoint showpoint 显示浮点数的小数点
showpos, nonshowpos showpos 显示正数符号
skipws, noskipws skipws 跳过空白字符
uppercase, nouppercase uppercase 使用大写字母表示十六进制前缀
setw() width() 设置输出宽度
setfill() fill() 设置填充字符
setprecision() precision() 设置浮点数输出精度

学习目标

  • 学习内容
    • C-Style格式化输出
    • C-Style格式化输入

学习目标

  • 了解C-Style格式化IO函数族
  • 掌握C-Style格式化IO的基本用法

考试中如果需要使用C风格IO,不要抓瞎

一、格式化输出

1.1 C-style格式化输出函数族

  • C++支持C-style格式化输出函数族
    • 包含头文件<cstdio>
C风格格式化输出函数族
函数签名 描述
int printf( const char* fmt, ... ); 输出到标准输出
int fprintf( std::FILE* stream, const char* fmt, ... ); 输出到文件
int sprintf( char* buf, const char* fmt, ... ); 输出到字符串

明察秋毫

  • 可变参数列表是C/C++中一个重要的特性,允许函数接受不定数量的参数,在函数声明中使用省略号...表示
  • 以上函数的返回值都是输出字符的个数(当发生错误时返回负值)

1.1 printf函数

int printf(const char *format, ...);
  • printf函数的功能是将数据输出到标准输出,第一个参数是一个格式化字符串,后跟一个可变参数列表
    • 格式化字符串中,%标记一个格式指示符的开始
    • 格式指示符与可变参数列表中的实参一一对应(按顺序匹配)
    • 格式指示符用于控制对应实参的输出格式
#include <cstdio>
int main() {
  int a = 0;
  double b = 1.0;
  char c = '2';
  char *s = "hello world";
  printf("print an int %d, a double %f, a char %c, and a string %s\n",
         a, b, c, d); // a对应%d,b对应%f,c对应%c,s对应%s
}

1.2 格式指示符

格式指示符的构成
%[flags][width][.precision][length-modifier]conversion
  • 格式指示符由以下要素组成
    • %:格式指示符的开始
    • flags:标志位,0个或多个
    • width:最小输出宽度
    • .precision:精度
    • length-modifier:长度修饰符,0个或多个
    • conversion:转换说明符,1个
  • 其中,conversion是必须的,其他要素是可选的

1.2 格式指示符

格式指示符的构成
%[flags][width][.precision][length-modifier]conversion
  • 转换说明符(conversion)用于指定输出数据的类型
转换说明符 描述 转换说明符 描述
c 字符 s 字符串
p 指针 % 百分号
d, i 有符号十进制整数 o, u, x, X 无符号八进制、十进制、十六进制整数
f, F 小数格式十进制浮点数 e, E 科学记数法十进制浮点数
g, G 自动格式十进制浮点数 a, A 十六进制浮点数
  • 长度修饰符(length-modifier)用于指定输出类型的长度信息
长度修饰符 描述 长度修饰符 描述
hh 单字节整数 h short整数
l long整数 ll long long整数
L long double

1.2 格式指示符

格式指示符的构成
%[flags][width][.precision][length-modifier]conversion
  • 最小输出宽度(width)用于指定输出数据的最小宽度
    • 作用类似于setw流操纵子
  • 标志位用于控制输出格式
标志位 描述
- 左对齐,默认为右对齐
+ 始终显示数的符号,即输出正数时添加正号
(一个空格) 正数前添加空格
0 用0填充空白,默认为空格

1.2 格式指示符

格式指示符的构成
%[flags][width][.precision][length-modifier]conversion
  • 精度(.precision)用于指定输出浮点数的精度
    • 当使用f/Fe/E转换说明符时,精度表示小数点后数字的个数
    • 当使用g/G转换说明符时,精度表示总有效数字的个数

考考你

这和setprecision流操纵子的作用是否相同?

1.2 格式指示符

格式指示符的构成
%[flags][width][.precision][length-modifier]conversion
printf-usage.cpp
#include <cstdio>
static const double PI = 3.14159265358979323846;
int main() {
  printf("Hello, World!\n"); // 打印一个字符串,不包含格式指示符
  printf("%d, %+4d, %-4d, %+04d\n", 42, 42, 42, 42);   // 打印整数
  printf("%f, %e, %-10.4f, %+-.4e\n", PI, PI, PI, PI); // 打印浮点数
  printf("There is two '%c's in '%s'\n", 'l', "Hello");// 打印字符和字符串
  return 0;
}
$ g++ -o code/printf-usage code/printf-usage.cpp
$ ./code/printf-usage
Hello, World!
42,  +42, 42  , +042
3.141593, 3.141593e+00, 3.1416    , +3.1416e+00
There is two 'l's in 'Hello'

提示

只需要记住几个基本的转换说明符即可,考试题目中会有相应说明

1.3 fprintf函数

int fprintf(FILE *f, const char *format, ...);
  • fprintf函数的功能是将数据输出到文件
    • 相较于printf函数,fprintf函数多了一个参数FILE *f
    • 其余参数的用法与printf函数相同
向文件中输出
#include <cstdio>
int main() {
  FILE *f = fopen("output.txt", "w"); // 第二参数表示打开模式,"r"表示读,"w"表示写,"b"表示二进制模式
  if (f == nullptr) {
    printf("Error opening file\n");
    return 1;
  }
  int a = 0;
  double b = 1.0;
  char c = '2';
  char *s = "hello world";
  fprintf(f, "print an int %d, a double %f, a char %c, and a string %s\n",
          a, b, c, d);
  fclose(f); // 关闭文件
}

1.4 sprintf函数

int sprintf(char *str, const char *format, ...);
  • sprintf函数的功能是将数据输出到字符串
    • 相较于printf函数,sprintf函数多了一个参数char *str
    • 其余参数的用法与printf函数相同
sprintf-usage.cpp
#include <cstdio>
int main() {
  char str[100];
  int a = 0;
  double b = 1.0;
  int count = 0;
  count += sprintf(str, "print an int %d, a double %f\n", a, b); // sprintf返回值是写入的字符数
  count += sprintf(str + count, "print an int %d, a double %f\n", a, b);
  printf("%s", str); // 输出字符串,由用户保证目标缓存的空间足够大,否则可能发生溢出
}
$ g++ -o code/sprintf-usage code/sprintf-usage.cpp
$ ./code/sprintf-usage
print an int 0, a double 1.000000
print an int 0, a double 1.000000

二、格式化输入

2.1 scanf函数

int scanf(const char *format, ...);
  • scanf函数的功能是从标准输入中读取数据
    • 第一个参数是一个格式化字符串,后跟一个可变参数列表
    • 格式化字符串中,%标记一个格式指示符的开始
    • 格式指示符与可变参数列表中的实参一一对应(按顺序匹配)
    • 格式指示符用于控制对应实参的输入格式

明察秋毫

scanf函数中的格式指示符与printf函数中的格式指示符用法相同

2.1 scanf函数

int scanf(const char *format, ...);
scanf函数使用示例
#include <cstdio>
int main() {
  int a = 0;
  double b = 0;
  char c = 0;
  char s[100] = {0};
  scanf("%d %e %3c %s", &a, &b, &c, s);
}

考考你

  • 格式指示符%3c表示什么?
  • 为什么可变参数列表中的变量都要取地址?
  • 参数s为什么不取地址?

2.2 fscanf函数

int fscanf(FILE *f, const char *format, ...);
  • fscanf函数的功能是从文件中读取数据
    • 相较于scanf函数,fscanf函数多了一个参数FILE *f
    • 其余参数的用法与scanf函数相同
从文件中读入
#include <cstdio>
int main() {
  FILE *f = fopen("input.txt", "r");
  if (f == nullptr) {
    printf("Error opening file\n");
    return 1;
  }
  int a = 0;
  double b = 0;
  char c = 0;
  char s[100] = {0};
  fscanf(f, "%d %e %3c %s", &a, &b, &c, s);
  fclose(f);
  return 0;
}

2.3 sscanf函数

int sscanf(const char *str, const char *format, ...);
  • sscanf函数的功能是从字符串中读取数据
    • 相较于scanf函数,sscanf函数多了一个参数const char *str
    • 其余参数的用法与scanf函数相同
sscanf函数使用示例
#include <cstdio>
int main() {
  int a = 0;
  double b = 0;
  char c = 0;
  char s[100] = {0};
  sscanf("123 3.14 ABC hello", "%d %e %3c %s", &a, &b, &c, s);
  return 0;
}

总结

本节内容

---
config:
  look: handDrawn
  themeVariables:
    fontSize: 24px
---
mindmap
  C风格IO
    格式化输出
      printf
      fprintf
      sprintf
      格式指示符
    格式化输入
      scanf
      fscanf
      sscanf

学习目标

  • 了解C-Style格式化IO函数族
  • 掌握C-Style格式化IO的基本用法

课后作业

  • 自学
    • 教材9.3节:C语言的输入与输出函数

计算机程序设计