轉載鏈接:https://blog.csdn.net/sunshineacm/article/details/78068987
一、string
string 是 C++ 提供的字符串類型,和 C 的字串相比,除了有不限長度的優點外,還有其他許多方便的功能。要使用 string, 必須先加入這一行:
#include <string>
接下來要定義一個字串變量,可以寫成:
string s;
我們也可以在定義的同時初始化字串:
string s = "you";
而要取得其中某一個字元,和傳統C 的字串一樣是用 s[i] 的方式取得。比較不一樣的是如果 s 有三個字元,傳統 C 的字串的 s[3] 是'\0' 字符,但是 C++ 的 string 則是隻到 s[2] 這個字符而已。
做一個對照:
操作 | string | 字符數組 |
定義字符串 | string s; | char s[100]; |
取得第i個字符 | s[i] | s[i] |
字符串長度 | s.length() 或 s.size() |
strlen(s) |
讀入一行 | getline(cin, s); | gets(s); |
賦值 | s = "you"; | strcpy(s, "you"); |
字符串連接 | s = s + "you"; s += "you"; |
strcat(s, "you"); |
字符串比較 | s == "you" | strcmp(s, "you"); |
兩個string常用的方法是find和substr。在下面的代碼當中:find函數從str的第3個位置查起,找到ssdf這個子串後,返回子串的位置。而substr函數從pos位置開始,截取5個字符,賦值給str2。也就是說,str2之後的內容將是ssdfs。
string str = "aaaaddddssdfsasdf";
size_t pos = str.find("ssdf", 3);
//可以用if(pos == string::npos) 用來判斷是否找到子串。
//傳送門: http://blog.csdn.net/sunshineacm/article/details/78075135
string str2 = str.substr(pos, 5);
二、stringstream
stringstream是 C++ 提供的另一個字串型的串流(stream)物件,和之前學過的iostream、fstream有類似的操作方式。要使用stringstream, 必須先加入這一行:
#include <sstream>
stringstream主要是用在將一個字符串分割,可以先用.clear( )以及.str( )將指定字串設定成一開始的內容,再用>>把個別的資料輸出。
舉個例子:
題目:輸入的第一行有一個數字 N 代表接下來有 N 行資料,每一行資料裏有不固定個數的整數(最多20個,每行最大200個字元),編程將每行的總和打印出來。
輸入:
3
1 2 3
20 17 23 54 77 60
111 222 333 444 555 666 777 888 999
輸出:
6
251
4995
代碼:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
string s;
stringstream ss;
int n;
cin >> n;
getline(cin, s); //讀取換行
for (int i = 0; i < n; i++)
{
getline(cin, s);
ss.clear();
ss.str(s);
int sum = 0;
while (1)
{
int a;
ss >> a;
if(ss.fail())
break;
sum += a;
}
cout << sum << endl;
}
return 0;
}
三、使用stringstream簡化類型轉換
C++標準庫中的<sstream>提供了比ANSI C的<stdio.h>更高級的一些功能,即單純性、類型安全和可擴展性。接下來,我將舉例說明怎樣使用這些庫來實現安全和自動的類型轉換。
一個例子:
#include <stdio.h>
int main()
{
int n = 10000;
char s[10];
sprintf(s, "%d", n);
//s中的內容爲“10000”
//到目前爲止看起來還不錯。但是,對上面代碼的一個微小的改變就會使程序發生錯誤
printf("%s\n", s);
sprintf(s, "%f", n);
//錯誤的格式化符
printf("%s\n", s);
return 0;
}
輸出:
在這種情況下,由於錯誤地使用了 %f 格式化符來替代了%d。因此,s在調用完sprintf()後包含了一個不確定的字符串。要是能自動推導出正確的類型,那不是更好嗎?
進入stringstream:
由於n和s的類型在編譯期就確定了,所以編譯器擁有足夠的信息來判斷需要哪些轉換。<sstream>庫中聲明的標準類就利用了這一點,自動選擇所必需的轉換。而且,轉換結果保存在stringstream對象的內部緩衝中。你不必擔心緩衝區溢出,因爲這些對象會根據需要自動分配存儲空間。
<sstream>庫定義了三種類:istringstream、ostringstream和stringstream,分別用來進行流的輸入、輸出和輸入輸出操作。另外,每個類都有一個對應的寬字符集版本。簡單起見,我主要以stringstream爲中心,因爲每個轉換都要涉及到輸入和輸出操作。
注意,<sstream>使用string對象來代替字符數組。這樣可以避免緩衝區溢出的危險。而且,傳入參數和目標對象的類型被自動推導出來,即使使用了不正確的格式化符也沒有危險。
1、string到int的轉換
string result = "10000";
int n = 0;
stream << result;
stream >> n; //n等於10000
2.重複利用stringstream對象
如果你打算在多次轉換中使用同一個stringstream對象,記住在每次轉換前要使用clear()方法。
在多次轉換中重複使用同一個stringstream(而不是每次都創建一個新的對象)對象最大的好處在於效率。stringstream對象的構造和析構函數通常是非常耗費CPU時間的。
3.在類型轉換中使用模板
你可以輕鬆地定義函數模板來將一個任意的類型轉換到特定的目標類型。例如,需要將各種數字值,如int、long、double等等轉換成字符串,要使用以一個string類型和一個任意值t爲參數的to_string()函數。to_string()函數將t轉換爲字符串並寫入result中。使用str()成員函數來獲取流內部緩衝的一份拷貝。
template<class T>
void to_string(string &result, const T &t)
{
ostringstream oss; //創建一個流
oss << t; //把值傳遞入流中
result = oss.str(); //獲取轉換後的字符並將其寫入result
}
//這樣,你就可以輕鬆地將多種數值轉換成字符串了
to_string(s1, 10.5); //double到string
to_string(s2, 123); //int到string
to_string(s3, true); //bool到string
//可以更進一步定義一個通用的轉換模板,用於任意類型之間的轉換。函數模板convert()含有兩個模板參數out_type和in_value,功能是將in_value值轉換成out_type類型:
template<class out_type, class in_value>
out_type convert(const in_value & t)
{
stringstream stream;
stream << t; //向流中傳值
out_type result; //這裏存儲轉換結果
stream >> result; //向result中寫入值
return result;
}
測試代碼:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
template<class T>
void to_string(string &result, const T &t)
{
ostringstream oss;
oss << t;
result = oss.str();
}
template<class out_type, class in_value>
out_type convert(const in_value & t)
{
stringstream stream;
stream << t;
out_type result;
stream >> result;
return result;
}
int main()
{
//to_string實例
string s1, s2, s3;
to_string(s1, 10.5); //double到string
to_string(s2, 123); //int到string
to_string(s3, true); //bool到string
cout << s1 << endl << s2 << endl << s3 << endl << endl;
//convert()例子
double d;
string salary;
string s = "12.56";
d = convert <double> (s); //d等於12.56
salary = convert <string> (9000.0); //salary等於"9000"
cout << d << endl << salary << endl;
return 0;
}
輸出:
4.結論
在過去留下來的程序代碼和純粹的C程序中,傳統的<stdio.h>形式的轉換伴隨了我們很長的一段時間。但是,如文中所述,基於stringstream的轉換擁有類型安全和不會溢出這樣的特性,使我們有充足得理由去使用<sstream>。<sstream>庫還提供了另外一個特性—可擴展性。你可以通過重載來支持自定義類型間的轉換。
5.一些實例
stringstream通常是用來做數據轉換的。相比c庫的轉換,它更加安全,自動和直接。
例子一: 基本數據類型轉換例子 int 轉 string
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
string s;
int i = 1000;
ss << i;
ss >> s;
cout << s << endl;
return 0;
}
運行結果:
例子二: 除了基本類型的轉換,也支持char *的轉換
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
char s[10];
ss << 8888;
ss >> s;
cout << s << endl;
return 0;
}
運行結果:
例子三: 再進行多次轉換的時候,必須調用stringstream的成員函數.clear()
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
int first = 0, second = 0;
ss << "456"; // 插入字符串
ss >> first; //轉換成int
cout << first << endl;
ss.clear(); //在進行多次轉換前, 必須清除ss
ss << true;
ss >> second;
cout << second << endl;
return 0;
}
運行結果:
運行.clear()結果
沒有運行.clear()結果
6.使用誤區
如果stringstream使用不當,當心內存出問題。試試下面的代碼,運行程序前打開任務管理器,看看內存變化。
複製代碼,把 stream.str(""); 那一行的註釋去掉,再運行程序,內存就正常了。
看來stringstream似乎不打算主動釋放內存( 或許是爲了提高效率 ),但如果你要在程序中用同一個流,反覆讀寫大量的數據,將會造成大量的內存消耗,因此這時候,需要適時地清除一下緩衝 ( 用 stream.str("") )。
另外不要企圖用 stream.str().resize(0) 或 stream.str().clear() 來清除緩衝,使用它們似乎可以讓stringstream的內存消耗不要增長得那麼快,但仍然不能達到清除stringstream緩衝的效果(做個實驗就知道了,內存的消耗還在緩慢的增長)
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
std::stringstream stream;
string str;
while(1)
{
//clear()這個名字讓很多人想當然地認爲它會清除流的內容。
//實際上它並不清空任何內容,它只是重置了流的狀態標誌。
stream.clear();
//去掉下面這行註釋,清空stringstream的緩衝,每次循環內存消耗將不再增加。
//stream.str("");
stream << "you see see you";
stream >> str;
// 去掉下面這行註釋,看看每次循環,你的內存消耗會增加多少
//cout << "Size of stream = " << stream.str().length() << endl;
}
return 0;
}
參考鏈接: