C++primer 薄片化系列之标准库特殊实施

std::tuple

tuple 可以将一些数据组合成单一对象.

构造函数是explicit 的

std::tuple<int,double> s = {
   
   1,2.5};//错误
std::tuple<int,double> s(1,2.5);//正确

访问成员

std::tuple<int,double> s(1,2.4);
std::get<0>(s);//获取s的第一个元素
std::get<1>(s);//获取s的第二个元素,以此类推

std::get返回的是一个引用,因此可以直接利用该函数修改tuple元素的值

std::tuple<int,double> s(1,2.4);
std::get<0>(s)*=20;
std::cout << std::get<0>(s) << std::endl;//输出20

获取tuple 的数量和类型

auto s;//假设s是一个不知道数量和类型的tuple
typedef decltype(s) trans;
auto sz = std::tuple_size<trans>::value;//获得了未知tuple的数量
std::tuple_element<1,trans>::type cnt = std::get<1>(s);//第一个元素的类型

两个tuple 只有数量,类型一致时才能进行比较, 并且每个成员的==运算符和<运算符必须合法

std::bitset

初始化的方式

#include<bitset>
void init()
{
  std::bitset<32> b1(1U);
  std::bitset<4> b2(0xf);//1111
  std::bitset<2> b3(0xf);// 11 ,高位多余的部分被丢弃
  std::bitset<32> b4(~0ULL);// long long 0ULL是64个0比特,因此~0ULL 是64个 1
  std::bitset<32> b5("1100");

  std::string s = "11001100";
  std::bitset<32> b6(s,4,4);//1100
  std::cout << b6.to_string() << std::endl;
  std::bitset<32> b7(s, s.size()-4);//取最后四位 1100

}

一些操作

void test()
{
    std::bitset<4> s1("1111");
    std::cout << s1.to_string() << std::endl;//输出1111
    std::cout << s1.all() << std::endl;//判断s1是否所有位都置位,也就是设为1
    std::bitset<4> s2("1000");
    std::cout << s2[0] << std::endl;//返回0,默认低位是从最右边开始
    std::cout << s2.any() << std::endl;//判断s2是否含有1
    std::bitset<4> s3("0000");
    std::cout << s3.none() << std::endl;//判断s3是否全部没有置位

    std::cout << s3.count() << std::endl;//统计s3中1的个数
    std::cout << s3.size() << std::endl;//统计s3的size

    std::cout <<  s3.test(0) << std::endl;//s3第0位为1返回true,否则返回false

    s1.reset();//将s1所有位设置为0, s1从"1111"变为 "0000"
    std::bitset<4> s4("1101");
    s4.reset(0);//将s4第0位设为0, 输出1100


    s3.set(); //将s3所有位设置为1, s3变成 1111
    s3.set(1,false); //将s3第1位设置为0, s3 变成 1101

    s3.flip();//将s3每一位改变状态,也就是每一位原来是1的变成0,原来是0的变成1
    s3.flip(2);//将s3的第2位改变状态
    std::cin >> s3; //读取0或者1,当下一个字符不是0或者1,或者已经读完b.size时,结束
    std::cout << s3 << std::endl; //直接输出

    std::bitset<4> s5("0010");
    std::cout << s5.to_ullong() << std::endl;//输出 2

}

正则表达式

int test()
{
    std::string pattern("[^c]ei");
    pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*";
    std::regex r(pattern); 
    std::smatch results;
    std::string teststr("receive freind teind");
    if(std::regex_search(teststr,results, r))
        std::cout << results.str() << std::endl;

}

regex默认使用的正则表达式语言是ECMAScript,可以设置为其他的方式

    std::regex r2(pattern, std::regex_constants::icase);//匹配时忽略大小写
    std::regex r3(pattern, std::regex_constants::nosubs);//不保存匹配的子表达式
    std::regex r4(pattern, std::regex_constants::optimize);//执行速度优于构造速度
    std::regex r5(pattern, std::regex_constants::basic);//使用POSIX基本的正则表达式语法
    std::regex r6(pattern, std::regex_constants::extended);//使用POSIX版扩展的正则表达式语法
    std::regex r7(pattern, std::regex_constants::awk);//使用POSIX版本的 awk语法
    std::regex r8(pattern, std::regex_constants::grep);//使用POSIX版本的 grep 语法
    std::regex r9(pattern, std::regex_constants::egrep);//使用POSIX版本的 egrep 语法

正则表达式可以被看成用简单程序设计语言编写的"程序". 如果编写的正则表达式有错误,只有在运行中才会报错,可以用try catch 捕获

 try{
       std::regex r("[[:alum:]+\\.(cpp|cxx|cc)$", std::regex_constants::icase);

   }catch(std::regex_error e)
   {
       std::cout << e.what() << "\n code:" << e.code() << std::endl;//错误代码
   }

匹配结果被传入了一个类型为std::smatch的对象中.
这里std::smatch 与输入序列存在一定的对应关系.输入string, 匹配结果为std::smatch, 如果输入一个const char*, 则匹配结果不再是std::smatch 类型,而是std::cmatch

输入序列         使用的正则表达式类
string          regex, smatch, ssub_match, sregex_iterator
const char *    regex, cmatch, csub_match, cregex_iterator
wstring         wregex, wsmatch, wssub_match, wsregex_iterator
const wchar_t*  wregex, wcmatch, wcsub_match, wcregex_iterator

POSIX 匹配字符集

[:alnum:]  文字数字字符
[:alpha:]  文字字符
[:digit:]  数字字符
[:graph:]  非空字符(非空格、控制字符)
[:lower:]  小写字符
[:cntrl:]  控制字符
[:print:]  非空字符(包括空格)
[:punct:]  标点符号
[:space:]  所有空白字符(新行,空格,制表符)
[:upper:]  大写字符
[:xdigit:] 十六进制数字(0-9,a-f,A-F)
//基本正则表达式(BRE)和扩展正则表 达式(ERE)
//BRE和ERE到底有什么区别?其实仅仅是元字符的不同!在BRE方式中,只承认^ 、$、 . 、[ 、] 、*这些是元字符,所有其他的字符都被识别为文字字符。而ERE中,则添加了(、 ) 、{ 、} 、?、 + |、等元字符(及其相关功能)。
void test()
{
    std::string pattern = "[[:alpha:]]*[[:digit:]].[[:alpha:]]*";//匹配含有至少一个数字的单词,[[:digit:]].必须在数字正则式外面继续包围一层[].来代表至少含有一个数字
    std::regex r(pattern);
    std::string s{
   
   "freind teind e2e b2b bb2bb"};
    try{
        for(std::sregex_iterator it(s.begin(),s.end(), r),end_it; it!=end_it;it++)
        {
            std::cout << "prefix:" << it->prefix() << "|suffix:" << it->suffix() << "|str:" << it->str() << std::endl;
        }
    }catch(std::regex_error e)
    {
        std::cout << e.what() << "\n code: " << e.code() << std::endl;
    }
}
//该函数的输出是
prefix:freind teind|suffix:b2b bb2bb|str:e2e
prefix:|suffix:  bb2bb|str:b2b
prefix:  |suffix:|str:b2b

子串匹配

void submatch()
{
    std::string pattern = "(\\()?(\\d)?";//捕获0或者多个括号以及0或者多个数字
    std::regex r(pattern);
    std::string s{
   
   "2b b2b bb2bb"};
    std::smatch result;
    try{
        if(std::regex_search(s,result, r)){
            std::cout << result.str() << std::endl;//输出2
            std::cout <<result.str(0) << std::endl;//输出2
            std::cout <<result.str(1) << std::endl;//输出空,因为2b没有'('
            std::cout <<result.str(2) << std::endl;//输出2
            std::cout << result[0].matched << std::endl;//True
            std::cout << result[1].matched << std::endl;//False
            std::cout << result[2].matched << std::endl;//True
        }

    }catch(std::regex_error e)
    {
        std::cout << e.what() << "\n code: " << e.code() << std::endl;
    }

}

regex_replace

void replace_test()
{
    std::string pattern = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})";
    std::regex r(pattern);
    std::string s{
   
   "(201)-555"};
    std::string s1{
   
   "(201) 555"};
    std::string s2{
   
   "(201).555"};
    std::smatch result;
    try{
       std::cout << std::regex_replace(s, r, "$2.$5") << std::endl;

    }catch(std::regex_error e)
    {
        std::cout << e.what() << "\n code: " << e.code() << std::endl;
    }

}

可以用std::regex_constants::match_flag_type中的变量格式化匹配标志

随机数

void randtest()
{
    std::default_random_engine e;//使用默认的随机数种子
    std::default_random_engine e1(214756);//使用给定的种子
    std::default_random_engine e2;
    e2.seed(32767);//调用seed函数生成新的种子
    std::default_random_engine e3(std::time(0));//time返回以秒计的时间,因此这种方式只适合生成种子间隔为秒及更长时间的应用
    for(size_t i = 0; i < 10; i++)
    {
        std::cout << e() << std::endl;// 原始随机数
    }
    std::uniform_int_distribution<unsigned> u(0,9); //均匀分布
    for(size_t i = 0; i < 10; i++)
    {
        std::cout << u(e) << std::endl;
    }
    std::uniform_real_distribution<double> u2(0,1);//随机实数
    for(size_t i = 0; i < 10; i++)
    {
        std::cout << u2(e) << std::endl;
    }

    std::uniform_real_distribution<> u3(0,1);//使用默认的模板类型参数 double
    std::normal_distribution<> u4(4,1.5);//生成正太分布的随机数
    for(size_t i = 0; i < 10; i++)
    {
        std::cout << u4(e) << std::endl;
    }
    std::bernoulli_distribution  u5;//这个不是个模板,是个普通类
    for(size_t i = 0; i < 10; i++)
    {
        std::cout << u5(e) << std::endl;
    }
}

