[What] std::basic_istream<CharT,Traits>::get
首先,这里说的get指的是专门用于读取输入流的 std::basic_istream<CharT,Traits>::get函数,具体一点就是
std::istream::get、std::ifstream::get、std::istrstream::get、std::iostream::get、std::fstream::get、std::strstream::get及相应的宽字符输入流类型的成员函数get。
get的作用是从输入流中读取(并释放)一个或多个字符。它有以下6种重载形式:
其中(3)(5)默认的delim是'\n',(4)(6)则可以指定delim。
get函数的终止条件如下:
读到eof:以上所有函数当读到流的结尾触发end of file条件时,会执行setstate(eofbit)。
读到delim:(3)(4)(5)(6)当将要读取的下一个字符c == delim时,get会将已读取到的字符串存储并从流中释放,但字符c并不会被释放。如果继续读取,第一个读取到的字符就是c。
读满s:(3)(4)至多读取count-1个字符存储至字符串s中,因为最后一个字符是\0。
没读到:如果没有读出任何字符,会执行setstate(failbit)。
注意:由于get默认的delim是'\n',使得在读文件时的行为表现出来就是读取一行的内容,但它又不会将'\n'读出并释放,这也是容易与getline造成混淆之处。
[How] std::basic_istream<CharT,Traits>::get
下面演示了get函数的具体用法(基本抄了cppreference):
#include <sstream>
#include <iostream>
static void testGet() {
std::istringstream s1("Hello, world.\nCannot read");
std::istringstream s2("123\n|Cannot read");
// [1]
char c1 = s1.get(); // reads 'H'
std::cout << "[1] After reading " << c1 << ", gcount() == " << s1.gcount() << '\n';
// [2]
char c2;
s1.get(c2); // reads 'e'
// [3]
char str1[5];
s1.get(str1, 5); // reads "llo,"
std::cout << "[3] After reading " << str1 << ", gcount() == " << s1.gcount() << '\n';
std::cout << c1 << c2 << str1;
// [5] 读取剩余字符,读到 '\n' 停止
s1.get(*std::cout.rdbuf()); // reads " world."
std::cout << "\n[5] After the last get(), gcount() == " << s1.gcount() << '\n';
// [4] 读到 '|' 停止,'|' 未释放仍保存在流中
char str2[10];
s2.get(str2, 10, '|'); // reads "123\n"
std::cout << "[4]After reading " << str2 << ", gcount() == " << s2.gcount() << '\n';
// [6] 读到 '|' 停止
s2.get(*std::cout.rdbuf(), '|'); // reads nothing
std::cout << "[6]After the last get(), gcount() == " << s2.gcount() << '\n';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
运行结果:
C++中有两种getline函数: 一种是std::basic_istream<CharT,Traits>::getline,它与上文的get函数都是istream类的成员函数;另一种是std::getline,定义于头文件<string>中,是std命名空间的全局函数。
下面分别介绍这两种getline函数。
[What] std::basic_istream<CharT,Traits>::getline
getline的作用是从流中读取(并释放)字符串,直到遇到指定的delim。常用的就是(1),它默认的delim为'\n',等效于getline(s, count, widen('\n'))。
getline函数的终止条件如下:
读到流的结尾,会执行setstate(eofbit);
下一个字符c == delim,字符c会被读取并释放,但不会被存储;
已经读取了count-1个字符,会执行setstate(failbit);
如果没有读出任何字符(e.g.count < 1),会执行setstate(failbit)。
[How] std::basic_istream<CharT,Traits>::getline
#include <iostream>
#include <sstream>
#include <vector>
#include <array>
static void testGetline_istream() {
std::istringstream input1("abc\ndef\ngh");
std::istringstream input2("123|456|\n78");
std::vector<std::array<char, 4>> v;
// [1]
for (std::array<char, 4> a; input1.getline(&a[0], 4);) {
v.push_back(a);
}
for (auto& a : v) {
std::cout << &a[0] << '\n';
}
v.clear();
// [2]
for (std::array<char, 4> a; input2.getline(&a[0], 4, '|'); ) {
v.push_back(a);
}
for (auto& a : v) {
std::cout << &a[0] << '\n';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
运行结果:
可以看出,getline函数并不只局限于读取一行的数据,只是默认delim为'\n'使得它的行为表现出来为读取一行而已。
[What] std::getline
std::getline的功能是从一个输入流中读取字符串,然后存储到一个string中。常用(2)默认的delim也是'\n',等效于getline(input, str, input.widen('\n'))。
std::getline的终止条件如下:
读到流结尾,会设置eofbit并返回;
下一个字符c == delim,字符c会被读取并释放,但不会被添加到str中;
已经读取了str.max_size()个字符,会设置failbit并返回;
如果没有读出任何字符,会设置failbit并返回。
注意:其实std::getline与std::basic_istream<CharT,Traits>::getline大同小异,可以看作是一个功能的两种接口形式:一个针对C++的string类型,另一个针对C类型字符串char *。
[How] std::getline
static void testGetline_string() {
// [2]
std::string name;
std::cout << "What is your name? ";
std::getline(std::cin, name);
std::cout << "Hello " << name << ", nice to meet you.\n";
// [1]
std::istringstream input;
input.str("1|2|3|4|5|6|7|");
int sum = 0;
for (std::string line; std::getline(input, line, '|'); ) {
sum += std::stoi(line);
}
std::cout << "\nThe sum is: " << sum << "\n";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
运行结果:
[Warning] 坑点
坑点1:使用getline(s, count)时字符数组s必须要预留足够的空间!!!
上文多次提到eofbit和failbit这两种指定流状态的标志,详情可见std::ios_base::iostate。
下面使用一个例子验证已经读取了count-1个字符时,会执行setstate(failbit):
static void testGetline_failbit() {
std::istringstream input("123|4567|89");
// note: the following loop terminates when std::ios_base::operator bool()
// on the stream returned from getline() returns false
std::array<char, 4> a;
while (input.getline(&a[0], 4, '|')) {
// 读取 "123" 同时 '|' 也被读出
std::cout << "After reading " << &a[0] << ", gcount() == " << input.gcount() << '\n'; // 4
std::cout << "After reading " << &a[0] << ", tellg() == " << input.tellg() << '\n'; // 4
}
// 读取 "456" 时字符数组读满导致 setstate(failbit) 执行,跳出循环
std::cout << "After reading " << &a[0] << ", gcount() == " << input.gcount() << '\n'; // 3
std::cout << "After reading " << &a[0] << ", tellg() == " << input.tellg() << '\n'; // -1
std::cout << "After reading " << &a[0] << ", rdstate() == " << input.rdstate() << '\n'; // 2 == failbit
// 设置状态为 goodbit 可找到循环退出时文件流指针的确切位置
input.clear();
std::cout << "After clear(), tellg() == " << input.tellg() << '\n'; // 7
// 移到指针到文件开头
input.seekg(0, std::ios::beg);
std::cout << "After seekg(), tellg() == " << input.tellg() << '\n'; // 0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
运行结果(具体的原因分析都已经在注释中标明):
坑点2:当使用流处理的方式读数据时,读到行尾并不会跳行!!!
get和getline读取的结果都是C/C++类型的字符串,有时为了读取其他类型的数据(e.g. int, double),我们通常会结合流处理的方式完成类型转换(流处理可以看作是C++类型转换的通杀法:万物转stream,stream转万物)。
但是,当使用流处理读到行尾时,它并不会主动跳转到下一行!如下面的代码示例:当读出123后,流指针还是在第一行,需要调用两次getline函数使指针跳到456所在的行。
static void testGetlineAndIstream() {
std::istringstream input("123\n***\n456");
int a, b;
std::string str;
input >> a;
std::cout << "First reading from stream: " << a << '\n';
std::getline(input, str);
std::getline(input, str);
input >> b;
std::cout << "Second reading from stream: " << b << '\n';
}
1
2
3
4
5
6
7
8
9
10
11
12
运行结果:
[Summary] 三者的比较
# std::basic_istream<CharT,Traits>::get std::basic_istream<CharT,Traits>::getline std::getline
头文件 <iostream>/<sstream>/<fstream> <iostream>/<sstream>/<fstream> <string>
输出对象 C类型字符串 C类型字符串 C++string对象
释放delim ❌ ❌ ✔️
[Reference] 参考
std::basic_istream<CharT,Traits>::get
std::basic_istream<CharT,Traits>::getline
原文链接:https://blog.csdn.net/llllllyyy/article/details/106153067