1. 程式人生 > >第三章 標準庫型別(part 1) 標準庫 string 型別

第三章 標準庫型別(part 1) 標準庫 string 型別

除基本資料型別外,C++ 還定義了一個內容豐富的抽象資料型別標準庫。

其中最重要的標準庫型別是 stringvector,它們分別定義了大小可變的字串和集合。

stringvector 往往將迭代器用作配套型別(companion type),用於訪問string 中的字元,或者vector 中的元素。

這些標準庫型別是語言組成部分中更基本的那些資料型別(如陣列和指標)的抽象。

另一種標準庫型別 bitset,提供了一種抽象方法來操作位的集合。與整型值上的內建位操作符相比,bitset 類型別提供了一種更方便的處理位的方式。

兩種最重要的標準庫型別是stringvectorstring

型別支援長度可變的字串,vector 可用於儲存一組指定型別的物件。說它們重要,是因為它們在 C++ 定義的基本型別基礎上作了一些改進。後續還將學習類似於標準庫中stringvector 型別的語言級構造,但標準庫的 stringvector 型別可能更靈活,且不易出錯。

另一種標準庫型別提供了更方便和合理有效的語言級的抽象設施,它就是bitset 類。通過這個類可以把某個值當作們的集合來處理。

3.1. 名稱空間的 using 宣告

需要從標準輸入讀取資料時,就用 std::cin。這些名字都用了:: 操作符,該操作符是作用域操作符。它的含義是右運算元的名字可以在左運算元的作用域中找到。因此,std::cin
的意思是說所需要名字cin 是在名稱空間std 中定義的。顯然,通過這種符號引用標準庫名字的方式是非常麻煩的。

幸運的是,C++ 提供了更簡潔的方式來使用名稱空間成員。介紹一種最安全的機制:。

使用 using 宣告可以在不需要加字首namespace_name:: 的情況下訪問名稱空間中的名字。using 宣告的形式如下:

     using namespace::name;

一旦使用了 using 宣告,我們就可以直接引用名字,而不需要再引用該名字的名稱空間。

     #include <string>
     #include <iostream>
     // using
