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_matchstring中匹配的子表达式的结果
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; }
正则表达式库类
输入序列类型使用正则表达式类
stringregex,smatch,ssub_match,sregex_iterator
const char*regex,cmatch,csub_match,cregex_iterator
wstringwregex,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 endsregex_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表示距当前位置的偏移量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值