天天看點

C++:标準庫類型string

資料來源:《C++ Primer》、《C++ Primer Plus》、部落格随筆
           

1、标準庫類型string

  • string是什麼??其實就是可以變長的字元序列,伸縮自如的string首先需要包含頭檔案<string>才能聽令你的指揮
  • string的使用過程中,由于它跟cin、cout一樣存在與命名空間當中,是以必須std::string,或者在全局當中直接聲明:using std::string;或者using namespace std;
  • 在工作過程中我們要盡可能不去使用using namespace std;這種習慣,由于很多函數都在全局命名空間namespace中,我們寫函數偶爾單詞會跟裡面的函數重複,這就導緻發生一些沖突性的錯誤

(1)定義和初始化string對象:直接初始化和拷貝初始化

直接初始化簡單來說是一個不含=的式子,而拷貝初始化就是将=的右側的初始值拷貝到建立的對象去。

#include<iostream>
#include<string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
	string s1;				//預設初始化,s1是一個空串
	string s2(s1);			//直接将s2初始化為s1的内容
	string s3 = s1;			//拷貝s1的内容賦給s3

	string s4;
	s4 = "hello world";			
	string s5("value");		 //直接将s4初始化為value
	//string s6(n, 'c');	 //直接将s5初始化為連續的n個字元c
	string s7(5, 'c');		 //輸出ccccc
	string s8 = string(5, 'c');
	//s8等價于建立了string temp(5'c');
	//再把temp的内容拷貝給s8,這種方式沒有什麼意義
	string s9(s4, 6);		//将s4第六個字元後的内容賦給s9,空字元也算一個字元
	string s10(s4,6,3);		//将s4第六個字元後開始的内容最多為3個字元的内容賦給s10
	string s11(s4.begin(), s4.begin() + 5); //将s4開始到s4第五個字元的内容賦給s11
	cout << " s1 : "<<s1 << endl;
	cout << " s2 : " << s2 << endl;
	cout << " s3 : " << s3 << endl;
	cout << " s4 : " << s4 << endl;
	cout << " s5 : " << s5 << endl;
	
	cout << " s7 : " << s7 << endl;
	cout << " s8 : " << s8 << endl;
	cout << " s9 : " << s9 << endl;
	cout << " s10 : " << s10 << endl;
	cout << " s11 : " << s11 << endl;
	system("pause");
	return 0;
}
           

運作結果:

C++:标準庫類型string

(2)讀寫string對象

當用cin輸入一串字元時,string對象會自動忽略開頭的空白(空格符,換行符,制表符等),然後從第一個真正的字元開始讀起,直到遇到下一處空白結束。

string str;		
	cin >> str;				
	cout << str << endl;
           

輸入的内容為(雙引号不為輸入内容)“    輸入空格    空格    ”,很顯然以下的結果忽視了開頭空格與字元後的空格内容。

結果為:

C++:标準庫類型string
解決方案1:使其不忽略所有字元(除了空白)
string str1,str2;		
	cin >> str1 >> str2;
	cout << str1<<str2 << endl;
           

輸出的結果依舊忽視的空白,但是将str1和str2輸出

C++:标準庫類型string
解決方案2:隻設定一個str,反複讀取,逐個輸出字元串,每個字元串後面緊跟換行
string str;
	while(cin >> str)
		cout << str << endl;
           

輸入内容後的結果依舊忽略了空白,但是輸入的所有字元串能輸出并且換行

C++:标準庫類型string
解決方案3:使用getline()函數讀取整一行
string str;
	while (getline(cin, str))
		cout << str << endl;
           

getling函數的一項功能就是從第一個字元開始讀取,直至輸入換行符,再将内容存入到string對象(對象不含有換行符),如果隻輸入換行符(回車)那麼那個string對象輸出為空

C++:标準庫類型string

(3)string特性描述

bool empty() const;    根據string對象是否為空傳回一個對應布爾值,為空傳回true,反之為false
int size() const;          傳回string對象的字元個數,可以用來限制字元串
int max_size() const; 傳回string對象中可存放的最大字元串的長度
int capacity() const;   傳回目前不必增加記憶體即可存放的元素個數
int length() const;       傳回目前字元串的長度
 void resize(int len, char c);     把字元串目前大小設定為len, 多去少補, 多出的字元c填充不足部分
#include<iostream>
#include<string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
	string str("C++ ");
	if(str.empty())		//為空傳回1,反之傳回0
        {
	   cout << "str is NULL." << endl;
	else
	   cout << "str is not NULL." << endl;
        }

	str += "資料結構";
	cout << "str is " << str << endl;
	cout << "str's size is " << str.size() << endl;				//傳回目前字元串大小
	cout << "str's capacity is " << str.capacity() << endl;		//傳回目前不增加記憶體時的容量
	cout << "str's max size is " << str.max_size() << endl;		//傳回最大字元串長度
	cout << "str's length is " << str.length() << endl;			//傳回目前字元串長度

	str.resize(20, 'n');					//把目前字元串設定大小為20,用n補充其餘部分
	cout << "str is " << str << endl;
	str.resize(5);							//大小設定為5
	cout << "str is " << str << endl;
		
	system("pause");
	return 0;
}
           

輸出其結果:我們知道,字母跟英文符号占1個位元組,中文字元占2個位元組,最後一個列印的大小是5,但是C++ 後面多了空格含4個位元組,與中文字元算下去6個位元組,是以中文的數不會列印出來,但是那消失的2個位元組去哪了,我也太懂這原理

C++:标準庫類型string

(3)字面值與string對象相加,兩個string對象相加

string相加的時候注意加号兩側必須要有一個對象是string,不然為錯誤的無效string相加

string s1 = "hi, ";    //逗号後面添加了空格
string s2 = "badby.";
string s3 = s1 + s2;
cout<<s3<<endl;       //輸出結果為hi, badbye.


string s4 = "hello" + ",";       //錯誤,加号左右沒有string對象
string s5 = "hello" + s1;        //正确
string s6 = "hello" + "," + s1;  //錯誤,第一個加号左右沒有string對象,字面值不可以直接相加
           

還有一個注意的點,在第一個加号左右有string的前提下,第二個加号為什麼不需要添加string對象。其實裡面有一個工作原理,編譯器會先建立temp臨時量,先将s1與"hi"臨時存放在temp當中,再讓temp與"badbye"相加,當且僅當第一個加号左右有一個string對象時,才允許這種操作出現

string s7 = s1 + "hi" + "badbye";    //正确,這個式子的工作原理如下

string s7 = (s1 + "hi") + "badbye";
string temp = s1 + "hi";             //編譯器先臨時建立temp臨時量
s7 = temp + "baybye";                //再将temp與後面的字元串相加
           

還有一種字元串相加的方法:append()函數

string s8("hi, ");
s8.append("boy next door");
cout << s8 <<endl;
//輸出hi, boy next  door
           

(4)string對象比較

1、比較操作符:>,>=,<=,<,==,!=

操作符按照字典順序靠前的字元小,比較順序由前到後,遇到不相等的字元就按這個位置上的兩個字元的比較結果确定兩個字元串的大小

2、成員函數compare()

支援多參數處理,支援用索引值和長度定位子串進行比較,前面減去後面的ASCII碼

  1. 将對象1與對象2對比時還:對象1.compare(對象2),從[0,size)一個一個字元進行對比
  2. 同時還支援區間内字元的對比A.compare(2, 2 ,B),從A的[2, 2)與B字元串進行對比
  3. A.compare(2,  2, B, 2, 2),從A的[2, 2)與B的[2, 2)進行對比
string A("123AaAa");
	string B("123BbBb");
	string C("123CcCc");
	string D("123DdDd");

	cout << "A.compare(B):" << A.compare(B) << endl;                          //"123AaAa"和"123BbBb"比較,A<B	,-1
	cout << "A.compare(2, 2, B):" << A.compare(2, 2, B) << endl;               //"3A"和“123BbBb"比較,3>1	,1
	cout << "A.compare(2, 2, B, 2, 2):" << A.compare(2, 2, B, 2, 2) << endl;  //"3A"和“3B"比較,A<B		,-1
	cout << "C.compare(2, 4, D, 2, 4):" << C.compare(2, 4, D, 2, 4) << endl;   //"3CcC"和"3DdDd"比較,C<B	,-1
           

