第 6 章: 文字列クラス

シリーズ記事ディレクトリ



序文

string は文字シーケンスのクラスです


C++ ドキュメント

文字列クラスを学ぶ理由

C言語の文字列

C 言語では、文字列は「\0」で終わるいくつかの文字の集合です。操作の便宜上、C 標準ライブラリには str 系のライブラリ関数がいくつか用意されていますが、これらのライブラリ関数は文字列から分離されており、OOP の考え方に準拠しておらず、その下にある領域はユーザーが管理する必要があります。注意しないと、範囲外にアクセスする可能性があります。

アスキー

ASCII (American Standard Code for Information Interchange): American Standard Code for Information Interchangeは、ラテン文字に基づくコンピュータ コード システムで、主に現代英語やその他の西ヨーロッパ言語を表示するために使用されます。これは最も一般的な情報交換標準であり、国際標準 ISO/IEC 646 と同等です。ASCII が最初に仕様標準として発行されたのは 1967 年で、最後の更新は 1986 年で、これまでに合計 128 文字が定義されています。

**英語の情報をコンピュータに保存および表示する

26 文字の大文字と小文字、数字、句読点

シンボルとエンコーディングの対応表を作成する

1 バイトのサイズを使用して 7 ビットエンコーディングを保存します。最上位ビットは使用されません。1 シンボル→ 1 バイト**

ここに画像の説明を挿入

int main()
{
    
    
	char str1[] = "apple";	

	char str2[] = "好好";

	cout << sizeof(str1) << endl;
		
	cout << sizeof(str2) << endl;

	return 0;
}

ここに画像の説明を挿入

int main()
{
    
    

	char str2[] = "好好";

	cout << sizeof(str2) << endl;

	str2[3]--;
	cout << str2 << endl;

	str2[3]--;
	cout << str2 << endl;

	str2[3]++;
	cout << str2 << endl;
	
	str2[3]++;
	cout << str2 << endl;

	return 0;
}

ここに画像の説明を挿入

ユニコード

Unicode (Unicode) は、 Universal CodeおよびUnicodeとも呼ばれ、Unicode Consortium によって開発され、文字セットやエンコーディング スキームを含むコンピューター サイエンスの分野の業界標準です。

Unicode は、従来の文字エンコーディング スキームの制限を解決するために作成され、言語間およびプラットフォーム間のテキスト変換と処理の要件を満たすために、各言語の各文字に統一された一意のバイナリ エンコーディングを設定します

UTF-8

UTF-8 の特徴は、文字の範囲ごとに異なる長さのエンコーディングを使用することです。0x00 ~ 0x7F の文字の場合、UTF-8 エンコーディングは ASCII エンコーディングとまったく同じです。UTF-8 エンコードの最大長は 4 バイトです。上の表からわかるように、4 バイトのテンプレートには 21 個の x があり、21 個の 2 進数を収容できます。Unicode の最大コード ポイント 0x10FFFF はわずか 21 ビットです。

ここに画像の説明を挿入

UTF-16

UTF-16 エンコードは、16 ビットの符号なし整数の単位で行われます。Unicode エンコーディングを U と表します。

UTF-32

UTF-32 エンコードは、32 ビットの符号なし整数の単位で行われます。Unicode の UTF-32 エンコーディングは、それに対応する 32 ビットの符号なし整数です。

GBK

GBKの正式名称は「中国内部コード拡張仕様」(GBKは「国家標準」と「拡張」中国語ピンインの頭文字、英語名: Chinese Internal Code Supplement )です。

GBK も 2 バイト表現を採用しており、全体のコーディング範囲は 8140-FEFE、最初のバイトは 81-FE の間、最後のバイトは 40-FE の間で、xx7F 行は除外されます。合計 23,940 のコード ポイント、合計 21,003 の漢字 (部首とコンポーネントを含む) を含む合計 21,886 の漢字とグラフィック シンボル、および 883 のグラフィック シンボル。
ここに画像の説明を挿入

標準ライブラリの文字列クラス

