C++ primer 第十七章

1.tuple类型

tuple是类似pair的模板,不同的tuple类型的成员类型不相同,一个tuple可以有任意数量的成员

 每个确定的tuple类型的成员数量是固定的。

tuple适用于将一些数据组合成单一的对象,但又不想麻烦地定义一个新数据结构来表示的情况。

tuple类型及其伴随类型和函数都定义在tuple头文件中。

tuple支持的操作
tuple<T1,T2,...,Tn>  t; t是一个tuple,成员数为n,所有成员都进行值初始化
tuple<T1,T2,...,Tn> t(v1,v2,...,vn)

t是一个tuple,每个成员用对应的初始值v来进行初始化

此构造函数是explicit的。(不能隐式构造)

make_tuple(v1,v2,...,vn)

返回一个用给定初始值初始化的tuple

tuple的类型从初始值的类型推断

t1 == t2

t1 != t2

两个tuple具有相同数量的成员其成员对应相等时,

两个tuple相等,否则两个tuple不等。

t1 relop t2

relop是关系运算符,两个tuple必须具有相同数量的成员

且成员类型能进行比较。

get<i>(t) 返回t的第i个数据成员的引用
tuple_size<tupletype>:value

一个类模板,通过一个tuple类型来初始化,该模板具有

一个名为value的pubic static数据成员,类型为size_t,

表示给定tuple类型中成员的数量

tuple_element<i,tupletype>::type

类模板通过整型常量和一个tuple类型来初始化

type表示给定tuple类型中指定成员的类型

1.1、定义和初始化tuple

定义一个tuple时,需要指出每个成员的类型。

tuple<string,size_t,double> someval("constants",42,2.7);

 由于tuple的构造函数是explicit的,我们必须使用直接初始化语法。

tuple<size_t,size_t> twod = {1,2}; //错误
tuple<size_t,size_t> twod(1,2); //正确

标准库中的make_tuple函数用来生成tuple对象。

auto item = make_tuple("matter",5);

由于tuple的成员都是未命名的,我们需要通过get的标准库函数模板来访问成员。 

使用get,我们必须指定一个显式模板实参,它返回指定成员的引用。

auto book = get<0>(item); //返回item的第一个成员

 尖括号中的值必须是一个整型常量表达式。

使用辅助类模板(tuple_size和tuple_element)来获取tuple成员的数量和类型。 

typedef decltype(item) trans; //trans是item的类型
size_t sz = tuple_size<trans>::value; //返回3
tuple_element<1,trans>::type cnt = get<1>(item); //cnt是一个int

tuple_element所使用的索引是从0开始计数的。

由于tuple定义了<和==运算符,我们可以将tuple序列传递给算法,并且可以在无序容器中将tuple作为关键字类型。

1.2、使用tuple返回多个值 

 tuple的一个常见用途是从一个函数返回多个值。

struct sales_data {};
typedef std::tuple<vector<vector<sales_data>>::size_type, vector<sales_data>::const_iterator, vector<sales_data>::const_iterator> matches;
//书店在files中的索引(整个销售记录的位置),两个指向书店vector中元素的迭代器(对于单个销售记录的遍历器)

bool compareisbn() //比较函数
{}

//查找给定书籍
 vector<matches> findbook(const vector<vector<sales_data>>& files, const string& book)
{
	 vector<matches> ret; //保存给定书籍数据
	 for (auto it = files.cbegin(); it != files.cend(); ++it) //遍历file的门店数据
	 {
		 auto found = equal_range(it->cbegin(), it->cend(), book, compareisbn); //比较每个门店的销售书籍与book是否相同
		 if (found.first != found.second) //若有相同的书籍
			 ret.push_back(std::make_tuple(it - files.cbegin(), found.first, found.second)); //向ret插入数据
	 }
	 return ret;
}

 void reportresults(istream& in, ostream& os, const vector<vector<sales_data>>& file)
 {
	 string s;
	 while (in >> s)
	 {
		 auto trans = findbook(file, s);
		 if (trans.empty())
		 {
			 os << s << " not found in any stores" << endl;
			 continue;
		 }
		 for (const auto& store : trans)
			 os << "store " << std::get<0>(store) << endl;
	 }
 }

2.bitset类型 

bitset类使我们对于位运算的使用更加容易,并且能处理超过最长整型类型大小的位集合。

bitset类定义在头文件bitset中。

当我们定义一个bitset时,需要声明它包含多少个二进制位,大小必须是一个常量表达式

