Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

L12笔记 #52

Open
hycvegedog opened this issue May 22, 2022 · 1 comment
Open

L12笔记 #52

hycvegedog opened this issue May 22, 2022 · 1 comment

Comments

@hycvegedog
Copy link

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一样。

@hzhwcmhf
Copy link

hzhwcmhf commented May 23, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants