C++STL初阶(1):string的使用及初阶原理

此文作为学习stl的笔记,许多普及、概念性的知识点将不再罗列(如stl的发展、背景等)

便于读者作为复习等方法了解。

0.STL简介(笔记向)

      STL不是祖师爷本贾尼实现的,是在惠普实验室中实现的。其作为一个数据结构与算法的库(主要是数据结构),现在有很多商业公司开发的目的,有的开源有的收费,目前使用最多的是(也是我们使用的)继承自HP版本的SGI版本。

学习STL有三重境界:能用、明理、能扩展(如自己扩展B树等)

此篇我们介绍string的运用。


目录

0.STL简介(笔记向)

1.string

2.string的构造函数(constructor,重点是"abcde"会被当作const char*)

3.string的运算符重载[ ](两种重载,带const和不带const)

4.string的遍历 

5.string在VS编译器下的大小(了解)

6.将string按照字典序排序

7.string的插入和修改

7.1 push_back 和 append

7.2更常用的添加方法:运算符重载 +=

7.3 assign函数

7.4 重点: insert 与 erase

7.5 replace替换函数 (注意两个迭代器传参时的左闭右开)

8.小试牛刀

9.capacity大类

​编辑 9.1 max_size(了解)

9.2clear

9.3扩容

9.4 resize和reserve  ★★

9.5 shrink_to_fit

9.6 at

9.7 c_str

10.find

11. string相关的部分外部函数

12.编码表UNICODE


1.string

string,也就是常说的数据结构“串”。string出现的比stl早,所以有一些功能较冗杂

C 语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程) 的思想,而且底层空间需要用户自己管理,稍不留神可 能还会越界访问。
比如 strcpy和strcat,你需要自己管理空间大小、是否有/0、操作的方法。。。。
非常麻烦。
许多都需要使用string存储,如身份证号码(数据很大,用int存储很不划算,并且还有可能带X)

string也叫basic_string,并且被默认为管理char类型数据的容器。 

                                    

还可以管理wchar:

                                       

                                                     wchar是一种 双字节 字符

在大多数情况下,我们可以认为string就是一个char类型的数组、顺序表。 

 string - C++ Reference (cplusplus.com)

string一共一百二十个左右的成员函数,其实很多函数是冗余的。


2.string的构造函数(constructor,重点是"abcde"会被当作const char*)

我们要学会通过文献来使用函数

掌握默认构造、拷贝构造、带参构造、其他的会查网站看明白即可。

第一个是默认构造,第二个是我们最常用的常量区赋值:

string s="abcdefg";

如果我们定义一个空字符串 string str1;  那么str1中是包含了\0的

 默认构造出的s2中间不是乱码,而是什么都没有。

s可以直接被cout输出,也可以通过cin直接获得内容。甚至可以使用中文。

第三种初始化substring是按照使用者主观定制长度拷贝,第一个参数是数组名(字符串名字),第二个是开始拷贝的位置(包括该位置,数据结构喜欢左闭右开),第三个是拷贝的长度。长度的默认参数是npos

成员变量npos:

                                       

注意:npos是无符号整形size_t,所以npos其实是四十二亿左右。当然,一个string没有那么长。

             (其中str is too short的意思是相对于我传的参数len,str可以被拷贝的内容太短了)

                                      

                          (不传参或者写npos就相当于直接拷贝到末尾,都属于str is too short)

同样的,学会通过英文来了解含义

  


                            


常量字符串被认为是const char*,调用的是第五个构造:

这种直接常量字符串接数字表示前三个字符,如果是一个string接数字表示从第数字个位置开始,省略了一个要打印个数的参数。

 


如下,单参数构造函数的隐式类型转换:const char* 转化成string,中间会生成一个临时变量string(构造),再加上一个拷贝构造(有时候直接优化成一次构造)

                              

因此对于引用:

                             

string s4 = "abcdw";//涉及隐式类型转换,"abcdw"被当作char*

3.string的运算符重载[ ](两种重载,带const和不带const)

size和length在此处是相同的作用,length是一开始被设计的,size是在之后stl设计时从整体角度考虑的,便于和其他stl统一。

size和length都不会计算'\0'

                  


使用较少的调用方法:

                                            

这样,我们的自定义类型就能像数组一样使用。