格式化输入输出

void iotest()
{
    bool k = true;
    std::cout << k << "  " << std::boolalpha << k << " " << std::noboolalpha << k << std::endl;//控制/取消布尔值输出格式
//输出不同进制数字
    std::cout << "default: " << 20 << " " << 1024 << std::endl;
    std::cout << "oct: " << std::oct << 20 << "  " << 1024 << std::endl;
    std::cout << "hex:" << std::hex << 20 << "  " << 1024 << std::endl;
    std::cout << "decimal: " << std::dec << 20 << " " << 1024 << std::endl;

    std::cout << std::showbase;//输出数字时 会带进制标志
    std::cout << "default:" << 20 << " " << 1024 << std::endl;
    std::cout << "oct:" << std::oct << 20 << " " << 1024 << std::endl;//输出020 02000
    std::cout << "hex:" << std::hex << 20 << " " << 1024 << std::endl;//输出0x14 0x400
    std::cout << "decimal:" << std::dec << 20 << " " << 1024 << std::endl;//输出20 1024

    std::cout << "Precision:" << std::cout.precision() << ", Value:" << std::sqrt(2.0) << std::endl;
    std::cout.precision(12);
    std::cout << "Precision:" << std::cout.precision() << ", Value:" << std::sqrt(2.0) << std::endl;
    //另一种设置精度的方法
    std::cout << "Precision:" << std::setprecision(14) <<std::cout.precision() << ", Value:" << std::sqrt(2.0) << std::endl;

    std::cout << std::scientific << 100 * std::sqrt(2.0) << std::endl;//1E-5 这种形式
    std::cout << std::fixed << 100 * std::sqrt(2.0) << std::endl;//修正后的浮点长度
    std::cout << std::hexfloat << 100 *std::sqrt(2.0) << std::endl;
    std::cout << std::defaultfloat << 100 * std::sqrt(2.0) << std::endl;
    //打印小数点
    std::cout << std::showpoint << 10.0 << std::endl;
    std::cout << std::noshowpoint << 10.5 << std::endl; // 小数部分为0时不显示,非0则原样显示
    //输出宽度,补充空白
    int i = -16;
    std::cout << "i: " << std::setw(12) << i << std::endl;
    std::cout << "i: " << std::setw(12) << std::right << i << std::endl;// 默认右对齐
    std::cout << "i: " << std::setw(12) << std::left << i << std::endl;

    //控制输入格式
    //默认会忽略空白符,可以用std::noskipws 输入读取空白符,而不是跳过他们
    std::cout << std::noskipws;
}

未格式化的输入输出操作

void iotest()
{
    //单字节操作
    char c;
    std::cin.get(c);//读入单字节
    std::cout.put(c);//输出单字节
    std::cout  << " is print in the front of this line" << std::endl;

    //unget 和putback 功能基本一致,二者都是将之前cin>>a 出去的字符,再放回cin里,这样下一个再调用cin>> b的时候,b 又和前一次读取的字符一致了.唯一差别在于 putback 可以指定字符放回cin中,unget 只能放入之前的输入(不知道之前是啥)
    std::cin.putback(c);
    std::cin.get(c);
    std::cout << c << " is print again" << std::endl;

    std::cin.putback('k');
    std::cin.get(c);
    std::cout << c << " is print with assigned char k" << std::endl; //指定字符k 被放入cin,调用cin 就会被输出

    std::cin.unget();
    std::cin.get(c);
    std::cout << c << " is print with the help of unget" << std::endl;//输出单字节


    //peek将下个字符返回,但是这个字符仍然在cin中. 相当于std::cin.get(k) 和 std::cin.putback(k)的结合
    char l;
    std::cin.putback('t');//确保cin中有值
    c = std::cin.peek();
    std::cin.get(l);
    std::cout << "c is returned but still in cin, so l is same with c and l is " << l << " , c is " << c << std::endl;

    //多字节操作

    char s[1024];
    std::cin.get(s, 1024, ',');//最后一个参数,当输入字符串带有这个字符时,cin读到这个字符后,就就停止继续读了,剩余的字符被放在cin中,不被写入s中
    //输入abc,d
    std::cout << s << std::endl; //输出abc. ",d"被留在了cin中
    while(std::cin.peek() != '\n')
    {
        c = std::cin.get();
        std::cout << "Still has char " << c << std::endl;
    }
    std::cin.get();//消除最后一个换行符
    std::cout << "==============" << std::endl;
    std::cin.getline(s,1024, ',');
    std::cout << s << std::endl;//输入"abc,d",逗号会被跳过,输出abc, d还在流中
    while(std::cin.peek()!='\n')//
    {
        c = std::cin.get();
        std::cout << "Still has char " << c << std::endl;
    }
    std::cin.get();//消除最后一个换行符
    std::cout << "==============" << std::endl;
    std::cin.read(s, 5);
    std::cout << s<< std::endl;//输入abcde,输出 abcde
    std::cout << std::cin.gcount() << std::endl;//返回上一个未格式化读取操作从std::cin 中读取的字节数
    while(std::cin.peek()!='\n')//
    {
        c = std::cin.get();
        std::cout << "Still has char " << c << std::endl;
    }
    std::cin.get();//消除最后一个换行符
    std::cout << "==============" << std::endl;
    std::cout.write(s,3);//将s中的3个字节写入std::cout  输出abc
    std::cout << "######"<< std::endl;
    std::cin.ignore(3,',');//cin会读取输入,如果输入中的前3个字符不包含',', 则忽略这个三个字符.如果前3个字符中包含',',则流会跳过最先碰到的',',并停止忽略,逗号后面的字符将会留在cin  中
    while(std::cin.peek() != '\n')//输入了abc1,输出了1,abc 被忽略了. 输入了"a,b",输出了b,"a,"被忽略了,b还在留在cin中
    {
        c = std::cin.get();
        std::cout << "Still has char " << c << std::endl;
    }

}