bitset<32> bitvec(1U); //32位,低位为1,其他位为0

bitset中的二进制位是未命名的,且位置是从0开始,我们可以通过位置来访问它们。 

初始化bitset的方法
bitset<n> b;

b有n位,每一位均为0

此构造函数是一个constexpr

bitset<n> b(U);

b是unsigned long long值u的低n位的拷贝。

若n大于unsigned long long的大小,则b中超过的高位被置为0

此构造函数是一个constexpr

bitset<n> b

(s,pos,m,zero,one)

b是string s从位置pos开始m个字符的拷贝。

s只能包含字符zero或one,否则将抛出异常

pos默认为0,m默认为string::npos(string的结束位)

bitset<n> b

(cp,pos,m,zero,one)

与上一个构造函数相同,但从cp指向的字符数组中拷贝字符。

当我们使用一个整型值来初始化bitset时,此值将被转换为unsigned long long类型并被当作位模式来处理。

我们可以从一个string或一个字符数组指针来初始化bitset,字符都直接表达位模式。

当我们使用字符串表示数时,字符串下标最小的字符对应bitset的高位。 

bitset<32> bitvec("1100"); //2,3两位为1,剩余两位为0

string中下标最大的字符(最右字符)用来初始化bitset中的低位(下标为0的二进制位)。

 我们不必使用整个string来作为bitset的初始值,可以只用一个子串作为初始值。

string str("1111111000011011");
bitset<32> bitvec(str,5,4); //从str[5]开始的四个二进制位,1100

2.2、bitset操作 

 bitset操作定义了多种检测或设置一个或多个二进制位的方法。

bitset操作
b.any() b中是否存在置位的二进制位
b.all() b中所有位都置位了吗
b.none() b中不存在置位的二进制位吗
b.count() b中置位的位数
b.size()

返回b的位数(大小)

b.test(pos) 若pos位置的位是置位的,则返回true

b.set(pos,v)

b.set()

将位置pos处的位设置为bool值v。v默认为true

若未传递实参,则将b中所有位置位

 b.reset(pos)

b.reset()

将位置pos处的位复位或将b中所有位复位

b.flip(pos)

b.flip()

改变位置pos处的位的状态或改变b中每一位的状态

b.to_ulong()

b.to_ullong()

返回一个unsigned long或一个unsigned long long值,其位模式与b相同。

若b中位模式不能放入指定的结果类型,则抛出一个异常

b.to_string

(zero,one)

返回一个string,表示b中的位模式。zero和one用来表示b中的0或1

os << b

is >> b

将b中二进制位打印为字符1或0,打印到流os

从is读取字符存入b,当下一个字符不是1或0时,读取过程结束

  前五个操作都不接受参数,返回整个bitset的状态,其他操作则改变bitset的状态。

只有当bitset的大小小于等于对应的大小时,我们才能使用to_ulong和to_ullong操作。

unsigned long ulong = bitvec.to_ulong();

若bitset中的值不能放入给定类型中,则操作会抛出一个overflow_error异常。 

bitset的输入运算符直到读取的字符数达到对应的大小时,或遇到不是1或0的字符时,或遇到文件尾时,读取过程才停止。

3.正则表达式

正则表达式是一种描述字符序列的方法,正则表达式库定义在头文件regex中。

正则表达式库组件
regex 表示有一个正则表达式类
regex_match 将一个字符序列与一个正则表达式匹配(整体格式匹配)
regex_search  寻找第一个与正则表达式匹配的子序列(子串格式匹配)
regex_replace 使用给定格式替换一个正则表达式
sregex_iterator 迭代器适配器,调用regex_search来遍历一个string中所有匹配的子串
smatch 容器类,保存在string中搜索的结果
ssub_match string中匹配的子表达式的结果
regex_search和regex_match的参数

(seq,m,r,mft)

(seq,r,mft)

在字符序列seq中查找regex对象r中的正则表达式。

mft是一个可选的标志。

m是一个match对象,用来保存匹配结果的相关细节。

m和seq必须具有兼容的类型。

3.1、使用正则表达式库

首先定义一个string来保存希望查找的正则表达式。

string pattern("[^c]ei"); //查找不在字符c之后的字符串ei
pattern = "[[::alpha:]]*" + pattern + "[[::alpha:]]*"; 
//[[::alpha:]]* 表示将匹配零个或多个字母

模式[[::alpha:]]*匹配任意字母,符号+和*分别表示我们希望“一个或多个”或“零个或多个”匹配。

将正则表达式存入pattern中,用它来初始化一个regex对象。

regex r(pattern); //构造一个用于查找模式的regex

定义一个名为results的smatch对象和搜索文本text,results用于保存匹配位置的细节信息。

smatch results; //保存搜索结果
string test = "receipt freind theif receive"; //搜索文本

 调用regex_search,若函数找到匹配子串,就返回true。

if(regex_search(text,results,r))
{ cout << results.str(); }

我们可以指定一些标志来影响或控制regex对象的处理过程。

regex选项

regex r(re)

regex r(re,f)

re表示一个正则表达式

f是指出对象如何处理的标志,默认值为ECMAScript

r1 = re 将r1中的正则表达式替换为re,re表示一个正则表达式。
r1.assign(re,f)

与使用赋值运算符效果相同

f是对象如何处理的标志。

r.mark_count()

r.flags()

r中子表达式的数目

返回r的标志集

定义在regex时的标志
icase 在匹配过程中忽略大小写
nosubs 不保存匹配的子表达式
optimize 执行速度优先于构造速度
ECMAScript 使用ECMA-262指定的语法
basic 使用POSIX基本的正则表达式语法
extended 使用POSIX扩展的正则表达式语法
awk 使用POSIX版本的awk语言的语法
grep 使用POSIX版本的grep的语法
egrep 使用POSIX版本的egrep的语法

标志的使用形式:

regex r("[[:alnum:]] + \\.(cpp | cxx | cc)$",regex::icase);

正则表达式与C++一样,可以通过在字符之前放置一个反斜线来去掉其特殊含义。 

一个正则表达式的语法是否正确是在运行时解析的。

若正则表达式存在错误,则在运行时标准库会抛出一个类型为regex_error的异常。

regex_error有一个what操作来描述发生了什么错误,一个code成员来返回某个错误类型对应的数值编码。

try{...}
catch(regex_error)
{ cout << e.what() << " " << e.code() << endl; }
正则表达式错误类型
error_collate 无效的元素校对请求
error_ctype 无效的字符类
error_escape 无效的转义字符或无效的尾置转义
error_backref 无效的向后引用
error_brack 不匹配的方括号[]
error_paren 不匹配的小括号()
error_brace 不匹配的花括号{}
error_badbrace {}中无效的范围
error_range 无效的字符范围
error_space 内存不足,无法处理正则表达式
error_badrepeat 重复字符之前没有有效的正则表达式
error_complexity 要求的匹配过于复杂
error_stack 栈空间不足,无法处理匹配

正则表达式的错误类型编号从0开始计算。

正则表达式的编译是一个非常慢的操作,应尽量避免创建不必要的regex。

正则表达式可以搜索多种类型的输入序列,但是要确保类型与输入序列类型匹配。

