C/C++ 与 Java IO 机制对比解析和流与缓冲的概念介绍
系统级 IO
系统级 IO
是操作系统提供的输入输出接口 , 直接与硬件内核交互 .
不同操作系统的 系统级 IO
不同 .
流
流是 数据传输的抽象管道 , 定义数据如何输入输出 , 有流才能传输数据 . 本质是 封装 系统级 IO
的高级抽象 , 拥有自己的统一接口 ( iostream
, InputStream
等) .
为什么要将系统级 IO
抽象成流 ?
-
设备无关性 : 对于文件 , 网络数据 , 内存等数据 , 流能够提供统一的操作接口 .
-
跨平台兼容 : 流将不同操作系统的系统级
IO
封装起来 , 使操作兼容不同平台 . -
优化 : 流拥有缓冲区 , 类型检查 , 错误处理等优化机制 .
缓冲区
缓冲区 是 内存中的临时数据存储区域 , 用于优化流的性能 , 减少系统调用次数 , 匹配网络和硬件读取访问的不同速度 .
B题 我才是奶龙 : 给你一组R、G、B的字符,判断其中是否恰好只有R、G。
缓冲区认识最深刻的时候是上学期新生赛的
B
题 , 学校机房设备的Dev-C++
编译器没有调整缓冲模式 , 导致我在这题的输入输出浪费了很多时间 .
缓冲模式
缓冲模式 | 触发刷新条件 | 应用 |
---|---|---|
全缓冲 | 缓冲区满 | 文件 IO |
行缓冲 | 遇到换行符或缓冲区满 | 控制台 IO |
无缓冲 | 立即输出 | 我才是奶龙 |
纵向介绍
C scanf & printf
基于 stdio
库函数封装操作系统的系统级 IO
, 速度极快 . 默认使用 stdio
缓冲 : 终端行缓冲 ; 文件全缓冲 . 可通过 setvbuf
修改缓冲模式和自定义缓冲区大小 .
#include<stdio.h> // #include<cstdio>
int main(){
int n;
scanf("%d", &n);
printf("%d", n);
int a[n];
for(int i = 0; i < n; ++i){
scanf("%d", &arr[i]);
}
for(int i = 0; i < n; ++i){
printf("%d", arr[i]);
}
return 0;
}
C++ cin & cout
基于流对象 iostram
, 通过操作符重载实现类型安全 . 默认与 C 风格 stdio
同步共享缓冲区 , 解除同步避免额外计算可以提升输入输出操作效率 .
操作符重载 : 在自定义类或结构体中重新定义语言内置操作符的行为 .
#include<bits/stdc++.h> // #include<iostream>
int main(){
int n;
cin >> n;
cout << n;
int arr[n];
for(int i = 0; i < n; ++i){
cin >> n;
}
for(int temp : arr){
cout << temp;
}
return 0;
}
Java Scanner
类
基于 InputStream
, 通过正则表达式分隔解析输入 . 多次小规模读取的效率较低 . 无法直接修改缓冲区大小 .
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.print(n);
int[] arr = new int[n]; // int arr[n];无效
for(int i = 0; i < n; ++i){
arr[i] = scanner.nextInt();
}
for(int temp : arr){
System.out.print(temp);
}
scanner.close();
}
}
Java
格式化输出
基于 Formatter
的装饰器流 , 语法和 C
语言的 printf
类似 , 拥有类型安全 . 提供格式说明符对齐输出结构 .
装饰器模式 : 允许动态地向对象添加新的功能而无需修改原有类的代码 , 以组合代替继承的方式扩展对象行为 .
import java.util.Scanner;
public class Main {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.printf("%d", n);
String temp = String.format("%d", n); // 支持代码复用
scanner.close();
}
}
Java BufferedReader
类输入
基于 Reader
, 使用缓冲优化减少系统调用次数 , 无类型安全 , 需要手动解析数据类型 . 可以直接在析构函数中直接指定缓冲区大小 .
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bufferedReader.readLine());
String[] input = br.readLine().split(" ");
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(input[i]);
}
bufferedReader.close();
}
}
横向对比
\ | printf/scanf | cin/cout | Scanner | Java格式化输出 | BufferedReader |
---|---|---|---|---|---|
性能 | 极高 直接调用 | 一般 需要关闭同步 | 极低 正则匹配成本高 | 较低 解析格式符成本高 | 较高 批量读取 缓冲优化 |
类型安全 | 无 易出现未定义行为 | 编译时推导类型 | 运行时检查 | 格式化字符串与参数强匹 | 无 手动解析字符串的具体类型 |
缓冲管理 | setvbuf 修改缓冲区大小和缓冲模式 | 可自定义缓冲区 默认共享 | 无 依赖底层流缓冲 | 无 同 Scanner | 不可更改缓冲模式 默认 8196 Kb 缓冲 可调整大小 |
补充
底层流 是直接与数据源交互的原始数据通道 .
Scanner
和 Java
格式化输出 依赖底层流缓冲 , 没有独立的缓冲管理能力 .
// 底层流是System.in 标准输入流 无缓冲
Scanner scanner1 = new Scanner(System.in);
// 底层流是FileInputStream 字节流 无缓冲
FileInputStream fis = new FileInputStream("data.txt");
Scanner scanner2 = new Scanner(fis);
// 底层流是BufferedReader 字符流 已缓冲
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
Scanner scanner3 = new Scanner(br);