2、額外的string操作

(1)構造string的其他方法

//n、len2和pos2都是無符号值

char *p[] = "hi!";
string s(cp, n);       //s是cp指向的數組中前n個字元的拷貝、此數組至少包含n個字元

string s(s2, pos2);    //s是string s2從下标pos2開始的字元的拷貝。如果pos2的大小大于s2目前大小,則構造函數的行為未定義,可運作成功其他的結果,但是此項顯示為空

string s(s2, pos2, len2);    //從s2拷貝下标為pos開始len2個字元給s
           
#include<iostream>
#include<string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
	const char *p = "boy next door!!";
	string s1(p, 2);        //拷貝p指向的數組中的前個字元
	cout << s1<< endl;
	

	string s2("123456789");
	string s3(s2,1);        //拷貝s2當中從下标為1開始的字元。當設定大小大于9,會列印空并且報錯
	cout << s3 << endl;

	string s4(s2, 3, 2);    //拷貝s2從下标為3開始的2個字元
	cout << s4 << endl;
	system("pause");
	return 0;
}
           

提示:下标從0開始 

運作結果:

C++:标準庫類型string

當char數組存放的内容為字元時,必須以'\0'結尾,全為數字時,不需要'\0'結尾,不然計算機會顯示多出來的内容。舉個例子,圖書館當中一列櫃子有100個格子,裡面未必放滿,可能存放10本書、20本,是以我們不能用格數算字元串長度(sizeof()),實際存放書的輸了是動态變化的,但是管理者告訴我們當我們用'\0'标記最後一位時,就明确了到此為止不會存放任何書了,編譯器就懂得隻列印'\0'前的内容了。

C标準庫中的字元串處理程式,是隻認'\0'的,隻要沒找到'\0',它就認為字元串沒有結束,拼命地往後找,這個尋找的過程不理會可能已經超過書櫃的格數了(計算機其實很蠢);同樣,也可能你在一排書中的中間抽走一本,在那個位置上寫上'\0',那麼愚蠢的計算機也會認為書到這裡為止,它不理會後面其實還有(這是某種截斷字元串的技巧)。
char i[] = {'boy','next','door'};       
string s(i);              //錯誤,如果char數組不以\0結尾,拷貝進s除了會出現i的内容外,還會出現一些亂碼
string s(i,2);            //拷貝i的2個字元給s
           
char i[] = { 'b','n','d' };
	string s5(i);
	cout << s5 << endl;        //列印結果:bnd燙燙蘇m箏4齇,每次列印結果時,出了bnd相同,後面的亂碼是會改變的,也說明了char的動态變化
	string s6(i, 2);
	cout << s6 << endl;        //列印結果:bo


char i[]={ 'b','n','d', '\0' };
    string s5(i);
	cout << s5 << endl;        //列印結果:bnd
           

我在網上找到了類型相關'\0'結尾的總結:

  1. const char* 字元串 以 “\0”結尾。
  2. char[] 字元串 以 “\0”結尾。
  3. string 字元串 不以 “\0”結尾。
  4.  char[n] = "string", 當string 長度+“\0”>n時,會因空間不足出錯。
  5. string.c_str() 轉 const char* 時, 會在字元串末尾 自動補“\0”
  6. char* 轉string 時, 會自動把末尾的 “\0” 去掉。
  7. strlen()是取字元串除去結尾符 “\0” 的長度。

(2)substr操作

substr會傳回一個string,它是原始string的一部分或全部拷貝。

string s1("boy next door");    //總共14個字元

string s2 = s1.substr(0,13);        //s2 = boy next door,拷貝從下标0開始到13的值
string s3 = s1.substr(6);           //s3 = xt door ,拷貝從下标6到結束的值
string s4 = s1.substr(14)           //會抛出一個out_of_range的異常
           

(3)改變string的其他方法

string類型支援順序容器的指派預算符以及assign、insert和erase操作。append和replace函數還有額外操作。

s.insert(pos,args)   在s的pos(下标或疊代器)之前插入args指定的字元。下标:傳回指向s的引用。疊代器:傳回一個指向第一個插入字元的疊代器。
string str("boy next door!!!");
str.insert(str.size(), "???");        //str == boy next door!!!???,從str最後的字元增加???

str.insert(16, "~~~");              //boy next door!!!~~~???,從第16個字元開始添加~~~
           
s.append(args)      在s後追加args指向的内容,或者字元串。傳回一個指向s的引用。
string s("c++");              //初始化s
    
s.append("Primer");           //s == c++Primer

string str("sstr");
s.append(str);                //s == c++sstr

const char *c = { "ccc" };
s.append(c);                  //s == c++ccc
           
s.erase(pos,len)    删除從pos開始的len個字元。如果len被省略直接删除s當中全部字元。傳回一個指向s的引用。
const char *cp = "boy next door!!!";

s.insert(s.size(), 5, '!');       //在末尾插入5個感歎号
s.assign(cp,8);                //s == boy next,從cp中将前八個字元賦給s
s.insert(s.size(),cp+8);       //s == boy next door!!!,将cp第八個開始到結束的内容加到s中

s.erase(s.size()-5, 5);           //s == boy next do,從s删除最後5個字元
           
s. assign(args)     将s中的字元替換為args指定的字元。傳回一個指向s的引用。
const char *cp = "boy next door!!!";
s.assign(cp,8);                //s == boy next
           

s.replace(range,args)    删除s中範圍range内的字元,替換為 args指定的字元。range可以是下标,或是一堆指向s的疊代器。傳回一個指向s的引用。

replace操作是調用erase和insert的一種簡寫。

string s("c/c++");

s.erase(2,3);            //s == c/,從第二個字元開始删除3個字元
s.insert(2,"指針");      //s == c/指針,從第二個字元開始加入内容

//隻用replace就能完成以上操作
s.replace(2,3,"指針");
           

(4)string的搜尋

  1. string類提供了6個不同的搜尋函數,每個函數都有4個重載版本。
  2. 每個搜尋操作都傳回一個string:size_type值,表示比對發生位置的下标。如果搜尋失敗傳回一個名為string::npos的static成員(npos是一個無符号類型,可能會等于string最大的可能大小)。

s.find(args)      查找s中args第一次出現的位置

s.rfind(args)    查找s中args最後一次出現的位置

s.find_first_of(args)    在s中查找args中任何一個字元第一次出現的位置

s.find_last_of(args)       在s中查找args中任何一個字元最後一次出現的位置

s.find_first_not_of(args)    在s中查找第一個不在args中的字元

s.find_last_not_of(args)    在s中查找從最後一個數起不在args中的字元

string s("C++ Primer");
	auto pos1 = s.find("+");				//查找第一次出現
	cout << "+在s中的下标為:"<<pos1 << endl;

	pos1 = s.rfind("+");					//查找最後一次出現
	cout << "+在s中的下标為:" << pos1 << endl;
		
	pos1 = s.find_first_of("or");			//定位r第一次出現
	cout << "r在s中第一次出現的下标為:" << pos1 << endl;
	pos1 = s.find_first_of("R");			//查找明确區分大小寫,不然輸出string的最大記憶體
	cout << "R在s中第一次出現的下标為:" << pos1 << endl;

	pos1 = s.find_last_of("lr");			//定位r最後一次出現
	cout << "r在s中最後一次出現的下标為:" << pos1 << endl;

	pos1 = s.find_first_not_of("C");		//定位括号内在s中沒有的内容
	cout << "括号内在s第一次不存在的内容下标為:" << pos1 << endl;

	pos1 = s.find_last_not_of("r");			//定位括号内在最後一次s中沒有的内容
	cout << "括号内在s最後一次不存在的内容下标為:" << pos1 << endl;
           

運作結果如下: 

C++:标準庫類型string