主要的笔记内容针对于C++相关的内容,主要针对的课程为Stanford CS106L (2020)的笔记,主要为后面的xv6写的前置笔记知识
Lecture 1: Streams I
Streams 主要的作用就是:外部设备 ↔ (字符缓冲区)stream ↔ 你的变量(double、int、struct)
stringstream
std::istringstream: 输入字符串流(从string中按类型提取)std::ostringstream: 输出字符串流(往里面放入各种类型,得到string类型)std::stringstream: 既能读又能写
#include <iostream>
#include <sstream>
int main()
{
// “16.9 Ounces”, pos at front
std::istringstream iss("16.9 Ounces");
double value;
std::string unit;
iss >> value >> unit;
std::cout << "Value: " << value << ", Unit: " << unit << std::endl;
return 0;
}
// Value: 16.9, Unit: Ounces
上面的例子可以看到输入字符串流中的iss -> 16.9 Ounces后面根据类型进行分类,因为中间遇见了空格进行了两个位置进行分割
// “Ito En Green Tea ”, pos at front
std::ostringstream oss("Ito En Green Tea ");
oss << 16.9 << " Ounce";
std::cout << oss.str() << std::endl;
// 16.9 Ounceen Tea
上面的例子中的输出字符串流,指针进行因为是在字符串的最开始的位置开始的,所以内容会进行覆盖,这里就和PPT中的一个位置是一样的
如果想从字符串的最后面的位置开始进行拼接,可以使用方式ate或者直接使用+
// “Ito En Green Tea ”, pos at back
std::ostringstream oss("Ito En Green Tea ", std::stringstream::ate);
oss << 16.9 << " Ounce";
std::cout << oss.str() << std::endl;
std::string valuestr = "16.9";
std::string result = "Ito En Green Tea " + valuestr + " Ounce";
std::cout << result << std::endl;
//Ito En Green Tea 16.9 Ounce
//Ito En Green Tea 16.9 Ounce
stringstream formatted i/o(» / «)
<<:把变量转成字符,用于将数据推送到流对象中
>>:从缓冲区读取下一个 token,按变量类型解析
什么属于空白分隔符:空格,\t,\n等等可以被隔开的片段
//16.9 Ounces . -38271
std::cout << "Enter a string: ";
std::string input;
std::getline(std::cin, input);
std::istringstream iss(input);
double token1;
std::string token2;
char token3;
bool token4;
iss >> token1 >> token2 >> token3 >> token4;
std::cout << "Token 1: " << token1 << std::endl;
std::cout << "Token 2: " << token2 << std::endl;
std::cout << "Token 3: " << token3 << std::endl;
std::cout << "Token 4: " << token4 << std::endl;
//Enter a string: 16.9 Ounces . -38271
//Token 1: 16.9
//Token 2: Ounces
//Token 3: .
//Token 4: 1
#include <iostream>
#include <sstream>
int main()
{
//16.9 Ounces . -38271
std::cout << "Enter a string: ";
std::string input;
std::getline(std::cin, input);
std::istringstream iss(input);
int token1;
std::string token2;
char token3;
bool token4;
iss >> token1 >> token2 >> token3 >> token4;
std::cout << "Token 1: " << token1 << std::endl;
std::cout << "Token 2: " << token2 << std::endl;
std::cout << "Token 3: " << token3 << std::endl;
std::cout << "Token 4: " << token4 << std::endl;
return 0;
}
上面的例子中写入了cin的情况后面的lecture中会提到,正常的cin遇见空格就会停止读取,使用getline的话遇见的是\n,上面输入的是16.9 Ounces . -38271,那么通过istringstream把字符串包成一个输入流,去解析的时候
token1: 读int看到'1''6''.''9','.'对 int 无效,于是停在 ‘.’,结果token1 = 16token2: 跳过'.'前的空白,从'.'开始读string,string对'.'是合法字符,会读到"."(后面空白为止)token3: 跳过空白,读下一个char,得到'O'token4: 读取下一个那么就是U
stringstream positioning functions
oss.str():把当前缓冲区内容取成std::stringtellp()/seekp():输出位置指针(put pointer)tellg()/seekg():输入位置指针(get pointer)
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream oss("ABCDEF");
oss.seekp(3);
oss << "Z";
auto pos = oss.tellp();
std::cout << "Position before writing Z: " << pos << " " << oss.str() << std::endl;
return 0;
}
stringToInteger and state bits
-
goodbit (G):一切正常,可以继续读写 -
failbit (F):上一次操作失败(比如类型不匹配),之后所有操作都会被卡死 -
eofbit (E):读到了缓冲区结尾(EOF) -
badbit (B):严重错误(IO 崩溃、设备坏了之类)
这里了解了一个好的编程方式,主要针对于if else可以直接转化为? :表达式,看起来简洁也好很多
void printStateBits(const istream& iss) {
cout << "State bits: ";
cout << (iss.good() ? "G" : "-");
cout << (iss.fail() ? "F" : "-");
cout << (iss.eof() ? "E" : "-");
cout << (iss.bad() ? "B" : "-");
cout << '\n';
}
对于要实现的stringToInteger如下所示:
#include <iostream>
#include <sstream>
int stringtoint(const std::string& str)
{
std::istringstream iss(str);
int value;
char remain;
if (!(iss >> value) || (iss >> remain)) {
throw std::domain_error("not a pure integer");
}
return value;
}
int main()
{
std::cout << "input str: ";
std::string input;
std::getline(std::cin, input);
int number = stringtoint(input);
std::cout << "Converted integer: " << number << std::endl;
return 0;
}
Lecture 2: Streams II
这个鬼调试搞了半天!因为cppdbg不太兼容console,导致会卡死很裂开,换成lldb可以了
{
"version": "0.2.0",
"configurations": [
{
"name": "codeLLDB",
"type": "lldb",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"cwd": "${fileDirname}",
"preLaunchTask": "build c++",
"terminal": "integrated"
}
]
}
cout && cin && getline
cin:标准输入流(从终端/键盘方向来,但不是“直接从键盘读”)
cout:标准输出流(往终端打印,但不是“立刻显示”)
stream = buffer + position pointer + state bits 的抽象
cin在读我们键盘输入的内容,当我们的键盘回车后,系统会把这一行(包括 \n)交给cin的buffer
cin >> x并不是直接读键盘,而是buffer里面有内容就从buffer中读,buffer中没有内容才会卡住等带我们的输入,相当于只是从buffer中按照规则取字符,并移动读指针
cin >> 规则:
- 先
consume all whitespaces (空格、换行 \n、tab……),比如读取字符前面有这些的话会略过这些然后开始读 - 读取
token一般是到空白位置 - 对于
int/double这种基本类型,会尽可能读出一个变量所需的最大字符,从86.2提取int,会得到86,读指针停在小数点.
#include <iostream>
int main()
{
std::string name;
int age;
std::string response;
std::cin >> name;
std::cin >> age;
std::cout << name << age;
std::cin >> response;
return 0;
}
这个例子中假设我输入的是loxic null那么当age的位置的时候,想读int后,会先跳过空白读取的时候,读int失败,fall bit被打开,fallbit开了后,后面的cin都会失败
cout这里开始补充一下,比如我们代码是cout << "hello",这个并不能保证立刻出现在终端中,因为它可能先进入cout的输出缓冲区,这里补充一下输入(cin)操作都会 flush cout,endl以及std::flush都会强制刷新
#include <iostream>
int main()
{
std::cout << "Hello, World!";
std::flush(std::cout);
return 0;
}
对于cin的问题,cin会把整行输入放进buffer,但>>是按空白分token抽取,这里会很容易出现问题
这里引入了一个新的东西叫做getline,会读取到\n
#include <iostream>
#include <sstream>
int getInteger(const std::string& prompt = "integer: ",
const std::string& reprompt = "Please enter a valid ")
{
while(true)
{
std::cout << prompt;
std::string line; int value; char trash;
if(!std::getline(std::cin, line)) throw std::domain_error("[shortened]");
std::istringstream iss(line);
if(iss >> value && !(iss >> trash)) return value;
std::cout << reprompt;
}
}
int main()
{
std::cout << getInteger() << std::endl;
return 0;
}
>>和getline混用的话,这里要注意下,>>会把分隔符留在缓冲区内,而getline不会跳过最开始的空白区域,直接从当前的位置读取到\n
所以最好不用混着使用,如果要使用记得使用ignore()
#include <iostream>
#include <sstream>
int main()
{
std::istringstream iss("16.9 Ounces\n Pack of 12");
std::string str;
iss >> str;
std::string line;
std::getline(iss, line);
std::cout << "First word: " << str << std::endl;
std::cout << "Remaining line: " << line << std::endl;
return 0;
}
First word: 16.9
Remaining line: Ounces
输出结果是,会发现getline读取给line变量的时候会有空格,那么加上一个iss.ignore()去除第一个字符就可以把空格去掉
去看一下源码会知道很多
我们的输入会通过sentry来进行检查,这里会按照skipws跳过前导的空白
后面可以看到时sgetc取出来检查再进行sbumpc消费,所以到空格的时候检查没通过就不会消费,就会导致了分割的剩余
promptUserForFile challenge
写一个函数:提示用户输入文件名,尝试打开ifstream,如果打不开就reprompt,最后返回有效文件名
#include <iostream>
#include <sstream>
#include <fstream>
std::string promptUserForFile(std::ifstream& stream,
std::string prompt = "input your file name: ",
std::string reprompt = "false again, ")
{
std::string filename;
while(true)
{
std::cout << prompt;
if(!getline(std::cin,filename)) throw std::domain_error("input error");
stream.clear();
stream.open(filename);
if (stream.is_open()) {
return filename;
}
std::cout << reprompt;
}
}
int main()
{
std::ifstream file;
std::cout << promptUserForFile(file) << std::endl;
return 0;
}
modern c++ types
size_t
size_t 多用于索引/大小,无符号的整数,一般str.size()的返回类型通常是size_t(无符号),处理字符串的时候要小心如果字符串是空串的时候,一定要注意会不会出现无符号下溢的情况