C++基础:Stanford CS106L学习笔记 3 流

目录

3.1 流概览

Streams------a general input/output(IO) abstraction for C++

流作为一种通用的输入输出抽象机制,为 C++ 程序处理数据的读取(输入)和写入(输出)提供了统一的接口,屏蔽了不同数据来源 / 目的地的底层差异。

std::cout << "Hello, World" << std::endl;

std::coutstd::ostream 的实例

同理,std::cinstd::istream 的实例

集成图:

包含关系:

3.2 stringstreams

3.2.1 定义与使用

std::stringstreams是一种把字符串当作流处理的方式。

在处理数据类型混合的场景中十分实用。

cpp 复制代码
void foo() {
    /// partial Bjarne Quote
    std::string initial_quote = "Bjarne Stroustrup C makes it easy to shoot yourself in the foot"; 
    /// create a stringstream 
    // 方法一:用字符串构造函数初始化字符串流(initialize stringstream with string constructor)
    std::stringstream ss(initial_quote);    
    // 方法二:由于是流所以也可以这样插入字符串
    std::stringstream ss;
    ss << initial_quote;
    
    /// data destinations 
    std::string first; 
    std::string last; 
    std::string language, extracted_quote;
    
    // 问题:>> 只读取到下一个空格,所以此情况extracted_quote只能读取到makes!
    ss >> first >> last >> language >> extracted_quote;
    std::cout << first << " " << last << " said this: "<< language << " " << extracted_quote << std::endl;
}
3.2.1 getline()

istream& getline(istream& is, string& str, char delim)

  • getline() 函数会读取输入流 is 中的数据,直到遇到分隔符字符 delim 为止,并将读取到的数据存储到字符串缓冲区 str 中。
  • 分隔符字符(delim char)的默认值为'\n'(换行符)。
  • getline() 函数会 "消耗" 掉分隔符字符(delim character)!
cpp 复制代码
void foo() {
    /// partial Bjarne Quote
    std::string initial_quote = "Bjarne Stroustrup C makes it easy to shoot yourself in the foot"; 
    /// create a stringstream 
    // 方法一:用字符串构造函数初始化字符串流(initialize stringstream with string constructor)
    std::stringstream ss(initial_quote);    
    // 方法二:由于是流所以也可以这样插入字符串
    std::stringstream ss;
    ss << initial_quote;
    
    /// data destinations 
    std::string first; 
    std::string last; 
    std::string language, extracted_quote;
    
    ss >> first >> last >> language;
    // 使用getline
    std::getline(ss, extracted_quote);
    std::cout << first << " " << last << " said this: "<< language << " " << extracted_quote << std::endl;
}

cppreference------std::getline

3.3 输出流

std::ostream

输出流:一种把输入写入目的源或外部源的方式。

std::cout

用 << 把一些东西输出到控制台。

输出流中的字符会先存储在中间缓冲区中,然后再刷新(flush)到目标位置。

std::cout 流是行缓冲的,在发生显式刷新操作之前,缓冲区中的内容不会显示在外部输出源上!

std::endl

std::endl通知std::cout流结束当前行,也通知流进行刷新操作!


用std::endl可以,但是刷新操作开销很大!

'\n'

'\n'会存到中间缓冲区,减少刷新操作!

注意:

然而,在测试这些示例时,我发现 '\n' 似乎会以类似于 std::cout 的方式刷新缓冲区。进一步研究后,我查阅了 C++ 参考中关于 std::endl 的内容,其中指出:"在许多实现中,标准输出是行缓冲的,写入 '\n' 无论如何都会导致刷新,除非执行了 std::ios::sync_with_stdio(false)。" 这表明在许多标准输出中,'\n' 的行为与 std::cout 是相同的。此外,当我在程序中附加 | cat(通过管道将输出传给 cat 命令)时,我注意到在文件输出中,'\n' 并不会立即刷新缓冲区。

std::ios::sync_with_stdio(false)链接

cerr&clog
  • cerr(character error):用于输出可能导致系统崩溃的错误信息(无缓冲),类比std::endl,效率低
  • clog(character logging):用于非关键事件的日志记录(有缓冲),类比'\n',效率高

链接

std::ofstream&std::ifstream

std::ofstream

一种向文件写入数据的方式!

○ 使用 << 插入运算符(insertion operator)将数据写入文件

○ std::ofstream 类提供了一些成员方法,可自行查阅了解

○ 需要掌握的方法:

■ is_open ():判断文件流是否成功打开文件,返回 bool 值(true 表示已打开,false 表示未打开)

■ open ():打开指定文件,将文件与 std::ofstream 对象关联,需传入文件路径等参数

■ close ():关闭已打开的文件,释放文件资源,避免资源泄漏

■ fail ():检查文件流操作是否失败(如打开不存在的文件、无写入权限等),返回 bool 值(true 表示操作失败,false

表示操作正常)

ofstream示例:

cpp 复制代码
int main() {
    /// associating file on construction 
    std::ofstream ofs("hello.txt");     // 创建一个指向 "hello.txt" 文件的输出文件流。
    if (ofs.is_open()) {                // 检查文件是否已打开,若已打开,则尝试向该文件写入数据!
        ofs << "Hello CS106L!" << '\n';
    }
    ofs.close();                        // 这会关闭与 "hello.txt" 文件关联的输出文件流。
    ofs << "this will not get written"; // 会静默失败。
    
    ofs.open("hello.txt");                         // 重新打开流。10和11行选其一
    ofs.open("hello.txt", std::ios::app);          // 重新打开流,以追加的方式,而不是截断!
    ofs << "this will though! It's open again";    // 成功写入流
    return 0;
}

ifstream示例:

cpp 复制代码
int inputFileStreamExample() {
    std::ifstream ifs("input.txt"); 
    if (ifs.is_open()) {
        std::string line;
        std::getline(ifs, line);
        std::cout << "Read from the file: " << line << '\n';
    }
    if (ifs.is_open()) {
        std::string lineTwo;
        std::getline(ifs, lineTwo);
        std::cout << "Read from the file: " << lineTwo << '\n';
    } 
    return 0;
}

同一源 / 目标类型的输入流与输出流是互补的!

3.4 输入流

std::istream

一种从目的源或外界源读取数据的方式

std::cin

用>>从外界读取内容

std::cin是带缓冲的!可以将其理解为一个 "临时存储区":用户先将数据存入这里,之后程序再从这里读取数据

std::cin 的缓冲区会在遇到空白字符时停止(读取 / 存储)

C++的空白字符包括:" ",\n,\t

错误示例一

cpp 复制代码
#include <iostream>

void cinGetlineBug() {
  double pi;
  double tao;
  std::string name;
  std::cin >> pi;
  std::cin >> name;   // 仅仅是吃掉了之前的换行符
  std::cin >> tao;
  std::cout << "my name is : " << name << " tao is : " << tao
            << " pi is : " << pi << '\n';
}

int main() {
    cinGetlineBug();
    return 0;
}

错误示例二

cpp 复制代码
#include <iostream>

void cinGetlineBug() {
  double pi;
  double tao;
  std::string name;
  std::cin >> pi;
  std::getline(std::cin, name);   // 仅仅是吃掉了之前的换行符
  std::cin >> tao;
  std::cout << "my name is : " << name << " tao is : " << tao
            << " pi is : " << pi << '\n';
}

int main() {
    cinGetlineBug();
    return 0;
}

正确示例

cpp 复制代码
#include <iostream>

void cinGetline() {
  double pi;
  double tao;
  std::string name;
  std::cin >> pi;                 // 读取一个浮点数到pi,此时输入缓冲区会留下一个换行符\n
  std::getline(std::cin, name);   // !会直接读取到缓冲区中残留的\n,导致读取到空字符串,实际作用是 "吃掉" 前面留下的换行符
  std::getline(std::cin, name);   // 读取用户输入
  std::cin >> tao;
  std::cout << "my name is : " << name << " tao is : " << tao
            << " pi is : " << pi << '\n';
}

int main() {
    cinGetline();
    return 0;
}
相关推荐
光影少年1 小时前
web3学习路线
前端·学习·前端框架·web3
獭.獭.1 小时前
C++ -- STL【list的使用】
c++·stl·list
Q741_1471 小时前
C++ 栈 模拟 1047. 删除字符串中的所有相邻重复项 题解 每日一题
c++·算法·leetcode·模拟·
青衫码上行1 小时前
【JavaWeb学习 | 第17篇】JSP内置对象
java·开发语言·前端·学习·jsp
jimmyleeee1 小时前
人工智能基础知识笔记二十二:构建一个可以搜索Web的Agent
笔记
Molesidy1 小时前
【Embedded Development】【TCP-IP】关于TCP-IP网络协议的学习记录以及基于TCP-IP网络协议的上层协议的初步理解
网络协议·学习·tcp/ip
Darken031 小时前
基于 STM32 ——GPIO输出
单片机·学习·gpio·硬件
coderxiaohan1 小时前
【C++】map和set的使用
开发语言·c++
组合缺一1 小时前
Solon AI 开发学习10 - chat - 工具调用概念介绍
java·人工智能·学习·ai·llm·solon