流随机访问

istream 和ostream 不支持随机访问。只有fstream 和 sstream支持

void streamTest()
{
    std::ifstream is("b.txt");
    std::ofstream os("a.txt");
    if(!is && !os)
    {
        std::cout << "error" << std::endl;
    }
    char c;
    std::cout << "current position is " << is.tellg() << std::endl;//输入流中标记的当前位置,文件存在则输出0,否则输出-1
    std::cout << "current position is " << os.tellp() << std::endl;//输出流中标记的当前位置,输出0
   // is.seekg(pos);//将输入流的标记重定位到给定的绝对地址。pos通常可以从tellg获得
    // os.seekp(pos);//将输出流的标记重定位到给定的绝对地址。pos通常可以从tellp获得
   //假定a,txt文件内容如下:第一行是ab,第二行是c
    is.get(c);
    std::cout << "[" << c << "] and current position is " << is.tellg() << std::endl;//输出[a] 1


    char d ;
    is.seekg(1,std::ios::beg);输入流中标记重定位到开始后的第一个位置
    is.get(d);
    std::cout << "[" << d << "] and current position is " << is.tellg() << std::endl;//输出[b] 2

    char e;
    is.seekg(1, std::ios::cur);//输入流从当前定位向后移一位,跳过了换行符
    is.get(e);
    std::cout << "[" << e << "] and current position is " << is.tellg() << std::endl;//输出[c] 4

    char f;
    is.seekg(-1, std::ios::cur);//输入流从当前定位向前移动一位
    is.get(f);
    std::cout << "[" << f << "] and current position is " << is.tellg() << std::endl;//输出[c] 4


    is.seekg(0, std::ios::end);
     std::cout << "current position is " << is.tellg() << std::endl;//输出5

    char g;
    is.seekg(-1, std::ios::end);//输入流从当前定位向前移动一位
    is.get(g);//g是个换行符
    std::cout << "[" << g << "]" << is.tellg() << std::endl;//输出d 4

    char h;
    is.seekg(-2, std::ios::end);//输入流从末尾向前移动2位, 跳过文件结束符,输出文件里最后一个字符 h
    is.get(h);
    std::cout << "[" << h << "]" << is.tellg() << std::endl;//输出【c】4


    is.seekg(0);//直接将输入流重新定位到开头位置
    std::string line;
    std::getline(is,line);
     std::cout << "line is [" << line << "]" << is.tellg() << std::endl;//此时流位于第一行的最末尾位置,也就是换行符所在的位置

     is.seekg(-1,std::ios::cur);
     char i;
     is.get(i);
    std::cout << "[" << i << "]" << is.tellg() << std::endl;//输出【换行符】 3

    char j;
    is.get(j);
    std::cout << j << " " << is.tellg() << std::endl;//此时读取了第二行第一个字符[c] 4
    std::cout << os.tellp() << std::endl;
    os << line;
   std::cout << os.tellp() << std::endl;//输出1, 因而向其中写入一行数据
    os << '\n';
    os << h << '\n';
}

猜你喜欢

转载自blog.csdn.net/jxhaha/article/details/78584379