作为自定义、尤其是一些需要深拷贝的类型,我们可以传引用返回来减少拷贝次数以提升效率,那么此处的运算符重载[ ]为什么也要使用传引用返回?拷贝几个字节很费劲吗?

我们先只看第一排的重载

!!! 传引用返回的第二大作用得以体现:可修改容器中的返回值:

                          

string底层开的空间在堆上。

并且由于string的底层的越界检查很多都是使用assert,所以错误都能检查出来。

那么。面对不希望被修改的字符串,如下图的被const修饰的常量字符串(string底层开的空间在堆上

我们再观察之前的声明,[ ]有两种运算符重载,相互构成函数重载:第一种的参数不含const,也就是刚刚提到的展示引用返回作用的重载;还有一种是参数带const的,那么我们对返回值也进行const的修饰,使常量字符串不得被修改。

                              

再加上匹配机制(优先匹配最合适的),为了让不能被修改的就一直不被修改,可以修改的就可以修改,所以我们需要实现两个重载。(只使用带const的重载也可以调用非常量字符串,但是这样的话非常量字符串也不能修改, 不太方便。但是大部分函数用const修饰是只有好处没有坏处的,这样的观念我们在前文提到过)

所以,到底提供几个const,需要在实战中按照实际需要来确定。


4.string的遍历 

根据刚才的size功能和运算符重载,我们可以通过循环实现数组的遍历。

for (size_t i = 0; i < s4.size(); i++) {
	cout << s4[i] << " ";
}

C++还有一种方法遍历:迭代器 iterator。

首先,iterator是定义在类域中的,必须在string域中使用。

string::iterator it1 = s4.begin();
while (it1 != s4.end()) {
	cout << *it1 << " ";
	it1++;
}

那什么又是begin和end呢?

最重要的概念:所有容器的begin和end都满足左闭右开

begin()返回首字符“指针”,end()返回最后一个有效字符的下一个的“地址”

迭代器就可以看做是类似指针的东西。但是其本质不一定是指针。

我们可以利用typeid来看一下其类型,非常奇怪。

                    

                       (typeid还可以将被typedef的类型的原名称给显式出来)

                          

it1被当作指针一样,想访问其对应的内容就直接解引用。

就对于string而言,  直接使用[ ]下标访问 更方便,但是对于大部分数据结构,iterator更加主流。

上文中的begin和end的用法也都符合所有的容器。


利用范围for遍历(C++11才支持):

                                    

e是赋值拷贝,对e++或者--等操作不会实际影响s4中的字符大小。

     但是范围for的自动循环在编译时其实和迭代器的底层是几乎一样的,所以对于容器的学习主要掌握迭代器版本。


 5.begin等函数的重载

                                      

const string s1("abcdefg");

由匹配规则我们知道,当s1被const修饰时,如果再调用begin,会调用下面那个重载。

      (定义s1使用的const对应的是const_iterator begin() const中后面的那个const,有const修饰和没const修饰的被认为是两种参数类型) 

然后返回一个const类型的iterator,这样返回的iterator就是只读的。

细心的读者可能已经发现了,为什么不是const iterator,而是const_iterator?

const限定的是迭代器所指向的内容,迭代器指向的内容不能修改不代表迭代器本身不能修改。

如果迭代器本身都被限制,得到的迭代器甚至不能++,无法遍历。

               

此处的差距就可以类比于指针的const的不同的修饰:

string :: const_iterator 

                                        

const string :: iterator(这种我们一般不使用),更多使用string :: iterator和string :: const_iterator


还可以通过auto来减少输入量。

          

除了iterator和const_iterator,还有反向迭代器(用得不多):

                          

         

此时对反向迭代器++就是向左走,--就是向右走。他同样有两种:带const的和不带const的。带const的同样不能通过迭代器修改迭代器所指向的参数。

                                    

因为可以用方括号遍历的缘故,string其实很少这样遍历。直接循环即可。

string::reverse_iterator re_it2= s2.rbegin();
while (re_it2 != s2.rend()) {
	cout << *re_it2 << " ";
	re_it2++;
}

一共四种迭代器,在之后的容器中都是同样的作用。


5.string在VS编译器下的大小(了解)

    

第一点,不同的编译器的计算出的大小是不一样的,此处只针对vs2019进行学习:

string是一个字符串数组,按理来说其由 _str  _size  _capacity三部分组成,在x86也就是32位环境下,应该是一个指针 两个int,一共是12。但是其实string中还有一个大小为16的buffer数组,当字符串数量小于16时,直接存在string内,不存在堆中,因此大小为28  


6.将string按照字典序排序

   首先明确,字典序就是ASCII的顺序。

    其次,此处我们需要引入一个新的头文件<algorithm>,并利用其中的算法:sort

sort是一个函数模板,他不是属于容器string的类函数。sort函数的参数是各种类型的迭代器。

原文介绍如下:

Sort elements in range

Sorts the elements in the range [first,last) into ascending order.
The elements are compared using operator< for the first version, and comp for the second.
Equivalent elements are not guaranteed to keep their original relative order  stable_sort

可以直接排序vector等,不过链表暂时不能直接排序。

底层是快速排序,不具有稳定性。

      

传参时,任然要遵守左闭右开:

如果要全部排列,直接使用迭代器的begin和end

如果只排前n个,则直接让begin+n即可,因为类似于数组,begin返回的是0位置。

                                 

                                     (由于0的存才si,begin()+5其实指的是第六个字符)


7.string的插入和修改

7.1 push_back 和 append

首先介绍老朋友:push_back

插入常量字符串使用append

插入常量字符(只插入一个字符)使用push_back

                                     

append有多种重载,风格非常类似构造函数。

因此,还需要再注意常量字符串是const char*的问题。

可以从指定的位置加入指定数量的字符、全部用某个字符去“覆盖式加入”、使用迭代器区间去添加。

如从指定位置开始的指定个数(注意一定使用string类型变量,不要用常量字符串等const char*类型变量):

 同样可能用到npos,在无符号整形中这就是最大的数据。


7.2更常用的添加方法:运算符重载 +=

对于运算符+=的运算符重载:

                                           

更加形象,更易理解。

而且+=后面既可以是单个字符,也可以是字符串


7.3 assign函数

其本质是一种赋值,会先清空本来有的内容

                                        

则s1中原先的内容会被改为111111。

                                 


7.4 重点: insert 与 erase

insert的第一个参数都是与位置相关的,下标或者迭代器。

                                      

但是insert能少用就少用,因为其时间复杂度是O(n),每次插入都需要把被插入位置之后的所有元素平移。

但是不支持只在某个位置只插入一个字符:

这样是不可以的: 

                                              

这样是可以的:

                                           

但是给y加上一个双引号呢?(应该是没问题的)但是必须有支持单个字符的情况,否则无法应对诸多情况(如字符作为变量):

                                        

毕竟字符串不可能完全替代字符的插入。

如下,4的意思是在第四个字符的位置插入(包括0),会将位置4之后的元素都平移。

                     

频繁使用平移插入时需要注意效率。

此处只实现第三种(对应的是方法7)就好,也就是通过迭代器加入对应的区间中的字符串。                                                                                     

不过还是那句话,insert的时间复杂度不低,慎用。

erase同样时间复杂度为O(n),也要慎用。

关于erase,共有三种用法,一种是关于下标的,从pos位置删len个。还有两种是迭代器版本的。

我们观察第一个重载,默认是从0位置开始,删除size_t类npos个数据(很多个),也就是如果什么都不传就会全部删完。 同理,如果还是"is too short",依然是有多少删多少。

长度超了就自动删完,但是下标超了或者迭代器超了,就会报错。下标和迭代器必须合法!

                                   


7.5 replace替换函数 (注意两个迭代器传参时的左闭右开

         

                                   

                 将第五个位置开始的一个元素换成后面的内容(此处为const char*)

对于迭代器版本:i1和i2依然指的是一个左闭右开的空间。

        针对是char* 还是string&的问题 ,之前的解释都没有错,但是只实现一个string版本的就可以了,因为string类型可以用char* 去自动构造,只是在char*和string都存在时,会使用更匹配的那个。

      之前的构造函数中需要注意两种参数的不一样是因为 char*作为参数和string作为参数的功能是不一样的(前者表示前n个,后者表示从下标为n的位置开始的)。这次是一样的,所以只用实现一个。

但是隐式类型转换又会一定程度上较低效率,而c++就是以效率著称的语言.......

比如我们想实现一个功能,将给出的string 类型的s中的所有空格都替换为%20:

string& DeleteSpace(string& s) {
	for (auto it = s.begin(); it != s.end();) {
		if (*it == ' ') {
			s.replace(it, it+1, "%20");
			it += 3;
		}
		else {
			++it;
		}
	}
	return s;
}

如果写成s.replace(it,it,"%20"),就会不停的在第一个空格处加上%20。左闭右开的目的就是从 左边的闭区间开始替换,但是不替换右边开区间那个位置,因此会不停的替换直到溢出。

使用迭代器时(尤其是两个迭代器)都要注意左闭右开的问题。

                                           (replace处也可以直接使用下标版本) 

但是这样效率并不高,

除非替换的内容相互一样长(将三个元素换成另外三个元素)。

否则每次都涉及平移后面的全部内容。

可以用空间换时间的做法:

string QuickDeleteSpace(string& s) {
	string ret;
	for (auto ch : s) {
		if (ch != ' ') {
			ret += ch;
		}else{
			ret += "%20";
		}
	}
	return ret;
}

但是此处的ret是临时变量,不能再返回引用,而应该传值返回。 


8.小试牛刀

917. 仅仅反转字母 - 力扣(LeetCode)

class Solution {
public:
    bool IsLetter(char c){
        return ('a'<=c&&'z'>=c)||
               ('A'<=c&&'Z'>=c);
    }
    string reverseOnlyLetters(string s) {
        size_t left=0,right=s.size()-1;
        while(left<right){
            //让左右两个下标都找到是字母的位置。
            while(!IsLetter(s[left])&&left<right)
                 left++;
            while(!IsLetter(s[right])&&left<right)
                 right--;

            swap(s[left],s[right]);
            left++;
            right--;
        }
        return s;
    }
};

类似于快排中每一次单趟的方法。

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

利用计数统计,建立哈希映射,两次遍历。

new的开辟是利用了初始化的int数组值都为0

初始化的char数组值都为'\0'

class Solution {
public:
    int firstUniqChar(string s) {
        int* arr=new int[26]{0};
        for(auto e : s){
            arr[e-'a']++;
        }
        for(int i=0;i<s.size();i++){
            if(arr[s[i]-'a']==1){
                delete[] arr;
                return i;
            }
        }
        delete[] arr;
        return -1;
    }
};

在vs中,作为内置类型char的数组,delete delete[] free都可以用于释放new出来的数组,但是在leetcode中只能使用delete[] 

125. 验证回文串 - 力扣(LeetCode)


125. 验证回文串 - 力扣(LeetCode)

回文判断都是头尾指针。

此题最大的坑在于:‘0’+32是P,所以直接用32加减的方法不妥。需要加上判断语句。

class Solution {
public:
    bool IsLetterOrNumber(char& ch){
         if(ch>='A' && ch<='Z'){
            ch+=32;
            return true;
         }
         return (ch>='a' && ch<='z')||(ch>='0'&&ch<='9');
    }

    bool isPalindrome(string s) {
          int left=0,right=s.size()-1;
          while(left<right){
            while(left<right && !IsLetterOrNumber(s[left]))
          left++;
           while(left<right && !IsLetterOrNumber(s[right]))
          right--;

          if(s[left]!=s[right]){
             return false;
              }
              left++,right--;
          }
          return true;
    }
};

难点:大数运算之字符串加减

415. 字符串相加 - 力扣(LeetCode)

class Solution {
public:
    string addStrings(string num1, string num2) {
        int pcur1=num1.size()-1;
        int pcur2=num2.size()-1;
        string ans;            
        ans.reserve(max(num1.size(),num2.size()));
        int next=0;
        
        while(pcur1>=0||pcur2>=0){
             int x1= pcur1 >= 0 ? num1[pcur1]-'0' : 0;
             int x2= pcur2 >= 0 ? num2[pcur2]-'0' : 0;
             next=x1+x2+next;
             ans.insert(0,1,next%10+'0');
             next/=10;

             --pcur1,--pcur2;
        }   
        if(next){
            ans.insert(0,1,'1');
        }
        return ans;
    }
};

此处的reverse是用来先调整capacity大小,目的是提升效率,不加这一句也能跑过。但是不能乱使用resize。如果胡乱加大了size空间,ans串中可能有其他意想不到的值

              

我们进一步优化我们的算法,计算此时的时间复杂度:

算时间复杂度时需要联系每个接口来计算,此处消耗最大的就是每一次的insert(不想使用第二个参数也可以使用iterator版本)。如此处的头插复杂度就是n^2.

所以可以使用尾差加逆置(使用algorithm中的reverse,使用方法直接查cpp官网)的方法,复杂度就是O(n)

 

class Solution {
public:
    string addStrings(string num1, string num2) {
        int pcur1=num1.size()-1;
        int pcur2=num2.size()-1;
        string ans;            
        ans.reserve(max(num1.size(),num2.size()));
        int next=0;
        
        while(pcur1>=0||pcur2>=0){
             int x1= pcur1 >= 0 ? num1[pcur1]-'0' : 0;
             int x2= pcur2 >= 0 ? num2[pcur2]-'0' : 0;
             next=x1+x2+next;
             //ans.insert(ans.begin(),next%10+'0');
             ans.push_back(next%10+'0');
             next/=10;

             --pcur1,--pcur2;
        }   
        if(next){
            ans.push_back('1');
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

9.capacity大类

capacity表示当前string实际开出的空间大小,这一点有别于size,size表示的是此时已有字符串所占用空间的大小。

 9.1 max_size(了解)

max_size没什么用,目的就是告诉你理论上最多能开出多大的空间。

                  

实际上是不可能开出这么大空间的(这已经是九百万TB了)


9.2clear

就是全部清空,类似于不传参的erase

9.3扩容

在vs上,因为buffer数组的存在:

第一次严格意义不算扩容,因为刚开始都是存在buffer数组上的。

第一次change之后就开始在堆上存放数据了。

void TestOfCapacity(string& s) {
	size_t sz = s.capacity();
	cout << "the primary size :" << sz << endl;
	for (int i = 200; i >= 0; i--) {
		s.push_back('p');
		if (sz != s.capacity()) {
			sz = s.capacity();
			cout << "now the size of capacity is : " << sz << endl;
		}
	}
}

capacity显式出来的比实际的capacity的空间少一个,会预留一个给\0

在vs2019中:刚开始是二倍扩容,后面是接近一点五倍扩容

在linux中:

明显没有buffer数组 ,并且每次都是2倍扩容。

g++编译器更加直观,vs的编译器封装更加全面,有其自己的想法。

不过无论g++还是vs,capacity都没有计算斜杠零。


9.4 resize和reserve  ★★

reserve(保留,意为保留一定量的空间,区分于逆置reverse)和resize,分别用来管理capacity和size

但是两者的影响范围又稍有不同:

reserve只影响capacity,resize主要目标是影响size,但是因为影响了size所以也会影响capacity。

reserve:

在vs中:

你需要n个空间,他可能开的比n多。

对于reserve缩容,不同的平台有不同的实现(所以不建议使用)。

并且vs中的string有一个buffer数组,所以再怎么变都不会小于15 (真实大小是16,还有一个位置留给\0)

reserve的真正作用是在知道大致需要多少空间时,节省扩容的动作

                                 

提升了效率。

不过依然有一个问题:

                                              

这样操作会越界,编译器会报错。

operator []只能访问size以内的部分,而reserve不会改变size,所以超过size的部分依然不能被使用。因此在reserve之后是不能实现从尾部开始使用的,任然只能使用原size以内的部分。

resize:

如果不给值,默认值就是ASCII值为0的‘\0’

在已有空间上使用resize:

不会改变原空间内容,但是会同时对capacity和size造成影响。


9.5 shrink_to_fit

该函数是专门用来缩空间的。

但是空间在底层是不能分段释放的

所以其本质是开一个更小的空间,把已有内容拷贝过去。本质是时间换空间的做法,代价较大。

//调用方法:
str.shrink_to_fit();

9.6 at

功能与重载的[]类似,区别在于检查方面:[ ]用assert检查,会直接报错,而at可以在抛异常时捕获。 

但是只有下标版本,没有迭代器版本。


9.7 c_str

string是可以由char*作为参数来构造的,但是char* 不能通过string来构造。

作用是将string转换到c语言中的标准字符串。

                          

目的是与只能编C的接口兼容,如fopen不能使用str,只能使用char* , 此时就能发挥c_str的作用。

string file("string_test.cpp");
FILE* pf = fopen(file.c_str(), "r");

10.find

由缺省参数为0可知,find函数默认都是从头开始找。给了数字之后就可以从数字对应的位置开始找。

可以找单个字符也可以找一个句子。

若想从尾部开始找,可使用refind。
 

比如我们想取出一个后缀名suffix:

此时又涉及一个操作:substr

 将pos位置开始长度为len的内容拷贝到一个新生成的字符串中去。

记住substr的两个参数都是整数即可,前一个是位置,后一个是长度。

string file("string_test.cpp");
size_t pos=file.find('.');
string suffix = file.substr(pos, file.size() - 1);

cout << suffix << endl;

 再比如,

希望用find分别得到一个网址的协议,域名,端口 。

网址:

https://leetcode.cn/problems/add-strings/description/
string url("https://leetcode.cn/problems/add-strings/description/");
size_t pos1 = url.find(':',0);
string url1 = url.substr(0, pos1 - 0);
size_t pos2 = url.find('/', pos1 + 3);//从leetcode的l处开始寻找
string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));
string url3 = url.substr(pos2 + 1, url.size() - (pos2 + 1));

cout << url1 << endl << url2 << endl << url3 << endl;

此处选取pos时依然利用左闭右开的好处:直接做减法就能获得长度len


find_first_of 四兄弟 (类似于strtok) ,了解即可

使用一个string类型对象调用该函数时,他能正向(或逆向)找出 (非)你给出的参数中第一个与string对象中所包含元素一样的位置。所以最坏时间复杂度是两个串的长度之积,O(m*n)

官网解释:

     Searches the string for the first character that matches any of the characters specified in its arguments.(功能)
     When pos is specified, the search only includes characters at or after position pos, ignoring any possible occurrences before pos.(第二个参数pos的作用)
     Notice that it is enough for one single character of the sequence to match (not all of them). See string::find for a function that matches entire sequences.(与find区分)

find_last_of的实际作用:

比如在一个项目中,需要处理不同系统下的多个文件路径,但是由于Linux中的文件分隔符是右斜杠/,windows中的文件分隔符是左斜杠\  ,如果使用find函数查找这两个·会去找这两个一起出现的字符串(当然找不到),此时就需要find_first_of来发挥作用

void SplitFilename(const std::string& str)
{
	std::cout << "Splitting: " << str << '\n';
	std::size_t found = str.find_last_of("/\\");
	std::cout << " path: " << str.substr(0, found) << '\n';
	std::cout << " file: " << str.substr(found + 1) << '\n';
}

int main()
{
	std::string str1("/usr/bin/man");
	std::string str2("c:\\windows\\winhelp.exe");

	cout << str2 << endl;

	SplitFilename(str1);
	SplitFilename(str2);

	return 0;
}

左斜杠\\需要多加一个表示转义字符,而右斜杠/不用,在windows中的操作要注意这个。

转义字符仍然只是一个字符。

11. string相关的部分外部函数

11.1 operator +

为什么+不能写成成员函数,而是全局重载:

                                  

原因就在于最后这种,无法通过成员函数实现。

          

除了char* + char*没有实现,其他都能直接加。 

11.2 operator <  > ==

类似于strcmp,按照字典序比较两个字符串相应位置的ASCII码值。

成立返回1,不成立返回0。

流插入的优先级较高,需要加括号。

                             

11.3  getline

getline:

想获取一行中包含空格的串,不能直接使用流提取,需要使用getline

                                        

字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)

#include <iostream>
using namespace std;

int main() {
   string s;
   getline(cin,s);
   int pos=s.rfind(' ');
   cout<<s.size()-(pos+1);
   return 0;
}

持续获取:

你如果一次输入按下三次空格再回车,其实这个循环就走了三次,空格作为分隔符,将之后的内容都存在缓存区,cout的时候会输出全部,再等待下次输出。 

 使用 ctrl+Z+回车 可以终止这个程序。


11.4 字符串和int类型的转换

       to_string和stoi(类比于C语言的atoi itoa    a  to  i  ASCII到int      i   to   a    int 到ASCII)

但是stoi(string to int)不能处理大数运算,串长之后就会爆,放不下。

            


12.编码表UNICODE

string的底层是如何装汉字的呢?

计算机中的编码除了ASCII编码,还有万国码(UNICODE)。

万国码支持ASCII编码,并以此为基础增加了世界上绝大多数国家的语言文字。

大部分汉字编入两个字节的部分,部分生僻的编入3字节或者4字节

因此有可能一个字节表示,有可能两个字节表示,有可能三个字节去表示。

 string默认支持UTF8,还有默认支持UTF16和UTF32、以及双字符的wstring

所有的string都是以basic_string为模版实现的。

还有一种库叫GBK库,由于万国码在中文一些方面优化的不够好,这一套GBK作为国标也被很多系统支持,如windows等,更适合中国宝宝的体质。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/609183.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

加密“发射台”:未来通信的新模式

随着区块链技术的飞速发展&#xff0c;加密“发射台”作为一种新兴的安全通信工具&#xff0c;正逐渐受到关注。本文将从专业角度深入探讨加密“发射台”的概念、原理、应用场景及其未来发展趋势&#xff0c;以期为读者提供有深度和逻辑性的思考。 一、加密“发射台”的概念与…

开源项目介绍-02 Aubio【1】环境配置和使用 @ Ubuntu + Pycharm + Python

前言&#xff1a; aubio 是一组算法和工具&#xff0c;用于标记和变换音乐和声音。它扫描或监听音频信号&#xff0c;并尝试识别音乐事件。例如&#xff0c;当鼓被击打时&#xff0c;它能检测到音符的频率&#xff0c;或者一个有节奏的旋律的节拍是多少。 aubio 的功能包括&a…

java 文件表创建及前后端使用

表结构task_file 前端具体到业务表单 <el-form-item label"任务附件" prop"taskAttachment"><el-upload ref"upload" accept".jpg, .png, .txt, .xlsx, .doc, .docx, .xls, .pdf, .zip, .rar":action"upload.url" …

C语言例题39、输入一个正整数,将其反方向逆序输出

#include <stdio.h>void main() {int x;int ge; //个位int result 0;printf("请输入一个正整数&#xff1a;");scanf("%d", &x);while (x > 0) {//解题原理ge x % 10;//每次分解取得个位的数字result result * 10 ge;//个十百千万顺序向左…

限购仅剩6地,透过房价地图看楼市行情!

同一天&#xff0c;两地取消限购&#xff01; 5月9日&#xff0c;继杭州取消限购之后&#xff0c;西安也宣布全面取消住房限购&#xff01; 现在&#xff0c;我们透过几幅楼市数据的分布地图&#xff0c;来看看5月的楼市行情&#xff01; 楼市限购&#xff0c;仅剩6地&#…

YOLOv9改进策略 :一种新颖的通用倒瓶颈(UIB)搜索块助力检测| 轻量化之王MobileNetV4

💡💡💡创新点:轻量化之王MobileNetV4 开源 | Top-1 精度 87%,手机推理速度 3.8ms,原地起飞! 最主要创新:引入了通用倒瓶颈(UIB)搜索块,这是一个统一且灵活的结构,它融合了倒瓶颈(IB)、ConvNext、前馈网络(FFN)以及一种新颖的额外深度可分(ExtraDW)变体技…

【iOS】事件传递与响应机制

文章目录 前言事件UIEvent一、事件传递遍历顺序 二、手势识别三、响应机制UIResponder&#xff08;响应者&#xff09;响应者链 四、相关应用扩大button点击范围穿透事件 总结 前言 提到响应者链与事件传递&#xff0c;如果看过其他人的博客&#xff0c;经常能看到这经典的三张…

《探索信息技术中心一体化系统架构:提升企业数字化转型的效率与能力》

在当今数字化时代&#xff0c;信息技术中心&#xff08;IT Center&#xff09;扮演着至关重要的角色&#xff0c;它不仅是企业的技术支撑&#xff0c;更是推动数字化转型的引擎。为了更好地应对日益复杂的业务需求和技术挑战&#xff0c;许多企业开始探索一体化系统架构&#x…

引用数据类型 栈内存 堆内存

let m { a: 10, b: 20 }; let n m; n.a 15; console.log(m.a) // 15 原因&#xff1a;基本数据类型存储在栈内存中&#xff0c;引用数据类型存储在堆内存中 &#xff0c;引用数据类型存储在堆内存中会在栈内存中创建一个指针&#xff0c;栈内存中的这个指针指向堆内存中的地…

英语学习笔记9——How are you today?

How are you today? 你好吗&#xff1f; 词汇 Vocabulary well adj. 好的 n. 井 fine adj. 美好的 两个方面&#xff1a;天气、身体。 搭配&#xff1a;a fine day 晴朗的一天    It’s a fine day today. 今天很晴朗。 good adj. 好的 口语偏多 搭配&#xff1a;Good jo…

基于光纤技术的新能源汽车电池安全监测--FOM²系统

为什么要进行动力电池包的温度监测&#xff1f; 新能源电动汽车的动力电池包的工作温度&#xff0c;不仅会影响电池包性能&#xff0c;而且直接关系到车辆安全。时有发生的新能源汽车电池包起火事件&#xff0c;对电池包、冷却系统以及电池管理系统&#xff08;BMS&#xff09…

初阶C语言(8) - 实用的调试技巧

1. 什么是bug? bug 是计算机领域专业术语&#xff0c;是计算机在硬件、软件、协议和系统安全策略上存在的缺陷&#xff0c;攻击者能够在未授权情况下访问的危害&#xff0c;世界最早的一批程序设计师之一&#xff0c;美国的葛丽丝霍波在调试设备时出现故障&#xff0c;拆开继电…

Java基础教程 - 6 方法

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 6 方法 方法也可以称之为…

算法day03

第一题 179. 查找总价格为目标值的两个商品 本题采用前后指针和单调性规律可解&#xff1b; 解题思路如下&#xff1a; 1、设置前后指针 2、前后指针所指的两数之和大于目标数&#xff0c;右指针左移&#xff1b; 前后指针所指的两数之和小于目标数&#xff0c;左指针右移&…

软件工程复习之软件定义时期

1.什么是软件&#xff1f; 答&#xff1a;软件是程序&#xff0c;数据和文档的集合。 程序是完成指定功能的计算机可执行的指令序列。 数据是程序进行信息处理的数据结构。 文档是开发&#xff0c;使用&#xff0c;维护的图文资料。 2.软件有何特点&#xff1f; 答&#…

2024年小沙弥小视频,轻松吸引中老年观众,上手简单,轻松月入了万

利用人工智能工具制作小沙弥主题的抖音内容&#xff0c;已成为网络赚钱的新途径。这个项目主要吸引中老年人群体&#xff0c;尤其是对智慧和人生哲理感兴趣的观众。小沙弥以其温馨且启发性的内容&#xff0c;引起中老年用户的共鸣&#xff0c;为他们提供心灵慰藉。 项 目 地 …

上传自己的项目到PyPI

准备工作 已注册pypi账号pypi账号已经配置了双重验证pypi账号的token令牌&#xff08;最后上传到pypi需要这个&#xff09;pip install twine&#xff08;上传需要用到的工具&#xff09; 操作步骤 1、准备好工程2、编写setup.py3、开始上传 大功告成 在pypi查看自己包的…

Linux—— 任务规划、SELinux、ACL、磁盘介绍

任务规划&#xff1a; 未来任务的一次性调度 atatqatrm服务&#xff1a; atd周期性任务的调度 crontab 所有用户的任务列表都以文件的形式存在于系统 /var/spool/cron/用户名-l -e-u-r时间 需要执行的任务服务&#xff1a; crond系统维护任务&#xff1a; yum软件仓库缓存的…

MySQL数据库基础(数据库操作,常用数据类型,表的操作)

MySQL数据库基础&#xff08;数据库操作&#xff0c;常用数据类型&#xff0c;表的操作&#xff09; 前言 数据库的操作1.显示当前数据库2.创建数据库3.使用数据库4.删除数据库 常用数据类型1.数值类型2.字符串类型3.日期类型 表的操作1.查看表结构2.创建表3.删除表 总结 前言 …

5. 分布式链路追踪TracingFilter改造增强设计

前言 在4. 分布式链路追踪客户端工具包Starter设计一文中&#xff0c;我们实现了基础的Starter包&#xff0c;里面提供了我们自己定义的Servlet过滤器和RestTemplate拦截器&#xff0c;其中Servlet过滤器叫做HoneyTracingFilter&#xff0c;仅提供了提取SpanContext&#xff0…