文字列クラス

  1. String は文字列を表すクラスです
  2. 標準の文字列クラスは、標準の文字コンテナと同様のインターフェイスを備えたこのようなオブジェクトのサポートを提供しますが、特にシングルバイト文字列を操作するための設計機能が追加されています。
  3. 文字列クラスは char を使用しています (つまり、文字型として、デフォルトの char_traits とアロケーター型を使用しています)
  4. string クラスは、basic_string テンプレート クラスのインスタンスです。このクラスは、char を使用して、basic_string テンプレート クラスをインスタンス化し、char_traits と allocator を、basic_string のデフォルト パラメータとして使用します。
  5. このクラスは、使用されるエンコーディングとは無関係にバイトを処理することに注意してください。マルチバイト文字または可変長文字 (UTF-8 など) のシーケンスを処理するために使用される場合、このクラスのすべてのメンバー (長さやサイズなど) およびその反復子は、引き続き (実際のエンコードされた文字ではなく) バイトで動作します。

文字列クラスのドキュメント

ここに画像の説明を挿入

string クラスは utf-8 でエンコードされ、1 バイトとして処理されます。u16string は
utf-16 でエンコードされ
、u32string は utf-32 でエンコードされます
。wstring クラスは 2 バイトとして処理されます。

ここに画像の説明を挿入

コーディング:バイナリの 0 と 1 のみがコンピュータに保存され、対応する ASCII テーブルがテキスト
を表現するために使用されます (英語をサポート)。ASCII テーブルは 256 の値に対応する表現値を確立するためのものです。初期の頃、コンピュータを使用していたのはヨーロッパとアメリカだけでした (初期のコンピュータは英語のみを表現でき、他の国のテキストは表現できませんでした)


要約する

  1. string は文字列を表す文字列クラスです。

  2. このクラスのインターフェースは基本的に通常のコンテナーと同じですが、文字列を操作するために特別に使用されるいくつかの通常の操作が追加されています。

  3. 文字列は実際には一番下にあります。これは、basic_string テンプレート クラスのエイリアス、typedef Basic_string<char, char_traits, allocator>string; です。

  4. マルチバイト文字または可変長文字のシーケンスは操作できません。

  5. string クラスを使用する場合は、#include ヘッダー ファイルをインクルードし、名前空間 std を使用する必要があります。

文字列クラスの共通インターフェース説明

文字列クラスの共通インターフェース

1. 文字列オブジェクトの一般的な構造

(コンストラクター) 関数名 機能の説明
string() (強調) 空の文字列クラス オブジェクト、つまり空の文字列を構築します。
string(const char* s) (強調) C 文字列を使用して文字列クラス オブジェクトを構築する
文字列(size_t n, char c) 文字列クラス オブジェクトには n 個の文字が含まれています。 c
string(const string&s) (重点) コピーコンストラクター
void Teststring()
{
    
    
    string s1; // 构造空的string类对象s1
    string s2("hello bit"); // 用C格式字符串构造string类对象s2
    string s3(s2); // 拷贝构造s3
}

2. 文字列オブジェクトの容量操作

関数名 機能の説明
サイズ(強調) 文字列の有効な文字長を返します。
長さ 文字列の有効な文字長を返します。
容量 スペースの合計サイズを返します。ただし、「\0」は含まれません。
空(強調) 検出文字列が空の文字列として解放された場合は true を返し、それ以外の場合は false を返します。
明確(強調) 有効な文字をクリアする
予備(強調) 文字列用のスペースを事前に予約する
サイズ変更(強調) 有効な文字数を n に変更し、余分なスペースを文字 c で埋めます。

ここに画像の説明を挿入

注:
1. size() メソッドと length() メソッドの基本的な実装原理はまったく同じです。size() を導入する理由は、他のコンテナのインターフェイスとの一貫性を維持するためです。一般的には、基本的に size() が使用されます。
2. clear() は文字列内の有効な文字のみをクリアし、基礎となるスペースのサイズは変更しません。
3.size(size_t n)とresize(size_t n, char c)はどちらも文字列内の有効文字数をnに変更しますが、違いは文字数が増加した場合、resize(n)は余分な要素のスペースを0で埋め、resize(size_t n, char c)は余分な要素のスペースを文字cで埋めることです。
注: サイズ変更によって要素の数が変更される場合、要素の数が増加すると、基礎となる容量のサイズが変更される可能性がありますが、要素の数が減少すると、基礎となる領域の合計サイズは変更されません。
4.reserve(size_t res_arg=0): 有効要素の数を変更せずに文字列のスペースを予約します。reserve のパラメータが string の基礎となるスペースの合計サイズより小さい場合、reserve は容量を変更しません。5. Windowsでは約1.5倍、Linuxでは約2倍
の容量になります