declarations states our intent to use these names from the namespace std using std::cin; using std::string; int main() { string s; // ok: string is now a synonym for std::string cin >> s; // ok: cin is now a synonym for std::cin cout << s; // error: no using declaration; we must use full name std::cout << s; // ok: explicitly use cout from namepsace std }

沒有 using 宣告,而直接使用名稱空間中名字的未限定版本是錯誤的,儘管有些編譯器也許無法檢測出這種錯誤。

每個名字都需要一個 using 宣告

一個 using 宣告一次只能作用於一個名稱空間成員。using 宣告可用來明確指定在程式中用到的名稱空間中的名字,如果希望使用std(或其他的名稱空間)中的幾個名字,則必須為要用到的每個名字都提供一個using 宣告。
     #include <iostream>
     // using declarations for names from the standard library
     using std::cin;
     using std::cout;
     using std::endl;
     int main()
     {
         cout << "Enter two numbers:" << endl;
         int v1, v2;
         cin >> v1 >> v2;
         cout << "The sum of " << v1
              << " and " << v2
              << " is " << v1 + v2 << endl;
         return 0;
     }

cincoutendl 進行 using 宣告,就意味著以後可以省字首std::,直接使用名稱空間中的名字,這樣程式碼可以更易讀。

使用標準庫型別的類定義

有一種情況下,必須總是使用完全限定的標準庫名字:在標頭檔案中。

理由是標頭檔案的內容會被前處理器複製到程式中。用 #include 包含檔案時,相當於標頭檔案中的文字將成為我們編寫的檔案的一部分。如果在標頭檔案中放置 using 宣告,就相當於在包含該標頭檔案using 的每個程式中都放置了同一using,不論該程式是否需要using 宣告。

通常,標頭檔案中應該只定義確實必要的東西。請養成這個好習慣。


3.2. 標準庫 string 型別

string 型別支援長度可變的字串,C++ 標準庫將負責管理與儲存字元相關的記憶體,以及提供各種有用的操作。標準庫 string 型別的目的就是滿足對字串的一般應用。

  與其他的標準庫型別一樣,使用者程式要使用 string 型別物件,必須包含相關標頭檔案。如果提供了合適的 using 宣告,那麼編寫出來的程式將會變得簡短些:

     #include <string>
     using std::string;

string 物件的定義和初始化

表 3.1. 幾種初始化 string 物件的方式

string s1;

Default constructor; s1 is the empty string

預設建構函式 s1 為空串

string s2(s1);

Initialize s2 as a copy of s1

s2 初始化為 s1 的一個副本

string s3("value");

Initialize s3 as a copy of the string literal

s3 初始化為一個字串字面值副本

string s4(n, 'c');

Initialize s4 with n copies of the character'c'

s4 初始化為字元 'c'n 個副本


警告:標準庫 string 型別和字串字面值

因為歷史原因以及為了與 C 語言相容,字串字面值與標準庫string 型別不是同一種類型。這一點很容易引起混亂,程式設計時一定要注意區分字串字面值和string 資料型別的使用,這很重要。


string 物件的讀寫

讀入未知數目的 string 物件

和內建型別的輸入操作一樣,string 的輸入操作符也會返回所讀的資料流。因此,可以把輸入操作作為判斷條件。下面的程式將從標準輸入讀取一組string 物件,然後在標準輸出上逐行輸出:

     int main()
     {
         string word;
         // read until end-of-file, writing each word to a new line
         while (cin >> word)
             cout << word << endl;
         return 0;
     }

上例中,用輸入操作符來讀取 string 物件。該操作符返回所讀的 istream 物件,並在讀取結束後,作為while 的判斷條件。如果輸入流是有效的,即還未到達檔案尾且未遇到無效輸入,則執行while 迴圈體,並將讀取到的字串輸出到標準輸出。如果到達了檔案尾,則跳出while 迴圈。

使用 getline 讀取整行文字
另外還有一個有用的 string IO 操作:。這個函式接受兩個引數:一個輸入流物件和一個string 物件。getline函式從輸入流的下一行讀取,並儲存讀取的內容到不包括換行符。和輸入操作符不一樣的是,getline 並不忽略行開頭的換行符。只要getline 遇到換行符,即便它是輸入的第一個字元,getline 也將停止讀入並返回。如果第一個字元就是換行符,則string 引數將被置為空 string

getline 函式將 istream 引數作為返回值,和輸入操作符一樣也把它用作判斷條件。例如,重寫前面那段程式,把每行輸出一個單詞改為每次輸出一行文字:

     int main()
     {
         string line;
         // read line at time until end-of-file
         while (getline(cin, line))
             cout << line << endl;
         return 0;
     }

由於 line 不含換行符,若要逐行輸出需要自行新增。照常,我們用 endl 來輸出一個換行符並重新整理輸出緩衝區。

《註解》:

由於 getline 函式返回時丟棄換行符,換行符將不會儲存在 string 物件中。

string 物件的操作

string Operations

s.empty()

Returns true if s is empty; otherwise returnsfalse

如果 s 為空串,則返回 true,否則返回 false

s.size()

Returns number of characters in s

返回 s 中字元的個數

s[n]

Returns the character at position n in s; positions start at 0.

返回 s 中位置為 n 的字元,位置從 0 開始計數

s1 + s2

Returns a string equal to the concatenation of s1 ands2

s1s2 連線成一個新字串,返回新生成的字串

s1 = s2

Replaces characters in s1 by a copy of s2

s1 內容替換為 s2 的副本

v1 == v2

Returns true if v1 and v2 are equal;false otherwise

比較 v1v2的內容,相等則返回 true,否則返回 false

!=, <, <=, >, and >=

Have their normal meanings

保持這些操作符慣有的含義


stringsizeempty 操作

     string 物件的長度指的是 string 物件中字元的個數,可以通過size 操作獲取

     int main()
     {
         string st("The expense of spirit\n");
         cout << "The size of " << st << "is " << st.size()
              << " characters, including the newline" << endl;
         return 0;
     }

編譯並執行這個程式,得到的結果為:

     The size of The expense of spirit
     is 22 characters, including the newline

瞭解 string 物件是否空是有用的。一種方法是將 size 與 0 進行比較:

     if (st.size() == 0)
          // ok: empty

本例中,程式設計師並不需要知道 string 物件中有多少個字元,只想知道 size 是否為 0。用string 的成員函式empty() 可以更直接地回答這個問題:

     if (st.empty())
          // ok: empty

empty() 成員函式將返回 bool,如果 string 物件為空則返回true 否則返回false

string::size_type 型別

從邏輯上來講,size() 成員函式似乎應該返回整形數值,或無符號整數。但事實上,size 操作返回的是string::size_type 型別的值。我們需要對這種型別做一些解釋。

string 類型別和許多其他庫型別都定義了一些配套型別(companion type)。通過這些配套型別,庫型別的使用就能與機器無關(machine-independent)。

就是這些配套型別中的一種。它定義為與unsigned 型(unsigned intunsigned long)具有相同的含義,而且可以保證足夠大能夠儲存任意string 物件的長度。為了使用由 string 型別定義的size_type 型別是由 string 類定義。

《註解》:任何儲存 stringsize 操作結果的變數必須為string::size_type 型別。特別重要的是,還要把 size 的返回值賦給一個int 變數。

雖然我們不知道string::size_type 的確切型別,但可以知道它是unsigned 型。對於任意一種給定的資料型別,它的 unsigned 型所能表示的最大正數值比對應的 signed 型要大倍。這個事實表明size_type 儲存的 string 長度是 int 所能儲存的兩倍。

使用 int 變數的另一個問題是,有些機器上int 變數的表示範圍太小,甚至無法儲存實際並不長的string 物件。如在有 16 位 int 型的機器上,int 型別變數最大隻能表示 32767 個字元的string 個字元的string 物件。而能容納一個檔案內容的 string 物件輕易就會超過這個數字。因此,為了避免溢位,儲存一個stirng 物件size 的最安全的方法就是使用標準庫型別 string::size_type

string 關係操作符

string 類定義了幾種關係操作符用來比較兩個 string 值的大小。這些操作符實際上是比較每個string

string 物件比較操作是區分大小寫的,即同一個字元的大小寫形式被認為是兩個不同的字元。在多數計算機上,大寫的字母位於小寫之前:任何一個大寫之母都小於任意的小寫字母。

== 操作符比較兩個 string 物件,如果它們相等,則返回 true。兩個 string 物件相等是指它們的長度相同,且含有相同的字元。標準庫還定義了 != 操作符來測試兩個 string 物件是否不等。

關係操作符 <<=>>= 分別用於測試一個 string 物件是否小於、小於或等於、大於、大於或等於另一個 string 物件:

     string big = "big", small = "small";
     string s1 = big;    // s1 is a copy of big
     if (big == small)   // false
         // ...
     if (big <= s1)      // true, they're equal, so big is less than or equal to s1
         // ...

關係操作符比較兩個 string 物件時採用了和(大小寫敏感的)字典排序相同的策略:


  • 如果兩個 string 物件長度不同,且短的 string 物件與長的 string 物件的前面部分相匹配,則短的 string 物件小於長的 string 物件

  • 如果 string 物件的字元不同,則比較第一個不匹配的字元。string

舉例來說,給定 string 物件;

     string substr = "Hello";
     string phrase = "Hello World";
     string slang  = "Hiya";

then substr is less than phrase, and slang is greater than either substr or phrase.

substr 小於 phrase,而 slang 則大於 substrphrase


string 物件的賦值

總體上說,標準庫型別儘量設計得和基本資料型別一樣方便易用。因此,大多數庫型別支援賦值操作。對 string 物件來說,可以把一個 string 物件賦值給另一個 string 物件;

     // st1 is an empty string, st2 is a copy of the literal
     string st1, st2 = "The expense of spirit";
     st1 = st2; // replace st1 by a copy of st2

賦值操作後,st1 就包含了 st2 串所有字元的一個副本。

大多數 string 庫型別的賦值等操作的實現都會遇到一些效率上的問題,但值得注意的是,從概念上講,賦值操作確實需要做一些工作。它必須先把 st1 佔用的相關記憶體釋放掉,然後再分配給 st2 足夠存放 st2 副本的記憶體空間,最後把 st2 中的所有字元複製到新分配的記憶體空間。

兩個 string 物件相加

string 物件的加法被定義為連線(concatenation)。也就是說,兩個(或多個)string 物件可以通過使用加操作符 + 或者複合賦值操作符 +=連線起來。給定兩個 string 物件:

     string s1("hello, ");
     string s2("world\n");

下面把兩個 string 物件連線起來產生第三個 string 物件:

     string s3 = s1 + s2;   // s3 is hello, world\n

如果要把 s2 直接追加到 s1 的末尾,可以使用 += 操作符:

     s1 += s2;   // equivalent to s1 = s1 + s2
和字串字面值的連線

上面的字串物件 s1s2 直接包含了標點符號。也可以通過將 string 物件和字串字面值混合連線得到同樣的結果:

     string s1("hello");
     string s2("world");

     string s3 = s1 + ", " + s2 + "\n";
string 物件獲取字元
string 型別通過下標操作符([ ])來訪問 string 物件中的單個字元。下標操作符需要取一個 size_type 型別的值,來標明要訪問字元的位置。這個下標中的值通常被稱為“下標”或“索引”index
《註解》:

string 物件的下標從 0 開始。如果 s 是一個 string 物件且 s 不空,則 s[0] 就是字串的第一個字元, s[1] 就表示第二個字元(如果有的話),而 s[s.size() - 1] 則表示 s 的最後一個字元。

可用下標操作符分別取出 string 物件的每個字元,分行輸出:

     string str("some string");
     for (string::size_type ix = 0; ix != str.size(); ++ix)
         cout << str[ix] << endl;
每次通過迴圈,就從 str 物件中讀取下一個字元,輸出該字元並換行。
下標操作可用作左值

前面說過,變數是左值,且賦值操作的左操作的必須是左值。

和變數一樣,string 物件的下標操作返回值也是左值。因此,下標操作可以放於賦值操作符的左邊或右邊。

通過下面迴圈把 str 物件的每一個字元置為 ‘*’:

     for (string::size_type ix = 0; ix != str.size(); ++ix)
         str[ix] = '*';

string 物件中字元的處理

我們經常要對 string 物件中的單個字元進行處理,例如,通常需要知道某個特殊字元是否為空白字元、字母或數字。表 3.3 列出了各種字元操作函式,適用於 string 物件的字元(或其他任何 char 值)。這些函式都在 cctype 標頭檔案中定義。

Table 3.3. cctype Functions

isalnum(c)

True if c is a letter or a digit.

如果 c 是字母或數字,則為 True

isalpha(c)

true if c is a letter.

如果 c 是字母,則為 true

iscntrl(c)

true if c is a control character.

如果 c 是控制字元,則為 true

isdigit(c)

true if c is a digit.

如果 c 是數字,則為 true

isgraph(c)

true if c is not a space but is printable.

如果 c 不是空格,但可列印,則為 true

islower(c)

true if c is a lowercase letter.

如果 c 是小寫字母,則為 true

isprint(c)

True if c is a printable character.

如果 c 是可列印的字元,則為 true

ispunct(c)

True if c is a punctuation character.

如果 c 是標點符號,則 true

isspace(c)

true if c is whitespace.

如果 c 是空白字元,則為 true

isupper(c)

True if c is an uppercase letter.

如果 c 是大寫字母,則 true

isxdigit(c)

true if c is a hexadecimal digit.

如果是 c 十六進位制數,則為 true

tolower(c)

If c is an uppercase letter, returns its lowercase equivalent; otherwise returns c unchanged.

如果 c 大寫字母,返回其小寫字母形式,否則直接返回 c

toupper(c)

If c is a lowercase letter, returns its uppercase equivalent; otherwise returns c unchanged.

如果 c 是小寫字母,則返回其大寫字母形式,否則直接返回 c

表中的大部分函式是測試一個給定的字元是否符合條件,並返回一個 int 作為真值。如果測試失敗,則該函式返回 0 ,否則返回一個(無意義的)非 0 ,表示被測字元符合條件。

表中的這些函式,可列印的字元是指那些可以表示的字元,空白字元則是空格、製表符、垂直製表符、回車符、換行符和進紙符中的任意一種;標點符號則是除了數字、字母或(可列印的)空白字元(如空格)以外的其他可列印字元

建議:採用 C 標準庫標頭檔案的 C++ 版本

C++ 標準庫除了定義了一些選定於 C++ 的設施外,還包括 C 標準庫。C++ 中的標頭檔案 cctype 其實就是利用了 C 標準庫函式,這些庫函式就定義在 C 標準庫的 ctype.h 標頭檔案中。
C 標準庫標頭檔案命名形式為 name 而 C++ 版本則命名為 cname ,少了字尾,.h 而在標頭檔案名前加了 c 表示這個標頭檔案源自 C 標準庫。因此,cctypectype.h 檔案的內容是一樣的,只是採用了更適合 C++程式的形式。特別地,cname 標頭檔案中定義的名字都定義在名稱空間 std 內,而 .h 版本中的名字卻不是這樣。

通常,C++ 程式中應採用 cname 這種標頭檔案的版本,而不採用 name.h 版本,這樣,標準庫中的名字在名稱空間 std 中保持一致。使用 .h 版本會給程式設計師帶來負擔,因為他們必須記得哪些標準庫名字是從 C 繼承來的,而哪些是 C++ 所特有的。

相關推薦

標準型別(part 1) 標準 string 型別

除基本資料型別外,C++ 還定義了一個內容豐富的抽象資料型別標準庫。 其中最重要的標準庫型別是 string 和 vector,它們分別定義了大小可變的字串和集合。 string 和 vector 往往將迭代器用作配套型別(companion type),用於訪問strin

筆記----深入淺出《React和Redux》Redux框架(不使用react-redux)

二、Redux與Flux    Redux的三個基本原則 A、唯一資料來源(全域性只有一個Store) B、保持狀態只讀(不能直接修改Store狀態) C、資料改變只能通過純函式完成()         1、Redux與Flux不

Java習題3-7(1到n的階乘和

Find.java /* * To change this template, choose Tools | Templates * and open the template in the ed

JHTP練習題及課題__控制語句Part 1-賦值、++、--運算子

 Exercises 4.10 Compare andcontrast the if single-selection statement and the while repetitionstatement. How are these twostatements

《機器學習實戰》:決策樹(1)基本概念

有半個月沒來了。 最近一段時間...大多在忙專案組的事(其實就是改一改現有程式碼的bug,不過也挺費勁的,畢竟程式碼不是自己寫的)。另外就是自己租了幾臺美帝的vps,搭了$-$的伺服器 ,效果還不錯。自己搭的話就不用去買別人的服務了,不過租vps畢竟還是要成本的,光用來番茄

:SpringBoot配置深入-1. 配置環境屬性

開發十年,就只剩下這套架構體系了! >>>   

C++ Primer 【四版】 標準型別

預設建構函式(default constructor)就是在沒有顯式提供初始化式時呼叫的建構函式。它由不帶引數的建構函式,或者為所有形參提供預設實參的建構函式定義。如果定義某個類的變數時沒有提供初始化式,就會使用預設建構函式。如果使用者定義的類中沒有顯式定義任何建構函式,編譯器就會自動為該類生成預設建構函式,

C++ Primer 4 標準型別

  1. 名稱空間的 using 宣告          使用 using 宣告可以在不需要加字首 namespace_name:: 的情況下訪問名稱空間中的名字。using 宣告的形式如下:      using namespace::name;          一個

c++ primer標準型別)學習筆記

1.在使用標準庫提供的string物件的size方法獲取字串長度時,為了避免溢位,儲存一個string對像size的最安全方法就是    使用標準庫型別string::size_type,處於同樣的道理在定義索引變數時也要使用string::size_type。 2.stri

C++ Primer 標準string型別

標準庫string型別: string型別支援長度可變的字串. #include<string> using std::string; 1.string物件的定義和初始化 string s1;   //預設建構函式,s1為空串 string s2(s1);  

C++ Primer 標準vector型別

vector是同一種類型的物件的集合,每個物件都有一個對應的整數索引值。 使用vector之前,必須包含相應的標頭檔案。 #include<vector> using std::vector; vector不是一種資料型別,而只是一個類模板,可用來定義任意多種資

C++ Primer 標準型別 筆記

C++ Primer 第三章 標準庫型別 標準庫型別是我之前沒有接觸過內容,不僅是這一章,整本書有很多東西對我來說都是新的,譚伯伯那本介紹的東西只是C++中的皮毛罷了。感覺到學習C++將是個無底洞。

C++ 標準型別

 1. 標準庫string型別 標頭檔案#include <string> getline(cin,line)函式接收兩個引數,讀取鍵盤輸入流,遇到換行符返回。 string常用操作,

C++ Primer學習筆記- 標準型別之四

四、標準庫bitset型別 標準庫中bitset型別用來處理二進位制位的有序集,bitset型別簡化了位集的處理,使用bitset時需要包含標頭檔案#include<bitset>     bitset物件的定義和初始化 bitset也是類模板,不過bits

【C++ Primer】【學習筆記】【標準型別之:bitset型別

#include <iostream> #include <bitset> using namespace std; int main() { bitset<32> bitvec; int a = 0, b = 1; i

(讀後感)C++ Primer(四版) 快速入門 標準型別

之所以稱為抽象型別,是因為我們在使用時不用關心它們是如何表示的,只需要知道這些抽象型別支援哪些操作就可以了。 字串與標準庫string型別不是同一型別。 cin >> s, 如果輸入 hello world,只會把hello存到s中去,因為標準輸入會讀取字串直

《Linux程式設計》標準IO、格式化輸入輸出、檔案和目錄的維護、掃描目錄)

標準IO庫 在啟動程式時,有三個檔案流是自動開啟的,分別是stdin,stdout,stderr。 1. fopen函式:用於檔案和終端的輸入和輸出。函式原型如下: #include <stdio.h> FILE *fopen(const char* f

計算機網絡(謝希仁版)——導讀(1

時有 互連 如何實現 遠的 共享 esc 了解 網絡 是否   ※數據鏈路層討論什麽   數據鏈路層討論的是局域網中主機與主機間的連接問題,網絡(IP)層討論的主要是網絡與網絡互連的問題。   在數據鏈路層(局域網)使用的信道主要有兩種:點對點信道和廣播信道,我們具體要討論

《數據設計入門經典》讀書筆記——:工作場所中的數據建模

中間 特定 理論 大學 並且 外鍵 另一個 必須 所有 規範化用於粒度化和組織在數據庫中使用的數據。 在第4章中將詳細介紹規範化和應用範式的過程。在這個階段只需要知道規範化是用於將數據劃分到單獨表中的方法或公式——根據一組規則。 不信任將視圖用於除了安全性目標之外的任何事情

關係資料庫標準語言SQL

SQL的特點:綜合統一 集資料定義語言(DDL),資料操縱語言(DML),資料控制語言(DCL)功能於一體。 可以獨立完成資料庫生命週期中的全部活動: 定義和修改、刪除關係模式,定義和刪除檢視,插入資料,建立資料庫;  對資料庫中的資料進行查詢和更新;  資料庫重構和維護 資料庫