• C++ primer 第十七章


    1.tuple类型

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

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

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

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

    tuple支持的操作
    tuple  t;t是一个tuple,成员数为n,所有成员都进行值初始化
    tuple 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(t)返回t的第i个数据成员的引用
    tuple_size:value

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

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

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

    tuple_element::type

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

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

    1.1、定义和初始化tuple

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

    tuplesize_t,double> someval("constants",42,2.7);

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

    1. tuple<size_t,size_t> twod = {1,2}; //错误
    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成员的数量和类型。 

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

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

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

    1.2、使用tuple返回多个值 

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

    1. struct sales_data {};
    2. typedef std::tuple>::size_type, vector::const_iterator, vector::const_iterator> matches;
    3. //书店在files中的索引(整个销售记录的位置),两个指向书店vector中元素的迭代器(对于单个销售记录的遍历器)
    4. bool compareisbn() //比较函数
    5. {}
    6. //查找给定书籍
    7. vector findbook(const vector>& files, const string& book)
    8. {
    9. vector ret; //保存给定书籍数据
    10. for (auto it = files.cbegin(); it != files.cend(); ++it) //遍历file的门店数据
    11. {
    12. auto found = equal_range(it->cbegin(), it->cend(), book, compareisbn); //比较每个门店的销售书籍与book是否相同
    13. if (found.first != found.second) //若有相同的书籍
    14. ret.push_back(std::make_tuple(it - files.cbegin(), found.first, found.second)); //向ret插入数据
    15. }
    16. return ret;
    17. }
    18. void reportresults(istream& in, ostream& os, const vector>& file)
    19. {
    20. string s;
    21. while (in >> s)
    22. {
    23. auto trans = findbook(file, s);
    24. if (trans.empty())
    25. {
    26. os << s << " not found in any stores" << endl;
    27. continue;
    28. }
    29. for (const auto& store : trans)
    30. os << "store " << std::get<0>(store) << endl;
    31. }
    32. }

    2.bitset类型 

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

    bitset类定义在头文件bitset中。

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

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

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

    初始化bitset的方法
    bitset b;

    b有n位,每一位均为0

    此构造函数是一个constexpr

    bitset b(U);

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

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

    此构造函数是一个constexpr

    bitset b

    (s,pos,m,zero,one)

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

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

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

    bitset 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的初始值,可以只用一个子串作为初始值。

    1. string str("1111111000011011");
    2. 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来保存希望查找的正则表达式。

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

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

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

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

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

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

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

    1. if(regex_search(text,results,r))
    2. { 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成员来返回某个错误类型对应的数值编码。

    1. try{...}
    2. catch(regex_error)
    3. { 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。

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

    1. smatch results;
    2. if(regex_search("myfile",results,r) //错误,输入序列为char*
    3. { 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的使用方式:

    1. for(sergex_iterator it(file.begin(),file.end(),r),end_it; it != end_it; ++it)
    2. { 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、使用子表达式

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

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

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

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

    1. //若文件名为foo.cpp
    2. results.str(0) = foo.cpp;
    3. 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()

    代码实例:

    1. //正则表达式
    2. string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})";
    3. regex r(phone); //regex对象,用于查找我们的模式
    4. smatch m; //每个smatch对象会包含5个ssub_match对象
    5. valid(m); //valid函数用来检查m的每个子表达式的格式内容是否完整
    6. bool valid(const smatch& m)
    7. {
    8. if(m[1].matched) //若有左括号,则数字后应紧跟一个右括号
    9. return m[3].matched;
    10. else
    11. return !m[3].matched;
    12. }

    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是新的替换规则

    代码实例:

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

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

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

    1. using std::regex_constants::format_no_copy;
    2. //在名字前同时加上两个命名空间的限定符
    匹配标志
    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、随机数引擎和分布

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

    1. default_random_engine e; //生成随机无符号数
    2. for(size_t i = 0; i < 10; i++)
    3. { 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

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

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

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

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

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

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

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

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

    1. vector<unsigned> randvec()
    2. {
    3. static default_random_engine e;
    4. static uniform_int_distribution<unsigned> u(0,9);
    5. ......
    6. }

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

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

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

    1. default_random_engine e1; //默认种子
    2. default_random_engine e2(34333); //使用给定的种子值
    3. 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将其改为十六进制、八进制和十进制。

    1. cout << oct << 20 << endl;
    2. cout << hex << 20 << end;

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

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

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

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

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

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

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

    1. cout << setpricision(5); //打印精度改为5
    2. 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返回的值是否为文件尾。

    1. while((ch = cin.get()) != EOF)
    2. 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表示距当前位置的偏移量。

  • 相关阅读:
    修改ONNX模型节点
    STM32CubeMX学习笔记-USART_DMA
    P1529 [USACO2.4] 回家 Bessie Come Home 题解
    Android提供了多种方式来打开特定文件夹中的视频
    【智能优化算法】基于金豺优化算法求解单目标优化问题附matlab代码
    OpenAI 发布会总结-图片版
    数据处理及跳转
    Internet Download Manager永久版功能强大的网络下载器
    SQL语句的约束 总结
    Tomcat 9.0.x 源码编译
  • 原文地址:https://blog.csdn.net/sskdspdl/article/details/137349831