文字列容量関連のメソッドを使用したコードのデモ

3. 文字列クラスオブジェクトのアクセスおよびトラバース操作

関数名 機能の説明
operator[] (重 点) const string クラス オブジェクトによって呼び出される、位置 pos の文字を返します。
開始+終了 begin は文字のイテレータを取得します + end は最後の文字の次の位置 ('\0') のイテレータを取得します
r開始+終了 rbegin は最後の文字のイテレータを取得 + rend は最初の文字の前の位置のイテレータを取得します
の範囲 C++11 は、より簡潔な範囲の新しいトラバーサル メソッドをサポートしており、最下層はイテレータです

文字列での要素アクセスとトラバーサル コードのデモンストレーション

int main()
{
    
    
    string s1("hello world");
    string::iterator it = s1.begin();
    //正向迭代器
    while (it != s1.end())
    {
    
    
	    cout << *it << " ";
	    it++;
    }
	string::reverse_iterator rit = s1.rbegin();
    //反向迭代器
	while (rit != s1.rend())
	{
    
    
		cout << *rit << " ";
		rit++;
        //都是++
	}
  
    for (auto ch : s1)
	{
    
    
		cout << ch << " ";
	}

	cout << endl;
  
	return 0;
}
void Func1(const string& s)
{
    
    	
	//遍历和读容器的数据,不能写
	//string::const_iterator it = s.cbegin();
    auto it = s.cbegin();
    //静态正向迭代器
	while (it != s.end())
	{
    
    
		cout << *it << " ";
        it++;
	}

	cout << endl;
}
void Func2(const string& s)
{
    
    	
    
	//string::const_reverse_iterator it = s.crbegin();
    auto it = s.crbegin();
    //静态反向迭代器
	while (it != s.rend())
	{
    
    
		cout << *it << " ";
        it++;
	}

	cout << endl;
}
int main()
{
    
    
	string s1("hello world");
	string::iterator it = s1.begin();
    //iterator 迭代器 是属于类的一种类型
	while (it != s1.end())
	{
    
    
		cout << *it << " ";
		it++;
	}

	for (auto ch : s1)
	{
    
    
		cout << ch << " ";
	}
    
    Func1();
    
    Func2();

	cout << endl;
	
	return 0;
}

概要:
イテレータは、ポインタのように動作するものです。ポインタである場合もあれば、ポインタでない場合もあります。イテレータは、同様の方法でコンテナにアクセスしたり変更したりするために使用できます (ベクトル/
文字
列などの構造は、アクセスするための添字 + [] をサポートしますが、リストやマップなどの構造はサポートしません)。イテレータはすべてのデータ構造で使用できますが、[] + 添字は、該当するシーケンス テーブルを参照します
Const オブジェクトは、読み取り専用で書き込みできない const イテレータを使用する必要があります。operator [] と at の違いは、範囲外のアクセスが発生した場合、operator[] はアサーション エラー (アサート) を報告しますが、at は例外を報告します (例外をキャッチする必要があります)。


4. 文字列クラスオブジェクトの変更操作

関数名 機能の説明
プッシュバック 文字列の末尾に文字 c を挿入します
追加する 文字列の後に文字列を追加する
演算子+= (強調) 文字列 str を文字列の後に追加します
c_str (強調) C 形式の文字列を返します
find + npos (キー) 文字列 pos の位置から逆方向に文字 c を検索し、文字列内の文字の位置を返します。
探す 文字列 pos の位置から文字 c を検索し、文字列内の文字の位置を返します。
部分文字列 str の pos の位置から開始し、n 文字をインターセプトして返します。

文字列の挿入や検索などのコード使用デモ

注:
1. 文字列の末尾に文字を追加する場合、s.push_back© / s.append(1, c) / s += 'c' の実装方法は似ています。一般に、string クラスの += 演算がよく使用されます。+= 演算は、単一の文字を結合するだけでなく、文字列を結合することもできます。
2. 文字列を操作する場合、配置する文字数を大まかに見積もることができる場合は、まず、reserve によってスペースを予約できます。

