AcWing C++语法笔记第五节字符串

10 篇文章 1 订阅

1、该系列为ACWing中c++语法课,已购买正版,课程作者为yxc(请大家支持正版)。
2、为啥写在这儿,问就是oneNote的内存不够了QAQ

1. 字符与整数的联系——ASCII码

  • 每个常用字符都对应一个-128 ~ 127的数字,二者之间可以相互转化:

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        char c = 'a';
        cout << (int)c << endl;
    
        int a = 66;
        cout << (char)a << endl;
    
        return 0;
    }
    
  • 常用ASCII值'A'- 'Z'65 ~ 90'a' - 'z'97 - 1220 - 948 - 57

  • 字符可以参与运算,运算时会将其当做整数:

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        int a = 'B' - 'A';
        int b = 'A' * 'B';
        char c = 'A' + 2;
    
        cout << a << endl;
        cout << b << endl;
        cout << c << endl;
    
        return 0;
    }
    

练习:输入一行字符,统计出其中数字字符的个数,以及字母字符的个数。

#include <iostream>

using namespace std;

int main(){
    char c;
    
    int nums = 0, chars = 0;
    while(cin >> c){
        if(c>='0' && c<='9') nums++;
        else if(c>='A' && c<='Z' || c>='a'&& c<='z') chars++;
    }
    printf("nums: %d\nchars: %d\n", nums, chars);
    return 0;
}

2. 字符数组

字符数组是存储字符串的一个数组(char类型的数组)。

字符串就是字符数组加上结束符'\0'。(加结束符的意义:需要知道字符串什么时候结束,因此规定以'\0'结束,其ASCII码值为0)

可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个'\0'字符,因此字符数组的长度至少要比字符串的长度多 1

#include <iostream>

using namespace std;

int main()
{
    char a1[] = {'C', '+', '+'};            // 列表初始化,没有空字符,不是字符串
    char a2[] = {'C', '+', '+', '\0'};      // 列表初始化,含有显示的空字符,是字符串。字符数组长度是4,里面字符串长度是3。
    char a3[] = "C++";                      // 自动添加表示字符串结尾的空字符,用字符串初始化字符数组。该初始化方式与char a2[] = {'C', '+', '+', '\0'} 相同,其字符数组长度是4。
    char a4[6] = "Daniel";                  // 错误:没有空间可以存放空字符

    return 0;
}
  • 注:如果a1初始化 char a1[4] = {'C', '+', '+'}也是字符串,最后一个位置初始化成0。

2.1 字符数组的输入输出

1、coutprintf输出字符串:

  • 输出 char a1[] = {'C', '+', '+'} 结果出现C++C++的原因:C++如果把变量定义到函数内部,变量一般按照顺序分配空间:
    在这里插入图片描述
  • 如果输出a1,计算机会一直输出到a2的结束符为止。
  • 想从第一个字符开始输出就写cout << a2 << endl,从第二个开始输出写cout << a2+1 << endl
  • puts(str)函数等价于printf("%s\n", str);(包括换行符),在库函数#include <cstdio>中。
    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    int main()
    {
        char a2[] = {'a','b','c','\0'};
        char a3[] = "abcdef";
        
        cout << a2+1 << endl;
        printf("%s\n", a3+2);
    
        return 0;
    }
    

2、读入字符串,cinscanf在碰到空格、回车和文件结束符都会停止(例如输入abcd efg 只能读入abcd ):

#include <iostream>

using namespace std;

int main()
{
    char str[100];
	
    cin >> str;             // 输入字符串时,遇到空格或者回车就会停止
    cin >> str+1;           //从str[1]存字符串
    scanf("%s", str); 
    
    cout << str << endl;    // 输出字符串时,遇到空格或者回车不会停止,遇到'\0'时停止
    printf("%s\n", str);

    return 0;
}
  • 注意scanf("%s", str)读取字符串时不加取地址符,因为字符串数组的名字str本身就是一个指针。(如果是int 变量scanf("%d", &a)需要变量的地址,这个地址&a就是指针)

  • scanf不能读入一个整数数组,只能读字符串。整数数组只能一个一个数的读。

  • string类型数据只能用cin读。用getline(cin, str)读取string类型数据。cin.getline(str,1000)读取char类型数据(并且不会过滤回车,100长度的字符数组需要101的参数)。

  • 读入一行字符串,包括空格fgets的含义是最多读入多少字符,从哪个文件读入(一般为stdin,将终端当做文件来读)。注意fgets不会删除行末的回车字符

#include <iostream>

using namespace std;

