zoukankan      html  css  js  c++  java
  • C++课程学习笔记第八周:字符串

    前言:本文主要是根据MOOC网北大课程——《程序设计与算法(三):C++面向对象程序设计》内容整理归纳而来,整理的课程大纲详见 https://www.cnblogs.com/inchbyinch/p/12398921.html

    本文介绍了string类的主要操作,字符串流,以及几种字符串分割和解析成数字的方法。

    1 string类

    string是C++标准库的一个重要的部分,主要用于字符串处理。string类本身定义了很多方便的函数,同时C++的算法库对string也有着很好的支持,而且string还和c语言的字符串之间有着良好的接口。下面列出string对象的一些常见操作。

    • 主要操作有初始化、访问、赋值、连接、比较、交换、增删查改、与C字符串交互、与流交互。
    • CPP官网例子较齐全:http://www.cplusplus.com/reference/string/basic_string/
    • string类是模板类:typedef basic_string< char > string
    • 使用string类要包含头文件 < string >

    1.1 string对象的初始化

    //默认初始化
    string s; //s是一个空串
    //使用字符串字面值初始化
    string s1="hello world";  //拷贝初始化
    string s2("hello world");  //直接初始化
    //使用其他字符串初始化
    string s2=s1; //拷贝初始化,s1是string类对象
    string s2(s1); //直接初始化,s1是string类对象
    //使用单个字符初始化
    string s(10, 'a'); //直接初始化,s的内容是aaaaaaaaaa
    
    //下面是错误的初始化方法
    string error1 = 'c'; // 错
    string error2('u'); // 错
    string error3 = 22; // 错
    string error4(8); // 错
    //可以将字符赋值给string对象
    string s; s = 'n'; //可以
    

    1.2 访问

    //获取长度
    s.length();
    s.size();
    
    //访问元素
    s[i];  //不会做范围检查
    s.at[i]; //会做范围检查
    
    //获取string迭代器,通过迭代器访问
    for(std::string::iterator it=str.begin(); it!=str.end(); ++it)
        std::cout << *it;
    std::cout << '
    ';
    

    1.3 赋值、连接、比较、交换

    //赋值方式1:=
    //赋值方式2:assign函数,将string对象,或char*字符串(或部分),或指定字符赋给s2
    string base("The quick brown fox jumps over a lazy dog.");
    string s1, s2;
    s1 = "hello";
    s1 = 'a';
    s1 = base;
    s2.assign(base);
    s2.assign(base, 1, 5); //从base中下标为1的字符开始复制5个字符给s2
    s2.assign(base.begin()+16, base.end()-12);
    s2.assign("pangrams are cool");  
    s2.assign("pangrams are cool", 7);  // "pangram"
    s2.assign(10, '*');
    
    //string连接方式1:+
    //string连接方式2:append
    string s1("good "), s2("morning! ");
    s1 += s2;
    s1.append(s2);
    s2.append(s1, 3, s1.size()); //从s1下标3开始,复制s1.size()个字符
    //如果字符串内没有足够字符,则复制到字符串最后一个字符
    
    //比较方式1: >,  >=,  <,  <=,  ==,  != 返回值为bool类型
    //比较方式2:成员函数compare,返回值int:若主体s1大则为正,若小则为负,若等于则为0
    string s1("hello"),s2("hello"),s3("hell");
    cout << (s1 > s3) << endl; //1
    cout << s1.compare(s2) << endl; //0
    cout << s1.compare(s3) << endl; //1
    cout << s3.compare(s1) << endl; //-1
    
    //swap函数交换string
    string s1("hello world"), s2("really");
    s1.swap(s2); //成员函数
    swap(s1, s2); //全局函数
    

    1.4 增删查改

    //成员函数substr获取子串
    string s1("hello world"), s2;
    s2 = s1.substr(4,5);  //下标4开始5个字符
    
    //查找string对象中子串,find()或rfind()
    //下例中find()从前向后查找"lo"第一次出现的地方,若找到,返回"lo"开始的位置,
    //即'l'所在的位置下标。否则返回string::npos(string中定义的静态常量)
    string s1("hello world");
    s1.find("lo");  //3
    s1.find("ll",3); //4294967295  从下标3处开始查找
    s1.rfind("ll"); //2  rfind()从后向前查找,用法和find()相同
    
    //查找string对象中字符
    //find_first_of(),find_last_of(),find_first_not_of(),find_last_not_of()
    //下例中find_first_of从前向后查找"abcde"中任何一个字符第一次出现的地方
    //而find_first_not_of是从前向后查找不在"abcde"中的字母第一次出现的地方
    //如果找到则返回找到字母的位置,如果找不到,返回string::npos
    string s1("hello world");
    s1.find_first_of("abcde");  //1
    s1.find_last_of("abcde");  //10
    s1.find_first_not_of("abcde"); //0
    s1.find_last_not_of("abcde");  //9
    
    //删除erase(), 插入insert(), 替换replace()
    s1.erase(5,s1.length()); //自下标5开始删除,可通过下标和迭代器均可定位
    s1.insert(5,s2); // 将s2插入s1下标5的位置
    s1.insert(2,s2,5,3); //将s2中下标5开始的3个字符插入s1下标2的位置
    s1.replace(2,3,"haha"); //将s1中下标2开始的3个字符换成"haha"
    s1.replace(2,3,"haha",1,2); //将s1中下标2开始的3个字符换成"haha"中下标1开始的2个字符
    

    1.5 与C风格字符串交互

    • C字符串可以无缝转为string对象,有三种方式;
    • cout可以直接打印C字符串((const) char* 类型或数组名),无需printf;
    • string对象可以通过c_str()成员函数转为C字符串,返回const char* 类型;
    //C风格字符串转为string
    const char *s = "Hello world!";
    string str1(s);
    string str2 = s;
    string str3("haha");
    str3 = s;
    
    //s1.c_str()返回s1内部的const char* 类型字符串,且该字符串以‘’结尾。
    string s1("hello world");
    const char* p = s1.c_str();
    cout << p << endl;  
    
    //copy(),将string复制为char* 字符串,返回复制的字符个数
    char buffer[20];
    string s1("Test string...");
    length = s1.copy(buffer,6,5); //复制长度为6,从下标5开始(与前不同)
    buffer[length]='';
    

    1.6 与流交互

    //string支持流读取运算符
    string stringObject;
    cin >> stringObject;
    
    //string支持全局getline函数
    string s;
    getline(cin, s);
    //getline和stringstream搭配用于split
    string a, b, c, d; 
    string lines="adfa;asdfasd;fasdf;ccc";  
    stringstream line(lines);  //可以直接初始化
    getline(line, a, 'f'); 
    getline(line, b, ';'); 
    getline(line, c, ';'); 
    getline(line, d);   //默认以换行符作为delim
    

    2 字符串流

    • < sstream >库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行字符串流的输入、输出和输入输出操作。
    • 它内部使用string对象来代替字符数组,这样可以避免缓冲区溢出的危险。此外可以进行自动推断类型,不会因为格式不符而出错。
    • stringstream作用1:通常用来做数据转换。相比c库的转换,它更加安全,自动和直接。
    • stringstream作用2:与getline结合,可用于字符串切分。
    • 参考1参考2

    注意:

    • stringstream可以很方便地用于数据转换,注意每次转换前要调用clear();
    • clear方法是清空ss的状态(比如出错等),清空内容需要使用.str("")方法(实际为替换。若需要继续用ss转换,仍然要调用clear清空状态);
    • 可以利用ostringstream一次性读入需要转换成string的各个变量,利用oss.str()提取string;利用待转换的string来构造istringstream,然后一次性转成各个变量.(建议用stringstream代替。)
    //示例1:利用stringstream,基本数据类型与string互转
    int main(){
        int a1 = 56, a2 = 0, a3=0;
        double b1 = 65.123, b2 = 0.0, b3=0.0;
        stringstream ss;  //头文件是sstream
    
        ss << a1 << " " << b1;  //将基本类型转为string
        cout << "1. " << ss.str() << endl; //1. 56 65.123
        ss >> a2 >> b2; //注:ss中数据应以空格作为分隔
        cout << "2. " << a2 << "---" << b2 << endl; //2. 56---65.123
    
        ss.clear(); //每一次转换之后都必须调用clear()成员函数清空状态
        //ss.str(""); //可以清空原数据
        ss << b1 << " " << a1;
        cout << "3. " << ss.str() << endl; //3. 56 65.12365.123 56
        ss >> b3 >> a3;
        cout << "4. " << ss.str() << endl; //4. 56 65.12365.123 56
        cout << "5. " << a3 << "---" << b3 << endl; //5. 56---65.123
        return 0;
    }
    
    
    //示例2:利用ostringstream和istringstream进行string与基本类型的互转
    int main(){
        int a=1, b=3, a1, b1;
        double c=2.4, d=3.4, c1, d1;
        //一次性读入需要转换成string的各个变量,并保存至string
        ostringstream oss;
        oss << a << " " << b << " " << c << " " << d;
        cout << oss.str() << endl;
        string s = oss.str();
        //利用待转换的string来构造istringstream,然后一次性转成各个变量
        istringstream iss(s);
        iss >> a1 >> b1 >> c1 >> d1;
        cout << iss.str() << endl;
        cout << a1 << " " << b1 << " " << c1 << " " << d1 << endl;
        return 0;
    }
    

    陷阱:

    • ostringstream.str()返回一个临时的string对象,因此应按照正规写法 my_string = ostringstream.str() 将返回值及时保存,而不可用ostringstream.str().c_str()。
    • c_str()返回的是一个const char* 类型的临时指针,若需要则可用自己的空间保存内容。
    • 参考1参考2

    3 字符串相关知识点

    3.1 一些理解

    三个头文件的区别 string、cstring、string.h

    • string.h 是 C 标准库提供的东西,里面有strcpy、memset等函数;
    • C++为了兼容C,给.h头文件套了一个壳子,前面加个c,作为.h头文件的对应;
    • 一般一个C++库老的版本带“.h”扩展名的库文件,比如string.h,在新标准后的标准库中都有一个不带“.h”扩展名的头文件(比如cstring)来对应,区别除了后者的一些改进之外,还有一点就是后者的东东都塞进了“std”名字空间中,譬如调用strlen函数,需要写成std::strlen(yourstr)才行;
    • 如果你用的是C++,那么请用cstring,如果你用的是C请用string.h;
    • string头文件是C++定义的std::string所使用的文件,是string类的头文件,属于STL范畴

    cstring/string.h头文件中常用的函数

    • strlen, strcpy, strcat, strcmp
    • memcpy, memset, strtok

    STL中排序函数 sort 机制:

    • 参数cmp为比较函数,传入两个参数,若为true,则符合要求;
    • 因此,在比较函数中,若 return a < b,则为升序,若 return a > b,则为降序;

    3.2 字符串分割的方法

    • 方法1:利用字符串流;(需要以空格作为分割符)
    • 方法2:利用字符串流和全局getline函数;(示例见1.6)
    • 方法3:利用cstring中std::strtok()函数。
    #include <iostream>
    #include <cstring>
    #include <string>
    using namespace std;
    
    int main(){
        //目的是将一个string对象按照分隔符分割成几个子串对象
        //通过C风格的strtok函数进行分割 
        //char * strtok(char* str, const char* delimiters);
        //由于strtok函数需要传入C风格的字符串,且会破坏字符串结构,故复制原string对象内容
        string s = "hello,  big  - world.";
        string str1, str2, str3;
        char a[100] = {0};
        std::strcpy(a, s.c_str()); //c_str()函数返回const char*类型
        cout << a << endl; //输出hello,  big  - world.
    
        str1 = std::strtok(a, " ,-.");
        str2 = std::strtok(NULL, " ,-.");
        str3 = std::strtok(NULL, " ,-.");
    
        cout << a << endl; //输出hello
        cout << str1 << "-" << str2 << "-" << str3; //输出hello-big-world
        return 0;
    }
    

    3.3 字符串解析成数字的方法

    • 方法1:利用字符串流,find()函数定位;
    • 方法2:利用正则表达式,或者C中scanf函数;
    • 方法3:利用cstdlib中std::strtod()函数。
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    
    class Complex{
    private:
        double r, i;
    public:
        Complex(){};
        Complex(const char* p){
            char a[20];
            char* pEnd;
            std::strcpy(a, p);
            r = std::strtod(a, &pEnd);
            i = std::strtod(pEnd+1, NULL);
        }
        void Print() {
            cout << r << "+" << i << "i" << endl;
        }
    };
    
    int main(){
        Complex a;
        a = "3+4i"; a.Print();
        a = "5+6i"; a.Print();
        return 0;
    }
    
  • 相关阅读:
    PCL点云
    unity碰撞,刚体笔记
    动画剪辑 状态配置 脚本编写2
    unity中动画剪辑,状态机关系配置,脚本编写方式1
    unity给物体着色加纹理图 material(材质)
    C++/Java小白解Leetcode题,发现了知识盲区……
    NLP之统计句法分析(PCFG+CYK算法)
    Java:基于TCP协议网络socket编程(实现C/S通信)
    Java实现:抛开jieba等工具,写HMM+维特比算法进行词性标注
    Java多线程技术:实现多用户服务端Socket通信
  • 原文地址:https://www.cnblogs.com/inchbyinch/p/12398540.html
Copyright © 2011-2022 走看看