当前位置: 首页 > news >正文

突破编程_C++_基础教程(输入、输出与文件)

1 流和缓冲区

C++中,流( stream )和缓冲区( buffer )是两个紧密相关的概念,它们在处理输入和输出时起着重要的作用。
流( Stream )
流是一种抽象的概念,用于表示数据的流动。在 C++ 中,流是一个类对象,它封装了与输入/输出设备(如键盘、显示器、文件等)的交互。C++标准库提供了多种流类,如 std::cin (标准输入流,通常用于从键盘读取数据)、 std::cout (标准输出流,通常用于向显示器输出数据)以及 std::fstream (文件流,用于文件的读写)。
缓冲区( Buffer )
缓冲区是一个用于临时存储数据的内存区域。当使用流进行输入或输出时,数据通常不会直接发送到设备或从设备读取,而是首先存储在缓冲区中。缓冲区的使用可以提高输入/输出的效率,因为它允许程序以块的形式处理数据,而不是一个字符一个字符地处理。
对于输出流,程序首先将数据写入缓冲区,然后当缓冲区满或程序显式地刷新缓冲区时,数据才会被发送到输出设备。对于输入流,程序从缓冲区读取数据,当缓冲区为空时,流会从输入设备读取更多的数据填充缓冲区。
缓冲区的类型
C++中的流缓冲区主要有三种类型:
(1)全缓冲:当缓冲区满时,缓冲区的数据才会被发送。这种方式适用于大量数据的输入/输出。
(2)行缓冲:当遇到换行符时,缓冲区的数据才会被发送。这种方式通常用于终端设备,如键盘和显示器。
(3)不带缓冲:数据立即被发送,不经过缓冲区。这种方式通常用于错误报告和紧急情况。
iostream 文件
C++ 中的 iostream 文件中包含了一些用来实现管理流和缓冲区的类。iostream 中的 io 是 “input/output” 的缩写,而 stream 表示流,即数据流动的通道。
在 iostream 库中,主要包含了以下几个组件:
(1)缓冲区( streambuf 类): streambuf 类为流提供了一个缓冲区,这个缓冲区用于暂存输入/输出的数据,从而允许更高效的I/O操作。streambuf 是一个抽象基类,不能直接实例化。它的实现(如 filebuf 、 stringbuf 等)会作为其他流类(如 ifstream 、 ofstream 、 istringstream 、 ostringstream 等)的成员被使用。
(2)输入流( istream 类):用于从输入设备(如键盘)读取数据。
(3)输出流( ostream 类):用于向输出设备(如显示器)写入数据。
(4)输入输出流 ( iostream 类):从 istream 和 ostream 继承了输入和输出的方法(多重继承),既可以读取数据也可以写入数据。
(5)流对象:如 std::cin(标准输入流,通常用于从键盘读取数据)、std::cout(标准输出流,通常用于向显示器输出数据)、std::cerr(用于输出错误消息)和 std::clog(用于输出日志消息)。
(6)操纵器( manipulators):这些是用来控制流的状态或格式的函数,例如 std::endl、std::flush、std::setprecision 等。
(7)流提取运算符( >> ):用于从输入流中读取数据。
(8)流插入运算符( << ):用于向输出流中写入数据。
重定向
可以使用 C 标准库函数 freopen 函数来重定向标准输入流 std::cin 或标准输出流 std::cout 到文件或其他流。当重定向一个流时,实际上是改变了流与底层设备(如文件或控制台)的关联。例如,可以将 std::cin 重定向到一个文件,这样当从 std::cin 读取时,实际上是从文件中读取数据。如下为样例代码:

#include <iostream>  
#include <fstream>  using namespace std;int main() {// 重定向 std::cin 到一个输入文件:在这个文件中输入 1 ,并保存freopen("input.txt", "r", stdin);// 重定向 std::cout 到一个输出文件  freopen("output.txt", "w", stdout);int val;cout << "get a number from input.txt : "; // cout 写入到 output.txt cin >> val; // 实际上从 input.txt 读取  cout << val << endl; // cout 写入到 output.txt  // 关闭重定向  //freopen(NULL, "r", stdin);  // 重置 std::cin 到标准输入  //freopen(NULL, "w", stdout); // 重置 std::cout 到标准输出  return 0;
}

执行完这段代码后,output.txt 被存入字符串:“get a number from input.txt : 1” 。
在上面代码中,freopen 函数将 std::cin 重定向到名为 input.txt 的文件用于读取,将 std::cout 重定向到名为 output.txt 的文件用于写入。因此,当从 std::cin 读取时,实际上是在读取 input.txt 文件的内容,而当向 std::cout 写入时,数据会被写入到 output.txt 文件中。
注意,重定向流可能会影响程序的其他部分,因为它改变了流的默认行为。因此,在重定向流之后,务必小心处理输入和输出,以避免意外的行为或错误。
另外,如果想要在执行完一些重定向操作之后恢复流的原始状态,可以使用 freopen 函数将流重新关联到其原始设备(比如将第一个参数设置为 NULL 即可将输入与输出定向为标准输入与输出)。

2 输出

输出流( output stream )是 I/O 流库中的一个关键概念,它允许程序将数据发送到特定的目的地,如控制台、文件或其他类型的设备。C++ 标准库中的 ostream 类是输出流的基础。

2.1 std::cout 的基本使用

std::cout 是 C++ 中最常用的输出流对象,它通常与控制台(或终端)相关联,用于向用户显示信息。可以使用插入运算符 << 向 std::cout 发送数据,该运算符将数据发送到输出流中。
。如下为样例代码:

#include <iostream>  
#include <string>  using namespace std;int main() {int val1 = 1;double val2 = 1.21;char val3 = 'a';string val4 = "hello";std::cout << "int val1 = " << val1 << std::endl;std::cout << "double val2 = " << val2 << std::endl;std::cout << "char val3 = " << val3 << std::endl;std::cout << "string val4 = " << val4 << std::endl;return 0;
}

上面代码的输出为:

int val1 = 1
double val2 = 1.21
char val3 = a
string val4 = hello

除了 std::cout ,C++ 标准库还提供了其他一些输出流对象,例如:
std::cerr:用于输出错误消息。与 std::cout 不同的是,std::cerr 通常不会被缓冲,因此它用于需要立即显示的错误或诊断信息。
std::clog:用于输出日志消息。与 std::cerr 类似,但它通常会被缓冲。
std::wcout:与 std::cout 类似,但用于输出宽字符(wchar_t 类型)。
std::wcerr 和 std::wclog:与 std::cerr 和 std::clog 类似,但用于输出宽字符。
此外,还可以创建自定义的输出流,通过继承 std::ostream 类并重载插入运算符来实现。这允许控制数据如何被发送到特定的目的地,例如写入到特定的文件或进行特定的格式化处理。
在处理输出流时,还可以使用操纵器( manipulators )来更改流的状态或格式。例如,std::endl 操纵器不仅插入一个新行,还刷新输出缓冲区,确保所有数据都被发送到其目标。 std::flush 操纵器也可以用来刷新输出缓冲区。

2.2 std::cout 的格式化

std::cout 可以使用各种格式化标志和操纵器(manipulators)来控制输出的格式。以下是一些常见的格式化选项:
(1)整数输出
(1.1)十进制

int val = 12;
std::cout << val << std::endl;// 输出 12

(1.2)十六进制

int val = 12;
std::cout << std::hex << val << std::endl;// 输出 c

(1.3)八进制

int val = 12;
std::cout << std::oct << val << std::endl;// 输出 14

(1.4)二进制
std::cout 默认不直接支持二进制数据的输出。如果想要输出一个整数的二进制表示,则要手动将整数转换为二进制字符串,然后再使用 std::cout 输出这个字符串。

#include <iostream>  
#include <bitset>  void printBinary(int val) {// std::bitset 可以将整数转换为二进制字符串  std::bitset<32> binaryVal(val); // 4 个字节,共 32 位  // 输出二进制字符串  std::cout << binaryVal << std::endl;
}int main() {int val = 12;printBinary(val); // 输出 num 的二进制表示  return 0;
}

上面代码输出为:

00000000000000000000000000001100

(2)浮点数输出
(2.1)固定点表示法
默认情况下,std::cout 使用科学记数法来输出非常大或非常小的浮点数。使用 std::fixed 使浮点数总是以固定的小数位数显示。

double pi = 3.141592653589793;// 不使用 std::fixed,默认输出可能使用科学记数法  
std::cout << "default output: " << pi << std::endl;// 使用 std::fixed 以小数位数显示  
std::cout << std::fixed << "fixed output: " << pi << std::endl;

(2.2)固定点表示法
默认情况下,std::cout 使用科学记数法来输出非常大或非常小的浮点数。使用 std::fixed 使浮点数总是以固定的小数位数显示。

double pi = 3.141592653589793;
std::cout << std::scientific << pi << std::endl;// 输出 3.141593e+00

(2.2)设置精度
设置精度使用接口 std::setprecision ,注意该接口需要引用头文件: #include

double pi = 3.141592653589793;
std::cout << std::setprecision(3) << pi << std::endl;// 输出 3.14

(3)字符串输出

std::cout << "Hello, World!" << std::endl;// 输出 Hello, World!std::string str = "Hello, World!";  
std::cout << str << std::endl;// 输出 Hello, World!

(4)布尔值输出
布尔值默认以整数形式显示( true为 1 , false 为 0 )。可以通过 std::boolalpha 来改变这一点,使 true 和 false 以文字形式显示。

bool flag = true;  
std::cout << flag << std::endl; // 输出 1std::cout << std::boolalpha << flag << std::endl;// 输出 true

(5)设置填充和宽度
使用 std::setw 操纵器可以设置下一个输出字段的宽度。如果输出数据小于这个宽度,std::cout 会在数据前面或后面填充空格,直到达到指定的宽度。此外还可以使用 std::setfill 操纵器来设置填充字符。

int val = 123;
std::cout << std::setw(5) << std::setfill('0') << val << std::endl;  // 输出 00123

(6)设置左对齐和右对齐
使用std::left或std::right来控制输出的对齐方式。

#include <iostream>  
#include <iomanip>int main() {std::cout << std::left << std::setw(10) << "Hello" << std::endl; // 左对齐  std::cout << std::right << std::setw(10) << "Hello" << std::endl; // 右对齐return 0;
} 

上面代码输出为:

HelloHello

2.3 多线程中应用 std::cout

C++ 中,std::cout 并不是线程安全的。这意味着如果从多个线程同时写入 std::cout,可能会遇到数据竞争(data race)的问题,这会导致未定义的行为(程序可能会崩溃或者无响应)。
可以使用互斥锁(如 std::mutex )来保护对 std::cout 的访问。每个线程在写入 std::cout 之前必须获取锁,并在写入完成后释放锁。这样可以确保每次只有一个线程可以访问 std::cout。如下为样例代码:

#include <iostream>  
#include <thread>  
#include <mutex>  std::mutex g_coutMutex;  // 全局互斥锁  void safePrint(const std::string& message) {std::lock_guard<std::mutex> lock(g_coutMutex);	// std::lock_guard是一个方便的RAII包装器,它会在构造时锁定互斥锁,并在析构时解锁。这使得代码更加简洁,并减少了出错的可能性。std::cout << message << std::endl;
}int main() {std::thread t1(safePrint, "Hello from thread 1");std::thread t2(safePrint, "Hello from thread 2");t1.join();t2.join();return 0;
}

2.4 printf

printf 是 C 语言标准库中的一个函数,用于格式化输出到标准输出流(通常是终端或控制台窗口)。它接受一个格式字符串和与之对应的值作为参数,然后根据格式字符串中的说明符将值打印出来。 printf 函数是线程安全的,所以在 C++ 的多线程开发中,比 std::cout 用的更方便一些。
** printf 常用的格式说明符及其含义:**
%d 或 %i:带符号十进制整数。
%u:无符号十进制整数。
%f:浮点数(默认保留小数点后六位)。
%c:字符。
%s:字符串。
%p:指针地址。
%x 或 %X:无符号十六进制整数(小写或大写)。
%o:无符号八进制整数。
%%:输出一个 % 字符。
除了转换说明符,格式字符串还可以包含以下可选的标志、宽度、精度和长度修饰符:
标志:
-:左对齐输出。
+:在正数前面显示符号。
(空格):在正数前面显示空格。
#:对于 f、e、E、g、G,输出小数点;对于 o,输出前导零;对于 x 或 X,输出 0x 或 0X 前缀。
0:用零填充空白处。
精度:
对于整数(d、i、o、u、x、X),指定最小数字个数。
对于浮点数(e、E、f、g、G),指定小数点后的数字个数。
对于字符串(s),指定最大字符数。
长度修饰符:
h:指定短整型(short)或单字符(char)。
l:指定长整型(long)。
ll:指定长长整型(long long)。
L:指定宽字符或宽字符串。
j:指定 intmax_t 类型。
z:指定 size_t 类型。
t:指定 ptrdiff_t 类型。

3 输入

C++ 的输入流(Input Stream)通常指的是从某个数据源(如键盘、文件等)读取数据的流。C++标准库中的 头文件提供了基本的输入流功能,主要通过 std::istream 类及其派生类 std::cin 来实现。
std::cin是预定义的对象,代表从标准输入(通常是键盘)接收数据的输入流。可以使用 >> 运算符从std::cin读取数据,这些数据会被存储在相应的变量中。
如下为样例代码(使用 std::cin 从键盘读取数据):

#include <iostream>  int main() {  int val;  std::cout << "input an integer : ";  std::cin >> val; 	// 从标准输入读取整数并存储在变量 val 中  std::cout << "the input integer is: " << val << std::endl;  return 0;  
}

4 文件操作

C++ 的文件操作主要涉及文件的打开、关闭、读取和写入。C++标准库中的 头文件提供了用于文件操作的类,其中 std::ifstream 用于读取文件, std::ofstream 用于写入文件,而 std::fstream 则既可以读取也可以写入文件。
打开文件
在读取或写入文件之前,需要使用相应的文件流对象打开一个文件。可以通过提供文件名来构造一个文件流对象,该对象会在构造时尝试打开文件。如下为样例代码:

#include <fstream>  
#include <iostream>  int main() {// 创建一个用于写入的文件流对象  std::ofstream outfile("test.txt");// 检查文件是否成功打开  if (!outfile) {std::cerr << "failed to open the file" << std::endl;return 1;}// 写入一些数据到文件  outfile << "hello" << std::endl;// 关闭文件  outfile.close();return 0;
}

读取文件
使用 std::ifstream 可以读取文件的内容。可以使用流提取运算符 >> 或 getline 函数来读取数据。如下为样例代码:

#include <fstream>  
#include <iostream>  
#include <string> int main() {// 创建一个用于读取的文件流对象  std::ifstream infile("test.txt");// 检查文件是否成功打开  if (!infile) {std::cerr << "failed to open the file" << std::endl;return 1;}// 读取文件内容  std::string line;while (std::getline(infile, line)) {std::cout << line << std::endl;}// 关闭文件  infile.close();return 0;
}

快速读取大文件
如果需要快速读取一个大文件,则要避免逐行读取或者逐字符读取带来的额外开销。一种快速读取文件的方法是使用文件流的 read 成员函数,它可以一次读取多个字符,这样可以减少系统调用的次数,从而提高读取效率。如下为样例代码:

#include <fstream>  
#include <iostream>  
#include <vector>  int main() {// 打开文件  std::ifstream file("large_file.bin", std::ios::binary);// 检查文件是否成功打开  if (!file){std::cerr << "failed to open the file" << std::endl;return 1;}// 获取文件大小  file.seekg(0, std::ios::end);std::streamsize fileSize = file.tellg();file.seekg(0, std::ios::beg);// 分配足够的内存来存储文件内容  std::vector<char> buffer(fileSize);// 读取文件内容到buffer  if (!file.read(buffer.data(), fileSize)) {std::cerr << "读取文件失败" << std::endl;return 1;}// 在这里处理文件内容,例如可以将其输出到标准输出  std::cout.write(buffer.data(), fileSize);// 关闭文件  file.close();return 0;
}

读写文件
下面是一个同时包含读取和写入操作的示例:

#include <fstream>  
#include <iostream>  
#include <string>  int main() {// 写入文件  std::ofstream outfile("test.txt");if (!outfile){std::cerr << "failed to open the file" << std::endl;return 1;}outfile << "first line" << std::endl;   //写入第一行outfile << "second line" << std::endl;  //写入第二行outfile.close();// 读取文件  std::ifstream infile("test.txt");if (!infile) {std::cerr << "failed to open the file" << std::endl;return 1;}std::string line;while (std::getline(infile, line)){std::cout << line << std::endl;}infile.close();return 0;
}

文件流的状态
可以使用文件流对象的 is_open() 成员函数来检查文件是否已成功打开。此外,还可以使用 fail() , eof() , 和 bad() 等成员函数来检查流的状态。
文件流的其他操作
clear(): 重置流的状态标志。
seekg() 和 seekp(): 移动文件的读写指针。
tellg() 和 tellp(): 返回文件的读写指针当前位置。
flush(): 清空输出缓冲区,确保所有数据都被写入文件。

相关文章:

  • NLP_Transformer架构
  • Code Composer Studio (CCS) - Current and Local Revision
  • HDR 摄影
  • 第三百四十九回
  • 基于Qt数据库项目实现(Sqlite3为例)|考查数据库、表格(QTableView 显示)(进阶)
  • c语言之for循环语句
  • LLM(2)之指令提示词(Prompt)基础教学
  • 5.10 BCC工具之stacksnoop简介
  • 爆火的人工智能开源open-interpreter源码解析
  • pytorch 实现线性回归(深度学习)
  • 力扣72. 编辑距离(动态规划)
  • EasyRecovery软件免费版与付费版有哪些功能区别?
  • Ps:污点修复画笔工具
  • 【Linux】线程同步
  • 《白话C++》第10章 STL和boost,Page67~70 std::auto_ptr
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • java小心机(3)| 浅析finalize()
  • JS基础之数据类型、对象、原型、原型链、继承
  • React-Native - 收藏集 - 掘金
  • REST架构的思考
  • 产品三维模型在线预览
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 异常机制详解
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • #Spring-boot高级
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (第二周)效能测试
  • (多级缓存)多级缓存
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (过滤器)Filter和(监听器)listener
  • (简单) HDU 2612 Find a way,BFS。
  • .md即markdown文件的基本常用编写语法
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET Core 和 .NET Framework 中的 MEF2
  • .net core开源商城系统源码,支持可视化布局小程序
  • .net 微服务 服务保护 自动重试 Polly
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .net中的Queue和Stack
  • [ CTF ]【天格】战队WriteUp- 2022年第三届“网鼎杯”网络安全大赛(青龙组)
  • []error LNK2001: unresolved external symbol _m
  • [20171102]视图v$session中process字段含义
  • [AI]ChatGPT4 与 ChatGPT3.5 区别有多大
  • [AIR] NativeExtension在IOS下的开发实例 --- IOS项目的创建 (一)
  • [HackMyVM]靶场 VivifyTech
  • [IE9] IE9 beta版下载链接
  • [JDK工具-2] javap 类文件解析工具-帮助理解class文件,了解Java编译器机制
  • [jQuery]使用jQuery.Validate进行客户端验证(中级篇-上)——不使用微软验证控件的理由...
  • [Lucene] Lucene 全文检索引擎简介
  • [MYSQL]mysql将两个表结果合并到一起
  • [na]wireshark抓包排错-tcp.flags.reset