目錄
前言
一、string類
二、初始化
1、無參或帶參
2、用字符串變量初始化
3、用字符串初始化
4、指定數(shù)量字符
三、容量操作
1、size
2、push_back
3、append
4、+=運算符
5、vs下string的結構
6、g++下string的結構
7、reserve
8、resize
四、迭代器
1、正向迭代器
2、反向迭代器?
3、const迭代器 (正向反向)
五、OJ練習
反轉字符
找出字符串中出現(xiàn)一次的字符?
前言
string類模板如下,為什么會有好幾個呢?
這些不同的string類模板是為了處理不同的字符編碼和字符集。每個模板都專門用于處理特定類型的字符數(shù)據(jù)。
-
std::string:這是最常見的string類模板,用于處理ASCII字符集。它使用單字節(jié)字符表示,適用于大多數(shù)常規(guī)字符串操作。
-
std::wstring:這是寬字符版本的string類模板,用于處理Unicode字符。它使用wchar_t類型來表示字符,適用于需要處理多語言字符集的情況。
-
std::u16string:這是用于處理UTF-16編碼的字符串的模板。UTF-16使用16位編碼表示字符,適用于處理較大的字符集,如大部分Unicode字符。
-
std::u32string:這是用于處理UTF-32編碼的字符串的模板。UTF-32使用32位編碼表示字符,適用于處理包含所有Unicode字符的字符集。
這些不同的string類模板提供了對不同字符編碼和字符集的支持,以便在處理不同類型的文本數(shù)據(jù)時能夠正確地表示和操作字符。通過選擇適當?shù)膕tring類模板,可以確保在不同的應用場景中正確處理和操作字符數(shù)據(jù)。
?
我們先來了解一下計算機存儲字符的方式:?
計算機中都是二進制形式,無法直接存儲字母和符號,這時就需要一個映射表,ASCll就誕生了。
?為了可以在計算機上顯示多個國家的文字,unicode誕生了
?統(tǒng)一碼(Unicode),也叫萬國碼、單一碼,由統(tǒng)一碼聯(lián)盟開發(fā),是計算機科學領域里的一項業(yè)界標準,包括字符集、編碼方案等。
統(tǒng)一碼是為了解決傳統(tǒng)的字符編碼方案的局限而產生的,它為每種語言中的每個字符設定了統(tǒng)一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求。
Unicode字符編碼方案分為UTF-8、UTF-16和UTF-32,它們被設計用于在計算機系統(tǒng)中表示和處理不同范圍的Unicode字符。
-
UTF-8:UTF-8是一種變長編碼方案,它使用1到4個字節(jié)來表示不同的Unicode字符。它是最常用的Unicode編碼方案之一,因為它可以兼容ASCII字符集,并且在表示常見字符時比較節(jié)省空間。UTF-8適用于在存儲和傳輸文本數(shù)據(jù)時節(jié)省空間的情況,特別是在互聯(lián)網和計算機網絡中廣泛使用。
-
UTF-16:UTF-16是一種定長或變長編碼方案,它使用16位編碼來表示Unicode字符。對于大部分常見的Unicode字符,UTF-16使用16位編碼表示,但對于一些較少使用的字符,它需要使用兩個16位編碼來表示。UTF-16適用于需要處理較大字符集的情況,如多語言文本處理和國際化應用。
-
UTF-32:UTF-32是一種定長編碼方案,它使用32位編碼來表示Unicode字符。每個Unicode字符都使用32位編碼表示,無論字符是否常見。UTF-32適用于需要處理包含所有Unicode字符的字符集的情況,如某些特定領域的文本處理和字符級操作。
這些不同的Unicode編碼方案提供了不同的權衡和適用性,根據(jù)具體的需求和應用場景,可以選擇適當?shù)木幋a方案來表示和處理Unicode字符。
其中,UTF-8使用的最多 。
我們中國也有針對漢字的GBK編碼,對一些生僻字提供了支持。
一、string類
文檔介紹?
- 字符串是表示字符序列的類,標準的字符串類提供了對此類對象的支持,其接口類似于標準字符容器的接口,但添加了專門用于操作單字節(jié)字符字符串的設計特性。
- string類是使用char(即作為它的字符類型,使用它的默認char_traits和分配器類型(關于模板的更多信息,請參閱basic_string)。
- string類是basic_string模板類的一個實例,它使用char來實例化basic_string模板類,并用char_traits和allocator作為basic_string的默認參數(shù)(根于更多的模板信息請參考basic_string)。
- 注意,這個類獨立于所使用的編碼來處理字節(jié):如果用來處理多字節(jié)或變長字符(如UTF-8)的序列,這個類的所有成員(如長度或大小)以及它的迭代器,將仍然按照字節(jié)(而不是實際編碼的字符)來操作。
- string是表示字符串的字符串類
- 該類的接口與常規(guī)容器的接口基本相同,再添加了一些專門用來操作string的常規(guī)操作。
- string在底層實際是:basic_string模板類的別名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字節(jié)或者變長字符的序列。
在使用string類時,必須包含#include頭文件string以及using namespace std;
二、初始化
-
默認構造函數(shù)?
string()
:創(chuàng)建一個空字符串對象。 -
復制構造函數(shù)?
string(const string& str)
:通過復制另一個字符串對象?str
?來創(chuàng)建一個新的字符串對象。 -
子字符串構造函數(shù)?
string(const string& str, size_t pos, size_t len = npos)
:從字符串對象?str
?的指定位置?pos
?開始,創(chuàng)建一個新的字符串對象,長度為?len
。如果未提供?len
?參數(shù),默認創(chuàng)建到字符串末尾的子字符串。 -
從C-String構造函數(shù)?
string(const char* s)
:從以空字符結尾的C字符串?s
?創(chuàng)建一個新的字符串對象。 -
從序列構造函數(shù)?
string(const char* s, size_t n)
:從C字符串?s
?的前?n
?個字符創(chuàng)建一個新的字符串對象。 -
填充構造函數(shù)?
string(size_t n, char c)
:創(chuàng)建一個包含?n
?個字符?c
?的新字符串對象。 -
范圍構造函數(shù)?
template <class InputIterator> string(InputIterator first, InputIterator last)
:通過迭代器范圍?[first, last)
?中的字符創(chuàng)建一個新的字符串對象。這個構造函數(shù)可以接受不同類型的迭代器,例如指針、容器的迭代器等。
1、無參或帶參
既可以無參初始化, 也可以帶參初始化。
int main()
{
string s1;
string s2("hello world");
string s3 = "hello";
return 0;
}
?我們也可以通過[ ]操作符訪問字符串某個位置。?
#include<string>
int main()
{
string s2("hello world");
for (size_t i = 0; i < s2.size(); ++i) {
s2[i]++;
}
cout << s2 << endl;
return 0;
}
我們可以通過流插入運算符<<輸出string類對象。?
這是因為string類中對流插入運算符<<進行了重載,流提取也進行了重載。
int main()
{
string s2;
cin >> s2;
for (size_t i = 0; i < s2.size(); ++i) {
s2[i]++;
}
cout << s2 << endl;
return 0;
}
?
2、用字符串變量初始化
用一個字符的指定位置開始指定字符個數(shù)為另一個字符初始化。
int main()
{
string s3 = "hello";
string s4(s3, 2, 3);
cout << s4 << endl;
return 0;
}
如果取字符的個數(shù)超過字符總長度,則取到末尾即可。?
int main()
{
string s3 = "hello";
string s4(s3, 2, 3);
cout << s4 << endl;
string s5(s3, 2, 10);
cout << s5 << endl;
return 0;
}
?
第三個參數(shù)取字符整數(shù)可以缺省,那么從指定位置開始取到末尾結束。?
int main()
{
string s3 = "hello";
string s4(s3, 2, 3);
cout << s4 << endl;
string s5(s3, 2);
cout << s5 << endl;
return 0;
}
3、用字符串初始化
也可以把要賦值的字符串直接放在第一個參數(shù)位置,第二個參數(shù)為賦值字符個數(shù)。?
int main()
{
string s7("hello world", 5);
cout << s7 << endl;
string s8("hello world", 5 , 6);
cout << s8 << endl;
return 0;
}
- 第一個構造函數(shù)中,除字符串如果只有一個參數(shù),則默認從字符串第一個字符開始,賦值第一個參數(shù)大小長度。
- 第二個構造函數(shù)中,除字符串如果有兩個參數(shù),第一個參數(shù)為指定起始位置,第二個參數(shù)為指定的初始化長度,長度大于實際字符串的長度,則截斷為實際字符串的長度。
?
4、指定數(shù)量字符
int main()
{
string s9(10, '$');
cout << s9 << endl;
return 0;
}
三、容量操作
1、size
size()與length()底層實現(xiàn)原理完全相同,都用于獲取字符串有效字符長度,引入size()的原因是為了與其他容器的接口保持一致,一般情況下基本都是用size()。
int main()
{
string s1("hello world");
cout << s1.size() << endl;
cout << s1.length() << endl;
return 0;
}
下面兩個了解即可 :
max_size()
函數(shù)返回一個無符號整數(shù),表示字符串對象可以容納的最大字符數(shù)。這個值通常取決于系統(tǒng)的限制,因此可能會因操作系統(tǒng)和編譯器而異。capacity()
函數(shù)返回一個無符號整數(shù),表示字符串對象當前分配的內存空間大小。這個值可能大于字符串實際包含的字符數(shù),因為字符串類通常會預留一些額外的空間以便進行擴展。?
int main()
{
string s1("hello world");
cout << s1.max_size() << endl;
cout << s1.capacity() << endl;
return 0;
}
?64位下輸出結果:
2、push_back
添加單個字符到字符串末尾?
int main()
{
string s1("hello");
s1.push_back(' ');
s1.push_back('!');
cout << s1 << endl;
return 0;
}
3、append
我們還可以使用append在字符串末尾添加單個字符或字符串 ,這種是最常用的形式。
int main()
{
string s1("hello");
s1.push_back(' ');
s1.push_back('!');
cout << s1 << endl;
s1.append("world");
cout << s1 << endl;
}
?
4、+=運算符
我們還可以使用+=運算符在字符串末尾添加字符或字符串,+=底層是調用push_back或append,在string尾部追加字符時,s.push_back(c) / s.append(1, c) / s += 'c'三種的實現(xiàn)方式差不多,一般情況下string類的+=操作用的比較多,+=操作不僅可以連接單個字符,還可以連接字符串。
int main()
{
string s1("hello");
s1 += ' ';
s1 += '!';
s1 += "world";
cout << s1 << endl;
}
?
5、vs下string的結構
先看下面代碼:?
int main()
{
size_t sz = s.capacity();
cout << "making s grow:\n";
cout << "capacity changed: " << sz << '\n';
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
上述演示了如何使用push_back()
函數(shù)向字符串對象中添加字符,并觀察字符串的容量變化
- 首先,在
main()
函數(shù)中聲明了一個size_t
類型的變量sz
,并將其初始化為字符串對象s
的容量(假設s
是之前已經定義的字符串對象)。 - 然后,通過使用
cout
對象和<<
運算符,輸出一條提示信息"making s grow:"。 - 接下來,使用一個循環(huán),從0到99,逐個向字符串對象
s
中添加字符'c'。在每次添加字符之后,通過比較之前記錄的容量sz
和當前字符串對象s
的容量,來判斷容量是否發(fā)生了變化。 - 如果容量發(fā)生了變化,將新的容量值賦給
sz
,并使用cout
對象和<<
運算符輸出一條提示信息"capacity changed:",以及新的容量值。
這里輸出的容量不包括 \0,也就是實際容量要再加1.
在監(jiān)視中可以看到,實際上將字符存入了_Buf數(shù)組中。
????????在C++的實現(xiàn)中,std::string
類通常使用兩個數(shù)組來存儲字符串的字符。當字符串的長度小于等于15個字符時(第十六個為\0),字符串的字符被存儲在一個名為_Buf
的內部固定大小數(shù)組中,該數(shù)組長度為15。這樣可以避免動態(tài)內存分配,提高性能。
????????當字符串的長度超過15個字符時,字符串的字符將被存儲在一個名為_Ptr
的動態(tài)分配的數(shù)組中,該數(shù)組的長度將根據(jù)需要進行動態(tài)調整。這樣可以容納更長的字符串,并且可以根據(jù)需要動態(tài)分配內存。
????????這種設計可以在字符串較短時節(jié)省內存,并在字符串較長時提供足夠的存儲空間。具體的實現(xiàn)可能會因編譯器和標準庫的不同而有所差異,但這種區(qū)分短字符串和長字符串的策略是常見的優(yōu)化技術之一
vs下string的結構:string總共占28個字節(jié),內部結構稍微復雜一點,先是有一個聯(lián)合體,聯(lián)合體用來定string中字符串的存儲空間:
- 當字符串長度小于等于15時,使用內部固定的字符數(shù)組來存放
- 當字符串長度大于等于16時,從堆上開辟空間
union _Bxty
{ // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
- 這種設計也是有一定道理的,大多數(shù)情況下字符串的長度都小于16,那string對象創(chuàng)建好之后,內部已經有了16個字符數(shù)組的固定空間,不需要通過堆創(chuàng)建,效率高。
- 其次:還有一個size_t字段保存字符串長度,一個size_t字段保存從堆上開辟空間總的容量
- 最后:還有一個指針做一些其他事情。
- 故總共占16+4+4+4=28個字節(jié)。
輸出查看一下string類對象s的大小?
string s;
cout << sizeof(s) << endl;
?可以看到是28,那么它在堆上起始就是大小32。
觀察擴容情況可以發(fā)現(xiàn)從大小32開始,每次1.5倍擴容。?
?
6、g++下string的結構
- 空間總大小
- 字符串有效長度
- 引用計數(shù)
struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_word _M_refcount;
};
指向堆空間的指針,用來存儲字符串。
7、reserve
在已知所需空間大小時,我們可以使用reserve提前開辟空間,減少擴容,提高效率。?
?
使用reverse一次性開辟100個字符空間。?
int main()
{
string s;
s.reserve(100);
size_t sz = s.capacity();
cout << "making s grow:\n";
cout << "capacity changed: " << sz << '\n';
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
這時就不需要一次次擴容了。?
?對string操作時,如果能夠大概預估到放多少字符,可以先通過reserve把空間預留好。
8、resize
resize將字符串大小調整為n個字符的長度。
- 如果n小于當前字符串長度,則將當前值縮短到前n個字符,刪除第n個字符以外的字符。
- 如果n大于當前字符串長度,則通過在末尾插入盡可能多的字符0來擴展當前內容,以達到n的大小。
- 如果指定了填充字符c,則新元素被初始化為c的副本,否則,它們是值初始化的字符(空字符)。
int main()
{
// 擴容
string s1("hello world");
s1.reserve(100);
cout << s1.size() << endl;
cout << s1.capacity() << endl;
// 擴容+初始化
string s2("hello world");
s2.resize(100);
cout << s2.size() << endl;
cout << s2.capacity() << endl;
return 0;
}
resize(size_t n) 與 resize(size_t n, char c)都是將字符串中有效字符個數(shù)改變到n個,不同的是當字符個數(shù)增多時:resize(n)用0來填充多出的元素空間,resize(size_t n, char c)用字符c來填充多出的元素空間。注意:resize在改變元素個數(shù)時,如果是將元素個數(shù)增多,可能會改變底層容量的大小,如果是將元素個數(shù)減少,底層空間總大小不變。
reserve(size_t res_arg=0):為string預留空間,不改變有效元素個數(shù),當reserve的參數(shù)小于string的底層空間總大小時,reserve不會改變容量大小。
四、迭代器
1、正向迭代器
begin指向字符串第一個字符,end指向字符串最后一個字符的后一個位置,功能和指針類似。
int main()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end()) {
cout << *it << " ";
++it;
}
return 0;
}
?
范圍for底層就是調用迭代器實現(xiàn)的。?
int main()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end()) {
cout << *it << " ";
++it;
}
cout << endl;
for (auto ch : s1) {
cout << ch << " ";
}
cout << endl;
}
?
2、反向迭代器?
rbegin指向字符串最后一個字符,rend指向字符串的第一個字符的前一個位置。?
?
int main()
{
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend()) {
cout << *rit << " ";
++rit;
}
cout << endl;
return 0;
}
3、const迭代器 (正向反向)
? const正向反向迭代器只能遍歷和讀數(shù)據(jù)。
int main()
{
string s1("hello world");
string::const_iterator it = s1.begin();
while (it != s1.end()) {
cout << *it << " ";
++it;
}
cout << endl;
string::const_reverse_iterator rit = s1.rbegin();
while (rit != s1.rend()) {
cout << *rit << " ";
++rit;
}
cout << endl;
cout << s1 << endl;
return 0;
}
這時可以借助auto自動推導類型。?
int main()
{
//string::const_iterator it = s1.begin();
auto it = s1.begin();
while (it != s1.end()) {
cout << *it << " ";
++it;
}
cout << endl;
//string::const_reverse_iterator rit = s1.rbegin();
auto rit = s1.rbegin();
while (rit != s1.rend()) {
cout << *rit << " ";
++rit;
}
cout << endl;
return 0;
}
?
五、OJ練習
反轉字符
917. 僅僅反轉字母 - 力扣(LeetCode)
采用快速排序的思路。?
class Solution {
public:
string reverseOnlyLetters(string s) {
size_t begin = 0, end=s.size()-1;
while(begin<end){
while(begin<end&&!isalpha(s[begin]))
++begin;
while(begin<end&&!isalpha(s[end]))
--end;
swap(s[begin],s[end]);
++begin;
--end;
}
return s;
}
};
找出字符串中出現(xiàn)一次的字符?
?387. 字符串中的第一個唯一字符 - 力扣(LeetCode)文章來源:http://www.zghlxwxcb.cn/news/detail-761874.html
采用計數(shù)排序的思路。?文章來源地址http://www.zghlxwxcb.cn/news/detail-761874.html
class Solution {
public:
int firstUniqChar(string s) {
int count[26]={0};
for(auto ch:s){
count[ch-'a']++;
}
for(int i=0;i<s.size();i++){
if(count[s[i]-'a']==1)
return i;
}
return -1;
}
};
到了這里,關于C++ string類(1)—初始化、容量操作、迭代器的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!