OOP-THU icon indicating copy to clipboard operation
OOP-THU copied to clipboard

L12笔记

Open hycvegedog opened this issue 2 years ago • 1 comments

L12笔记

ppt中讲的比较清楚或者比较容易的内容就不再作为重点了,主要挑一些我觉得比较难理解的来记。

string类

  • string s2(s0, 8, 3); //截取,index从8开始,长度为3。这实际上跟substr实现了一样的功能,是可以记住的两种截取方式。用法上,除了参数列表多了个s0, 跟substr也没有什么区别。

  • string s4(10, 'x'); //复制字符:xxxxxxxxxx

  • str.c_str() //注意返回值为常量字符指针(const char*),不能修改。

  • 清空:clear()。

  • 查询长度:size()或者length()

  • 尾部添加有两种:push_back()和append() ,当然也可以直接+=或者+。

  • 比较:字典序。C++的字典序:比较第一个不一样的字符。跟长度不一定有关系。比如aaaa<av。

  • 一个试水案例:

    #include <iostream>
    using namespace std;
    
    int main(){
        string str; 
        cout<<str.empty()<<endl;//1
        cin>>str;
        cout<<str.empty()<<endl;//0
        for(char c: str){//跟vector类似的用法
            cout<<c;
        }
        cout<<endl;
        str.clear();
        cout<<str.empty()<<endl;//1
    }
    
  • p10的解释:

    int b = stoi("50 cats", &sz)

    自动找到string中可以转成数字的那些东西

    (其实是非数字的字符前面的那些数字字符)

    开头是数字字符时才能直接用这个

    S表示string;

    i,d,f表示目标的类型

  • 输入方式

    直接cin:读取可见字符 到空格。

  • Getline(cin, str); :读取一行,以换行符判断。

  • Getline(cin, str, '#'); :读到指定分隔符为止,这个分隔符可以任选。

  • ***注意,字符串里面可以有换行符(作为元素)

  • 补充一些输入相关:

  • #include <iostream>
    #include <string>
    #include <cctype>
    #include <fstream>
    using namespace std;
    int main(){
        // string str;
        // str=cin.get();
        // cout<<str<<endl;//1,只能拿到一位。
        char a[15];
        cin.get(a, 10);//2 拿到10位
    }
    
  • IOstream

  • Istream, ostream都是类,cin, cout是类的对象。

  • 头文件中只有cin和cout对象

  • ostream:重载了针对基础类型(内嵌类型)的输出流运算符(<<), 接受不同类型的数据,再调用系统函数进行输出

  • 每次输出运算都要返回对象的引用

  • 格式化输出:include

  • cout << fixed << 2018.0 << " " << 0.0001 << endl;
    				//浮点数 -> 2018.000000 0.000100 6位
    cout << scientific << 2018.0 << " " << 0.0001 << endl;
    				//科学计数法 -> 2.018000e+03 1.000000e-04
    cout << defaultfloat;  //还原默认输出格式
    cout << setprecision(2) << 3.1415926 << endl;
    				//输出精度设置为2 -> 3.1
    cout << oct << 12 << " " << hex << 12 << endl; 
    				//八进制输出 -> 14  十六进制输出 -> c
    cout << dec;	//还原十进制
    cout << setw(3) << setfill('*') << 5 << endl;
    				//设置对齐长度为3,对齐长度所需填充的字符为* -> **5
    
    
  • 上面是课件内容。fixed、scientific、defaultfloat、oct、hex、dec是流操作算子。补充一点较常用的steprecision:

  •     -- precision是成员函数,其调用方式为:cout.precition(5);
        -- setprecision是流操作算子,其调用方式为:cout << setprecision(5);   // 可以连续输出 即是连续起作用,设置一个之后,后面其他浮点数都按照这个格式输出
        它们的功能相同。
        指定输出浮点数的有效位数(非定点方式输出时)
        指定输出浮点数的小数点后的有效位数(定点方式输出时)
        定点方式:小数点必须出现在个位数后面
        非定点方式:小数点不在个位数后面 例如科学计数法
    
    
  • 自定义流操纵算子,如:

  •     eg:
            ostream& tab(ostream& output)
            {
                return output << '\t';
            }
            // 函数可以交给cout用于输出,称作流操纵算子
            // 为什么可以把函数名字写在cout输入语句?
            // 因为iostream里对 << 进行了重载(成员函数)
            // ostream& operator<< (ostream& (*p)(ostream&));   // 函数指针
            // 该函数内部会调用p所指向的函数,且*this作为参数  hex, dec, oct都是函数
            cout << "aa" << tab << "bb" << endl;
    
  • endl:是一个函数,等同于输出'\n',再清空缓冲区 os.flush()。

  • 重载流运算符总是返回引用,禁止复制,只能移动。全局只有cout一个对象(减少复制开销,符合oop思想)。

    文件流

    ifstreamistream的子类,功能是从文件中读入数据。(不能写出)

    打开文件的三种方式:

    ifstream ifs("input.txt");
    
    ifstream ifs("binary.bin", ifstream::binary);//以二进制形式打开文件
    
    ifstream ifs;
    ifs.open("file");//do something
    ifs.close();
    
    

    p26读入示例的解释:

    • ws:用于在输入时跳过开头的空白符 。

    • EOF:EOF是end of file的缩写,表示”文字流”(stream)的结尾。这里的”文字流”,可以是文件(file),也可以是标准输入(stdin)。 EOF不是特殊字符,而是一个定义在头文件stdio.h的常量,一般等于-1。#define EOF (-1) 除了表示文件结尾,EOF还可以表示标准输入的结尾。但是,标准输入与文件不一样,无法事先知道输入的长度,必须手动输入一个字符,表示到达EOF (转自https://blog.csdn.net/sinat_36053757/article/details/66546610)

字符串输入输出流

stringstream是iostream子类,后者又是i和ostream的子类。所以stringstream继承了双方的接口。

  • 它在对象内部维护了一个buffer,使用流输出函数可以将数据写入buffer,使用流输入函数可以从buffer中读出数据。

  • 程序内部的字符串操作。

  • int main() {
    stringstream ss;
    ss << "10";
    ss << "0 200";
    
    int a, b;
    ss >> a >> b;		//a=100 b=200
    return 0;
    }
    //这里主要是体现了buffer在结合上<<, >>之后的用途
    //先把“10”和“0 200”用<<放进来,这样buffer里面是这样:100 200
    //然后再用>>把buffer里面的内容放进去,由于指定了读法:两个int,所以就和先后输入100和200一样了。
    //可以连接字符串,可以将字符串转换为其他类型的数据
    
    
  • p33解释:从tail开始读。head** 是等待读入的最后位置**(还没有读过的在开头;已经读过的在尾巴)head到tail表示当前等待读入的缓冲区,所以head在后面。

  • 函数模板:convert<目标类型>( ) 可以代替std::to_string 和atoi, stoi.

  • //ppt p35
    template<class outtype, class intype>
    outtype convert(intype val)
    {
    	static stringstream ss;
    						//使用静态变量避免重复初始化
    	ss.str(""); 	//清空缓冲区
    	ss.clear(); 	//清空状态位(不是清空内容)
        //https://blog.csdn.net/clearriver/article/details/4366872
    	ss << val;	//把intype类的输入量放进缓冲区
    	outtype res;	//先定义一个接收器
    	ss >> res;	//把缓冲区里的东西放进接收器。这个过程跟outtype的具体类型有关。
    	return res;
    }
    

字符串处理和正则表达式

  • 正则表达式的定义:由字母和符号组成的特殊文本,搜索文本时定义的一种规则

  • image-20220522124721269

  • 中括号:内部的东西是允许出现的

    大括号:长度范围要求

  • 三种模式:匹配,搜索,替换。

  • 指定某个字符,可以在字符串里找到所有的该字符。例如the,使用the进行搜索,可以找到句中所有的"the"。

  • 用法和格式:

    • [a-z] 匹配所有单个小写字母。

    • **[0-9]**匹配所有单个数字。

    • **[a-z] [0-9]**匹配所有字母数字的组合。

    • **[ab] c **匹配ac或者bc。例如:•[Tt]he: The car parked in the garage.

      • 推广来说,前面特殊(如[], . ,等等有特殊意义的表示)+后面一般(就是单纯的几个字符,没有任何形式的括号等)这个模式经常会用。例如[Tt]ree, .ar ......
      • 当然也可以是前面一般后面特殊。ge\ .
    • **\d **等于[0-9].

    • \w 等于**[a-zA-Z0-9_]**,大小写字母,数字,下滑线的组合。

    • . 匹配换行外的全部字符。

    • \ . 匹配句号。

    • [ ^ a-z] 匹配所有非小写字母的单个字符.

    • \D 等价[ ^ 0-9 ], 匹配所有单个非数字.

    • \s 匹配所有空白字符,如\t,\n

    • \S 匹配所有非空白字符

    • \W 匹配非字母、数字、下划线。

    • ^后面紧跟着的是开头,^\t只能匹配到以制表符开头的内容。

    • ? 出现0次或1次

    • +至少连续出现1次及以上

    • *至少连续出现0次及以上

    • 重复模式:

      • **x{n,m}**代表前面内容出现次数重复n~m次。m若为空则表示没有上限。按照个人尝试的结果,n不可以为空。

      • +前一个字符至少连续出现1次及以上。比如a\w+: The car parked in the garage

      • 扩展:[a-z] {5, 12}长度为5~12的英文字母组合; .{5}所有长度为5的。

  • 怎么用?先要引入.

  • 创建正则表达式:regex re("^[1-9][0-9]{10}$")

    这就是 第一位非0 其他是0-9

    创造方法:括号里面一个字符串。字符串是目标表达式

  • 如果需要创建正则表达式"\d+",应该写成regex re("\ \d+")。因为\是转义字符。

  • 使用原生字符串可以保留字面值,取消转义: R"(str)".

    • 括号里面是字符串,外面是个引号。
    • "\d+" = R"(\d+)" = \d+
    • 可以换行。
    • R这个符号不光可以用在regex中。实际上它本身是对string做操作的。仅用在string上当然也可以。
  • 匹配:•regex_match(s, re):询问字符串s是否能完全匹配正则表达式re。注意此处s自然是string类,而re是regex类的。

  • //自行试水
    #include <iostream>
    #include <string>
    #include <regex>
    using namespace std;
    
    int main() {
    string s("student");
    regex e("stu.*");
    //.匹配换行符以外任意符号
    //*任意长度
    if(regex_match(s,e))
    	cout << "matched" << endl;
    //another regex
    string str;
    cin>>str;
    regex new_e(R"(^[a-z0-9_]{3,15}$)");
    if(regex_match(str,new_e))
    	cout << "matched" << endl;
    }
    //注意到单纯以匹配为目的时,smatch sm这句话不是必要的。
    
  • 捕获和分组:有时我们需要知道正则式的某一部分匹配了字符串的哪一部分。

    • 使用()进行标识,每个标识的内容被称作分组。

    • regex_match(s, result, re):询问字符串s是否能完全匹配正则表达式re,并将捕获结果储存到result中。re是regex类的对象,result是smatch类型的对象。

    • 如果需要括号,又不想捕获该分组,可以使用(?:pattern)

      ​ 用(?:sub)(.*)匹配subject:0号为subject,1号为ject

      ​ 这样sub就不捕获了

    • 分组按顺序编号:

      •0号永远是匹配的字符串本身

      •(a)(pple): 0号为apple,1号为a,2号为pple

      •用(sub)(.*)匹配subject:0号为subject,1号为sub,2号为ject

      就是说,括号里面的匹配都是1+号匹配。

    • //ppt示例
      int main () {
      string s("version10");
      regex e(R"(version(\d+))"); smatch sm;
      if(regex_match(s,sm,e)) {
      	cout << sm.size() << " matches\n";
      	cout << "the matches were:" << endl;
      for (unsigned i=0; i<sm.size(); ++i) {
      	cout << sm[i] << endl;
      }
      }
      return 0;
      }
      //结果:2 matches
      //the matches were:
      //version10
      //10
      
      //一些解释
      //R"(version(\d+))"==" version(\d+)"其实只有一个括号,那个R自带的括号可以认为是假括号。
      //这里有两个匹配,0号匹配匹配整个字符串,匹配结果是version10;1号匹配是那个内部括号,匹配结果是10
      //regex_match(s,sm,e)
      //将匹配结果都放到了sm里面
      
      
  • 搜索

    • regex_search(s, result, re):搜索字符串s中能够匹配正则表达式re的第一个子串,并将结果存储在result中。result照例还是smatch对象。

    • 分组同样会被捕获。

    • //ppt示例,带有自己的批注
      #include <iostream>
      #include <string>
      #include <regex>
      using namespace std;
      int main() {
      string s("this subject has a submarine");
      regex e(R"((sub)([\S]*))");
      //正则表达式是:(sub)([\S]*)
      smatch sm;
      //每次搜索时当仅保存第一个匹配到的子串
      while(regex_search(s,sm,e)){
      	for (unsigned i=0; i<sm.size(); ++i)
      		cout << "[" << sm[i] << "] ";
      	cout << endl;
      	s = sm.suffix().str();
          //Suffix是把sm后面的子串传到s里面(也就是,不再有前面已搜索过的子串。这样可以不重复 
      }
      return 0;
      }
      
      
  • 替换

    • regex_replace(s, re, s1):替换字符串s中所有匹配正则表达式re的子串,并替换成s1(字符串类型)。

    • regex_replace返回值即为替换后的字符串。不过要注意一开始的那个字符串s并没有变

    • #include <iostream>
      #include <string>
      #include <regex>
      using namespace std;
      //ppt样例
      int main() {
      string s("this subject has a submarine");
      regex e(R"(sub[\S]*)");
      //表达式为sub[\S]*
      //regex_replace返回值即为替换后的字符串 
      cout << regex_replace(s,e,"SUB") << "\n";
      cout<<s<<endl;
      //this SUB has a SUB
      //this subject has a submarine
      //s没有变。
      return 0;
      }
      
      
    • #include <iostream>
      #include <string>
      #include <regex>
      using namespace std;
      //ppt样例
      int main() {
      string s("this subject has a submarine");
      regex e(R"((sub)([\S]*))");
      //表达式:(sub)([\S]*)
      //regex_replace返回值即为替换后的字符串 
      cout << regex_replace(s,e,"SUBJECT") << endl;
      //$&表示所有匹配成功的部分,[$&]表示将其用[]括起来
      cout << regex_replace(s,e,"[$&]") << endl;
      //$i输出e中第i个括号匹配到的值
      cout << regex_replace(s,e,"$1") << endl;
      cout << regex_replace(s,e,"$2") << endl;
      cout << regex_replace(s,e,"$1 and [$2]") << endl;
      return 0;
      }
      //this SUBJECT has a SUBJECT
      //this [subject] has a [submarine]
      //this sub has a sub
      //this ject has a marine
      //this sub and [ject] has a sub and [marine]
      
    • 可以看出,捕获的分组还是存贮了的。

      • $& 代表re匹配的所有子串

        $1, $2 代表re匹配的第1/2个分组

  • 学生信息例题的一些解释

    • 注意在正则表达式中使用|符合来表达“或者”

      第一个表达式:(My name is |I am )(\w+).

      0——整个;1——My name is |I am ;2——\w+

      第二个表达式:(\d{4}) [.-] (\d{1,2}) [.-] (\d{1,2})

      0——整个;1——\d{4};2——\d{1,2};3——同2

      第三个表达式:[1-9]\d{10}

      0——整个

      第四个表达式也没有括号,和3一样。

hycvegedog avatar May 22 '22 05:05 hycvegedog

我们去年已经有同学整理过笔记 https://github.com/thunlp/OOP-THU/issues/47 如果只是重复课上内容可能贡献不大,大家可能也不太想看到重复内容。如果有新增部分,欢迎对之前的笔记做一定的补充。

hzhwcmhf avatar May 23 '22 04:05 hzhwcmhf