int main()
{
    
    
	string s1("world");

	s1.insert(0, "hello");

	//不推荐使用insert,效率低
	//s1.insert(5, 1, ' ');
	//s1.insert(5, " ");
	string::iterator it = s1.begin();
	s1.insert(it+5, ' ');
	cout << s1 << endl;
	return 0;
}
int main()
{
    
    
	//不推荐使用erase,效率低
	string s1("hello world");
	//s1.erase(5, 1);

	string::iterator it = s1.begin();
	//s1.erase(it + 5);

	//s1.erase(5);
	s1.erase(5, 6);
	cout << s1 << endl;

}
int main()
{
    
    
	//string s1("hello world");
	//s1.replace(5, 1, "%%d");//扩容+移动数据
	//cout << s1 << endl;、

	string s1("hello world i love you");
	size_t num = 0;
	for (auto ch : s1)
	{
    
    
		if (ch == ' ')
		{
    
    
			num++;
		}
	}
	//提前开空间,避免replace时扩容
	s1.reserve(2* num + s1.size());
	size_t pos = s1.find(' ');
	while(pos != string::npos)
	{
    
    	
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ', pos + 3);
	}
	cout << s1 << endl;
}

//空间换时间
int main()
{
    
    
	string s1("hello world i love you");
	string newStr;
	size_t num = 0;
	for (auto ch : s1)
	{
    
    
		if (ch == ' ')
		{
    
    
			num++;
		}
	}
	//提前开空间,避免replace时扩容
	s1.reserve(2* num + s1.size());
	for (auto ch : s1)
	{
    
    
		if (ch != ' ')
			newStr += ch;
		else
			newStr += "%20";
	}
	s1 = newStr;
	cout << newStr << endl;
}
int main()
{
    
    
	string s1("xxx");
	string s2("yyy");

	s1.swap(s2);
	cout << s1 << endl;
	cout << s2 << endl;

	swap(s1, s2);
	cout << s1 << endl;
	cout << s2 << endl;
	 
}

ここに画像の説明を挿入

5. Stringクラスの非メンバ関数

関数 機能の説明
演算子+ 値が返されるため、ディープコピーの効率が低下するため、使用はできるだけ控えてください。
演算子>> (強調) 入力演算子のオーバーロード
演算子<< (強調) 出力演算子のオーバーロード
getline (強調) 文字列の行を取得します
関係演算子 (強調) サイズ比較

ここに画像の説明を挿入

ここに画像の説明を挿入

Cin はスペースと改行に遭遇すると分割 (または終了) しますが、getline はスペースに遭遇しても分割 (または終了) せず、改行に遭遇すると分割 (または終了) します。

6. 文字列クラスオブジェクトの文字列変換 (C++11 で適用可能)

ここに画像の説明を挿入

vs および g++ での文字列構造の説明

注: 次の構造は 32 ビット プラットフォームで検証されており、ポインターは 32 ビット プラットフォームで 4 バイトを占有します。

vs の下の文字列の構造

文字列は合計 28 バイトを占め、内部構造は少し複雑です。まず、文字列内の文字列の記憶領域を定義するために使用される共用体があります。

  • 文字列長が 16 未満の場合は、内部固定文字配列を使用して格納します。

  • 文字列の長さが 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;

この設計も理にかなっていて、ほとんどの場合、文字列の長さは 16 未満です。文字列オブジェクトの作成後、内部には 16 個の文字配列用の固定スペースがすでにあり、ヒープを介して作成する必要がないため、非常に効率的です。
第 2 に、文字列の長さを保存する size_t フィールドと、ヒープから解放されたスペースの総容量を保存する size_t フィールドもあります。 最後に、他のことを行うためのポインタもあります。
したがって、合計 16+4+4+4=28 バイトを占めます。
ここに画像の説明を挿入

g++での文字列の構造

G++ では、文字列はコピーオンライトによって実装されます。文字列オブジェクトは合計 4 バイトを占め、内部にポインタが 1 つだけ含まれます。このポインタは将来ヒープ領域を指し、内部には次のフィールドが含まれます。

  • 合計スペースサイズ

  • 有効な文字列長

  • 参照カウント

struct _Rep_base
{
    
    
	size_type        _M_length;
	size_type        _M_capacity;
	_Atomic_word      _M_refcount;
};
  • 文字列を格納するためのヒープ領域へのポインタ

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-1uFFi6yM-1689733417126)(https://flowus.cn/preview/cc560c88-e3f5-4381-b6fc-3a21c3afb6f4)]

おすすめ

転載: blog.csdn.net/yanyongfu523/article/details/131802834