smatch results;
if(regex_search("myfile",results,r) //错误,输入序列为char*
{ cout << results.str() << endl; }
正则表达式库类
输入序列类型 使用正则表达式类
string regex,smatch,ssub_match,sregex_iterator
const char* regex,cmatch,csub_match,cregex_iterator
wstring wregex,wsmatch,wssub_match,wsrexgex_iterator
const wchar_t* wregex,wcmatch,wcsub_match,wcregex_iterator

 3.2、匹配与regex迭代器类型

regex迭代器是一种迭代器适配器,被绑定到一个输入序列和一个regex对象上。

sregex_iterator操作

srgex_iterator it

(b,e,r)

一个sregex_iterator,遍历迭代器b和e表示的string。

它调用sregex_search(b,e,r)将it定位到输入中第一个匹配的尾置

sregex_iterator end sregex_iterator的尾后迭代器

*it

it->

返回一个smatch对象的引用或一个指向smatch对象的指针

++it

it++

从输入序列当前匹配尾置开始调用regex_search

前置版本返回递增后迭代器,后置版本返回旧值

it1 == it2

it1 != it2

若两个迭代器都是尾后迭代器,则它们相等

若两个非尾后迭代器是从相同的输入序列和regex对象构造,则它们相等

 sregex_iterator的使用方式:

for(sergex_iterator it(file.begin(),file.end(),r),end_it; it != end_it; ++it)
{ cout << it->str() << endl; }

一个ssub_match对象有两个str和length的成员,分别返回匹配的string和该string的大小。 

smatch操作
m.ready() 若已经通过调用regex_search或regex_match设置了m,则返回true
m.size()

若匹配失败,则返回0

否则返回最近一次匹配的正则表达式中子表达式的数目

m.empty() 若m.size() = 0,则返回true
m.prefix() 一个ssub_match对象,表示当前匹配之前的序列
m.suffix() 一个ssub_match对象,表示当前匹配之后的部分
m.format(...) 正则表达式的替换操作
m.length(n) 第n个匹配的子表达式的大小
m.position(n) 第n个子表达式距序列开始的距离
m.str(n) 第n个子表达式匹配的string

m.begin(),m.end()

m.cbegin(),m.cend()

表示m中sub_match元素范围的迭代器

cbegin和cend返回const_iterator

3.3、使用子表达式

正则表达式中的模式通常包含一个或多个子表达式,正则表达式语法通常用括号来表示

regex r("([[:alnum:]]+)\\.(cpp|cxx|cc)$",regex::icase);
//([[:alnum:]]+),匹配一个或多个字符的序列
//(cpp|cxx|cc),匹配文件扩展名

我们可以通过位置来访问模式中每个子表达式。

第一个子匹配位置为0,表示整个模式对应的匹配,随后是每个子表达式对应的匹配。

//若文件名为foo.cpp
results.str(0) = foo.cpp;
results.str(1) = foo;

子表达式的一个常见用途是验证必须匹配特定格式的数据。

 ECMAScript正则表达式语言的特点:

  • \{d}表示单个数字而\{d}{n}则表示一个n个数字的序列。(\{d}{3}匹配三个数组的序列)
  • 在方括号中的字符集合表示匹配这些字符中任意一个。([-.]匹配一个短横线或一个点)
  • 后接 ‘ ?’的组件是可选的。
  • 使用反斜线表示一个字符本身而不是其特殊含义。

若一个子表达式是完整的匹配的一部分,则其对应的ssub_match对象的matched成员是true的。

子匹配操作
matched

一个public bool数据成员

指出此ssub_match是否匹配了

first

second

public数据成员,指向匹配序列首元素和尾后位置
length() 匹配的大小,若matched为false,则返回0
str() 返回一个包含输入中匹配部分的string,若matched为false,则返回空string
s = ssub

将ssub_match对象ssub转化为string对象s 

等价于s = ssub.str()

代码实例:

//正则表达式
string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})";
regex r(phone); //regex对象,用于查找我们的模式
smatch m; //每个smatch对象会包含5个ssub_match对象
valid(m); //valid函数用来检查m的每个子表达式的格式内容是否完整

bool valid(const smatch& m)
{
    if(m[1].matched) //若有左括号,则数字后应紧跟一个右括号
       return m[3].matched;
    else
       return !m[3].matched;
}
    

3.4、使用regex_replace

正则表达式的regex_replace可以使用在我们想将找到的序列替换成另一个序列的情况。

正则表达式的替换操作

m.format(dest,fmt,mft)

m.format(fmt,mft)

使用格式字符串fmt生成格式化输出,

匹配在m中,可选的match_flag_type标志在mft中。

迭代器dest指向目的位置。

regex_replace(dest,seq,r,fmt,mft)

regex_replace(seq,r,fmt,mft)

遍历seq,用regex_search查找与regex对象r匹配的子串

使用格式化字符串fmt和可选的mft标志来生成输出。

seq是被替换的string

r是原来的regex对象

fmt是新的替换规则

代码实例:

string fmt = "$2.$5.$7"; //将号码格式改为ddd.ddd.dddd
regex r(phone); //用来查找模式的regex对象
string number = "(908) 555-1800"; //被替换的string
cout << regex_replace(number,r,fmt) << endl;

 标准库定义了用来替换过程中控制匹配或格式的标志。

这些匹配标志都定义在regex_constants的命名空间中,而regex_constants则定义在std中。

using std::regex_constants::format_no_copy;
//在名字前同时加上两个命名空间的限定符
匹配标志
match_default 等价于format_defalut
match_not_bol 不将首字符作为行首处理
match_not_eol 不将尾字符作为行尾处理
match_not_bow 不将首字符作为单词首处理
match_not_eow 不将尾字符作为单词尾处理
match_any 若存在多于一个匹配,则返回任意一个匹配
match_not_null 不匹配任何空序列
match_continuous 匹配必须从输入的首字符开始
match_prev_avail 输入序列包含第一个匹配之前的内容
format_default 用ECMAScript规则替换字符串
format_sed 用POSIX sed规则替换字符串
format_no_copy 不输出输入序列中未匹配的部分
format_first_only 只替换子表达式的第一次出现

默认情况下,regex_replace输出整个输入序列,未与正则表达式匹配的部分会原样输出;匹配的部分按格式字符串指定的格式输出。

4.随机数

程序通常需要一个随机数源。

定义在头文件random中的随机数库通过随机数引擎类和随机数分布类来解决随机数问题。

一个引擎类可以生成unsigned随机数序列。

一个分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数。

C++程序不应该使用库函数rand,而应使用default_random_engine类和恰当的分布类对象。

 4.1、随机数引擎和分布

随机数引擎是函数对象类,可以通过调用一个随机数引擎对象来生成原始随机数。

default_random_engine e;  //生成随机无符号数
for(size_t i = 0; i < 10; i++)
{ cout << e() << endl; }

 标准库定义了多个随机数引擎类,区别在于性能和随机性质量不同。

随机数引擎操作
engine e; 默认构造函数,使用该引擎类型默认的种子
engine e(s); 使用整型值s作为种子
e.seed(s); 使用种子s重置引擎的状态

e.min()

e.max()

此引擎可生成的最小值和最大值
engine::result_type 此引擎生成的unsigned整型类型
e.discard(u) 将引擎推进u步,u的类型为unsigned long long

对于大多数场合,随机数引擎的输出是不能直接使用的,因此随机数引擎的随机数也称为原始随机数。

我们使用一个分布类型的对象得到在一个指定范围内的数。

uniform_int_distribution<unsigned> v(0,9); //在0~9之间的均匀分布的随机数
default_random_engine e; 
for(size_t i =0; i < 10; i++)
{ cout << v(e) << endl; } //每个调用返回在指定方位内并服从均匀分布的值

分布类型是函数对象类,它接受一个随机数引擎作为参数,引擎参数生成随机数,并将其映射到指定的分布中。

注意:我们传递给分布对象的是引擎对象本身,而不是引擎对象的一个调用。

当我们说随机数发生器时,是指分布对象和引擎对象的组合。

随机数发生器的伪随机:对于一个给定的发生器,每次运行程序它都会返回相同的数值序列。

一个函数若定义了局部的随机数发生器,应将其定义为static的。

vector<unsigned> randvec()
{
     static default_random_engine e;
     static uniform_int_distribution<unsigned> u(0,9);
     ......
}

若我们希望每次运行程序都会生成不同的随机结果,可以通过提供一个种子来达到这一目的。 

种子是一个数值,引擎可以利用它从序列中一个新位置重新开始生成随机数。

为引擎设置种子的方式:在创建引擎对象时提供种子,或调用引擎中的seed成员。

default_random_engine e1;  //默认种子
default_random_engine e2(34333); //使用给定的种子值
e1.seed(3153); //调用seed设置一个新种子值

调用系统函数time,其返回从一个特定时刻到当前经过了多少秒,该函数定义在头文件ctime中。

4.2、其他随机数分布

uniform_real_distribution类型的对象,并让标准库来处理从随机整数到随机浮点数的映射。

分布类型的操作
Dist d;

默认构造函数,使d准备好被使用

分布类型的构造函数时explicit的

d(e)

用相同的e连续调用d的话,会根据d的分布式类型生成一个随机数序列

e是一个随机数引擎对象

d.min()

d.max()

返回d(e)能生成的最小值和最大值
d.reset() 重建d的状态,使得随后对d的使用不依赖于d已经生成的值

分布类型都是模板,具有单一的模板类型参数,表示分布生成的随机数的类型。

每个分布模板都有一个默认模板实参,当我们想使用默认随机数类型时应加上空尖括号:

uniform_real_distribution<> u(0,1);

新标准库的另一个优势在于可以生成非均匀分布的随机数。

标准库中含有一个分布不接受模板参数,即bernoulli_distribution(伯努利分布)。

伯努利分布是一个普通类,它总是返回一个bool值,概率是一个常数,默认值为0.5;

 由于引擎返回相同的随机数序列,所以我们必须在循环外声明引擎对象。

bernoulli_distribution允许我们调整先行一方的概率。

bernouli_distribution b(.55);

5.IO库再探

5.1、格式化输入与输出

标准库定义了一组操纵符来修改流的格式状态。

一个操纵符是一个函数或是一个对象,会影响流的状态,并能用作输入或输出运算符的运算对象。

操纵符用于两大类输出控制:控制数值的输出形式以及控制补白的数量和位置。

当操纵符改变流的格式状态后,通常改变后的状态对所有后续IO都生效。

由于上述原因,通常最好在不再需要特殊格式时尽快将流恢复到默认状态。

我们可以使用操纵符hex、oct和dec将其改为十六进制、八进制和十进制。

cout << oct << 20 << endl;
cout << hex << 20 << end;

操纵符hex、oct和dec只影响整型运算对象,浮点值的表示形式不受影响。

当对流应用showbase操纵符时,会在输出结果中显示进制。

cout << showbase << hex << 20 << endl; //0x14
cout << noshowbase; //操纵符noshowbase恢复到cout的默认状态

我们可以通过使用uppercase操纵符来输出大写的字母。

默认情况下,浮点值按六位数字精度打印。

我们可以通过调用IO对象的precision成员或使用setprecision操纵符来改变精度。

操纵符setprecision和其他接受参数的操纵符都定义在头文件iomanip中。

cout << setpricision(5); //打印精度改为5
cout.pricision(12); //精度改为12

默认情况下,精度值指定的是数字的总位数,而在执行scientific等操作符后,精度值控制的是小数点后面的数字位数。

定义在iomanip中的操纵符
setfill(ch) 用ch来填充空白
setprecision(n) 将浮点精度设置为n
setw(w) 读或写值的宽度为w个字符
setbase(b) 将整数输出为b进制

默认情况下,输入运算符会忽略空白符(空格符,制表符,换行符,回车符)。

5.2、未格式化的输入\输出操作

标准库还提供了一组底层操作,支持未格式化IO,允许我们把一个流当作一个无解释的字符序列。

单字节底层IO操作
is.get(ch) 从istream is读取下一个字符存入字符ch中,返回is
os.put(ch) 将字符ch输出到ostream os。返回os
is.get() 将is的下一个字节作为int返回
is.putback(ch) 将字符ch放回is,返回is
is.unget() 将is向后移动一个字节,返回is
is.peek() 将下一个字节作为int返回,但不从流中删除它

一般情况下,在读取下一个值之前,标准库保证我们可以退回最多一个值。

返回int的函数将它们要返回的字符先转换为unsigned char,然后再将结果提升到int。

头文件cstdio定义了一个名为EOF的const,我们可以用它来检测从get返回的值是否为文件尾。

while((ch = cin.get()) != EOF)
  cout.put(ch);

 一些未格式化IO操作能够一次性处理大块数据,有效提升了数据处理速度。

多字节底层IO操作
is.get(sink,size,delim)

从is中读取最多size个字节,并保存在字符数组中

字符数组的起始地址由sink给出

读取过程直至遇到字符delim或读取了size个字符或遇到文件尾时停止

若遇到了delim,则将其留在输入流中,不读取出来存入sink

is.getline(sink,size,delim) 与上述版本类似,但会读取并丢弃delim
is.read(sink,size) 读取最多size个字节,存入字符数组sink中,返回is
is.gcount() 返回上一个未格式化读取操作从is读取的字节数
os.write(source,size) 将字符数组source中的size个字节写入os,返回os
is.ignore(size,delim) 读取并忽略size个字符,包括delim。ignore含有默认参数

5.3、流随机访问

 各种流类型通常都支持对六种数据的随机访问。

标准库提供了一对函数:seek来定位到流中给定的位置,tell告诉我们当前的位置。

流的随机访问只适用于fstream和sstream类型。

为了支持随机访问,IO类型维护一个标记来确定下一个读写操作要在哪里进行。

seek和tell函数

tellg()

tellp()

返回一个输入流中(teellg)或输出流(tellp)中标记的当前位置

seekg(pos)

seekp(pos)

在一个输入流或输出流中将标记重定位到给定的地址

pos通常是前一个tellg或tellp返回的值

seekp(off,from)

seekg(off,from)

在一个输入流或输出流中将标记定位到from之前或之后off个字符

from可以是以下值之一

beg 偏移量相对于流开始位置

cur  偏移量相对于流当前位置

end 偏移量相对于流结尾位置

fstream和stringstream类型可以读写同一个流,在这些类型中,有单一的缓冲区,标记也只有一个,表示缓冲区中的当前位置。

由于只有单一的标记,因此只要我们在读写操作间切换,就必须进行seek操作来重新定位标记。

pos的类型是pos_type,表示一个文件位置,而off_type表示距当前位置的偏移量。

上一篇:【隐私计算实训营007——隐语SCQL的架构详细拆解】-2.SCQL CLL


下一篇:Qt | Qt 框架结构概述