int main()
{
    char str1[100];
    fgets(str1, 100, stdin);  // gets函数在新版C++中被移除了,因为不安全。
                              // 可以用fgets代替,100指这一行最多读100个字符,但注意fgets不会删除行末的回车字符。
	
	char str2[100];
	cin.getline(str2, 100);	  //这一行最多读100个字符,可以写大于字符串的任意数

    string str3;
	getline(cin, str3);       // 不能读到字符串里,只能读string
	
	cout << str1 << endl;
    cout << str2 << endl;
    cout << str3 << endl;

    return 0;
}

2.2 字符数组的常用操作

下面几个函数需要引入头文件:

#include <string.h> 或  #include <cstring>
  • strlen(str),求字符串的长度,不计入\0。在#include <cstring>中也有。
  • strcmp(a, b),比较两个字符串的大小,a < b返回-1a = b返回0a > b返回1。这里的比较方式是字典序(查字典的顺序,先比较前面的字符大小,a<b,再向后比较字母)!在#include <cstring>中也有。
  • strcpy(a, b),将字符串b复制给从a开始的字符数组。
#include <iostream>
#include <string.h>

using namespace std;

int main()
{
    char a[100] = "hello world!", b[100];

    cout << strlen(a) << endl;

    strcpy(b, a);

    cout << strcmp(a, b) << endl;
    cout << strcmp(a, "abcdef") << endl; 

    return 0;
}

2.3 遍历字符数组中的字符

#include <iostream>
#include <string.h>

using namespace std;

int main()
{
    char a[100] = "hello world!";

    // 注意:下述for循环每次均会执行strlen(a),运行效率较低,最好将strlen(a)用一个变量存下来
    for (int i = 0; i < strlen(a); i ++ )
        cout << a[i] << endl;
    for (int i = 0, len = strlen(a); i < len; i ++ )
        cout << a[i] << endl;

    return 0;
}

练习:给定一个只包含小写字母的字符串,请你找到第一个仅出现一次的字符。如果没有,输出no。

#include <iostream>
#include <cstring>

using namespace std;

char str[100010];
int cnt[26];

int main()
{
   cin >> str;
   int len = strlen(str);
   
   for(int i=0; i<len; i++) cnt[str[i]-'a']++;
   
   for(int i=0; str[i]; i++){  //遍历的另一种写法
       if (cnt[str[i]-'a'] == 1){
           cout << str[i] << endl;
           return 0;
       }
   }
   puts("no");
   return 0;
}

练习:把一个字符串中特定的字符全部用给定的字符替换,得到一个新的字符串。

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char str[31];
    scanf("%s", str);
    
    char c;
    scanf("\n%c", &c);
    
    for(int i=0; str[i]; i++)
        if(str[i] == c)
            str[i]='#';
            
    puts(str);
    
    return 0;
}

3. 标准库类型string

可变长的字符序列,比字符数组更加好用(不能将两个字符数组拼在一起且动态分配空间)。需要引入头文件:

#include <string>

3.1 定义和初始化

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s1;              // 默认初始化,s1是一个空字符串
    string s2 = s1;         // s2是s1的副本,注意s2只是与s1的值相同,并不指向同一段地址
    string s3 = "hiya";     // s3是该字符串字面值的副本
    string s4(10, 'c');     // s4的内容是 "cccccccccc"即10个c

    return 0;
}

3.2 string上的操作

(1) string的读写

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s1, s2;

    cin >> s1 >> s2;
    cout << s1 << s2 << endl;
    printf("%s", s1.c_str());
	puts(s1.c_str());
    return 0;
}
  • 不能用scanf(%s, &s1)读入string

  • 可以用printf输出string,需要写成:printf(“%s”, s.c_str())。其中c_str()函数返回存储该字符串s1的字符数组的首地址。

  • 可以使用puts输出string,需要写成:puts(s.c_str())

  • 通过while循环读入字符串。

    #include <iostream>
    
    int main()
    {
        string s;
        
        char c;
        while(cin >> c, c!=',') s+=c;
        return 0;
    }
    

(2) 使用getline读取一整行

  • cin.getline()接受的是字符数组(并且不会过滤回车),getline()接受的是字符串.
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s;
    getline(cin, s);	//字符串char是cin.getline();
    
    cout << s << endl;

    return 0;
}

(3) string的empty和size操作

  • empty判断字符串是否为空,空返回1,非空返回0
  • sizestrlen效果相同,返回字符串的长度。strlen的复杂度是O(n),但size是O(1)的。
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s1, s2 = "abc";

    cout << s1.empty() << endl;
    cout << s2.empty() << endl;

    cout << s2.size() << endl;

    return 0;
}

注意size是无符号整数,因此 s.size() <= -1一定成立。

(4) string的比较:

支持 >, <, >=, <=, ==, !=等所有比较操作,按字典序进行比较。(不需要strcmp(a, b)函数)

(5) 为string对象赋值

string s1(10, 'c'), s2;     // s1的内容是 cccccccccc;s2是一个空字符串
s1 = s2;                    // 赋值:用s2的副本替换s1的副本
                            // 此时s1和s2都是空字符串

(6) 两个string对象相加

string s1 = "hello,  "", s2 = "world\n";
string s3 = s1 + s2;                    // s3的内容是 hello, world\n
s1 += s2;                               // s1 = s1 + s2

(7) 字面值和string对象相加

做加法运算时,字面值和字符都会被转化成string对象(隐性转换),因此直接相加就是将这些字面值串联起来:

string s1 = "hello", s2 = "world";      // 在s1和s2中都没有标点符号
string s3 = s1 + ", " + s2 + '\n';

当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string

string s4 = s1 + ", ";  // 正确:把一个string对象和有一个字面值相加
string s5 = "hello" + ", "; // 错误:两个运算对象都不是string

string s6 = s1 + ", " + "world";  // 正确,每个加法运算都有一个运算符是string
string s7 = "hello" + ", " + s2;  // 错误:不能把字面值直接相加,运算是从左到右进行的

(8) ssin提取字符串信息

  • ssin可以将字符串初始化,并提取任意我们需要的格式(ssin可以视为cin的作用)。ssin可以改为其他变量名。
  • 需要加入#include <sstream>并将字符串初始化为字符串流。
  • 练习:从字符串中读取各种数据
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    int main()
    {
        string s;
        
        getline(cin, s);
        
        stringstream ssin(s);
        
        int a, b;
        string str;
        double c;
        
        ssin >> a >> str >> b >> c;
        cout << a << endl << str << endl << b << endl << c ;
        
        
        return 0;
    }
    输入:123 xvy 345 .456
    输出:123
          xvy
    	  345
          0.456
    
  • ssin 的循环:
    while (ssin >> str) cout << str << endl;
    
  • 练习 :输入一个字符串,以回车结束(字符串长度不超过 100)。该字符串由若干个单词组成,单词之间用一个空格隔开,所有单词区分大小写。现需要将其中的某个单词替换成另一个单词,并输出替换之后的字符串。
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    int main()
    {
        string s, a, b;
        
        getline(cin, s);
        cin >> a >> b;
        
        stringstream ssin(s);
        string str;
        while (ssin >> str)
        {
            if(str == a) cout << b << ' ';
            else cout << str << ' ';
        }
        
        return 0;
    }
    

(9) sscanf从字符数组中提取信息

类似于ssin只是通过scanf的方式从字符串数组中提取信息。

#include <iostream>
#include <sstream>

using namespace std;

int main()
{
    char s[1000];
    
    fgets(s, 1000, stdin);
    
    int a, b;
    char str[1000];
    double c;
    
    sscanf(s, "%d%s%d%lf", &a, str, &b, &c);
    printf("%d\n%s\n%d\n%lf", a, str, b, c);
      
    return 0;
}

3.3 处理string对象中的字符

可以将string对象当成字符数组来处理:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "hello world";

    for (int i = 0; i < s.size(); i ++ )
        cout << s[i] << endl;
    return 0;
}

或者使用基于范围的for语句:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "hello world";

    for (char c: s) cout << c << endl;
	//基于范围的for循环,等价于:
	for(int i=0; i<s.size(); i++)
	{
		char c = s[i] 
		//改变c的值,并不会改变字符串s的值
	}
	
	for(char &c:s)  c= 'a';  //改变c同时改变s的值,c和s在同一个地址。
    cout << s << endl;

    return 0;
}
  • auto语法:把一些变量类型用auto替代
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "hello world";
    for (auto c: s) cout << c << endl;

    return 0;
}

练习:密码翻译,输入一个只包含小写字母的字符串,将其中的每个字母替换成它的后继字母,如果原字母是'z',则替换成'a'

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    string s;
    
    getline(cin, s);
    
    for(auto &c:s)
        if(c>='a' && c<='z') c=(c-'a'+1)%26 + 'a';
        else if(c>='A' && c<='Z') c=(c-'A'+1)%26 + 'A';
    
    cout << s << endl;
    return 0;
}

练习:输入两个字符串,验证其中一个串是否为另一个串的子串。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值