Rust 的 char 型別為 32 位元值,儲存 Unicode 碼位,範圍介於 0 至 0xd7ff 或 0xe000 至 0x10ffff 之間。標準函式庫提供多種方法,用於字元分類別、數字處理、大小寫轉換以及與整數的轉換。Stringstr 型別保證只包含格式良好的 UTF-8 編碼,簡化了文書處理。建立 String 值可以使用 String::new()String::with_capacity(n)slice.to_string()iter.collect() 等方法。strString 提供了豐富的字串處理方法,例如 len()is_empty()push()push_str()extend()drain() 等,方便進行字串操作。此外,Rust 還提供了多種文字迭代方式,例如 chars() 方法可以遍歷每個 Unicode 字元。其他方法包括檢查位元組偏移量是否位於字元邊界,以及用於比較、排序和雜湊等操作。

UTF-8 編碼特性與 Rust 中的字元處理

UTF-8 編碼是一種廣泛使用的 Unicode 字元編碼方案,它具有多項優秀的特性,使其在處理文字資料時非常方便。

UTF-8 編碼的特點

  • 第一個位元組即可判斷編碼的完整長度,透過其前導位元即可得知。
  • 由於沒有任何編碼超過四個位元組,因此在處理不受信任的資料時,UTF-8 處理不會要求無限制的迴圈。
  • 在格式正確的 UTF-8 中,無論從隨機的位元組開始,都可以明確地判斷字元編碼的開始和結束位置。

這些特點使得 UTF-8 在處理上比預期的更為舒適。標準函式庫會處理大多數相關事務。

文字方向性

不同文字系統具有不同的書寫方向,例如拉丁字母、斯拉夫字母和泰文字母是從左到右書寫,而希伯來字母和阿拉伯字母則是從右到左。Unicode 儲存字元的順序與其書寫或閱讀順序一致。

assert_eq!("ערבטוב".chars().next(), Some('ע'));

在標準函式庫中,一些方法名稱使用「左」和「右」來表示文字的起始和結束。

字元(char)型別

Rust 的 char 型別是一個 32 位元的值,用於儲存 Unicode 碼位。char 保證落在 0 至 0xd7ff 或 0xe000 至 0x10ffff 的範圍內。所有建立和操作 char 值的方法都確保這一點。

字元分類別

char 型別具有用於將字元分類別為幾種常見類別的方法。這些方法根據 Unicode 的定義進行分類別,如下表所示:

| 方法名稱 | 描述 | 範例 | | :




  • | :






















– | :














| | ch.is_numeric() | 數值字元,包括 Unicode 的「數字;數字」和「數字;字母」一般類別,但不包括「數字;其他」。 | '4'.is_numeric()'ᛮ'.is_numeric()!'⑧'.is_numeric() | | ch.is_alphabetic() | 字母字元:Unicode 的「字母」衍生屬性。 | 'q'.is_alphabetic()'七'.is_alphabetic() | | ch.is_alphanumeric() | 數值或字母字元,如上所定義。 | '9'.is_alphanumeric()'饂'.is_alphanumeric()!'*'.is_alphanumeric() | | ch.is_whitespace() | 空白字元:Unicode 字元屬性「WSpace=Y」。 | ' '.is_whitespace()'\n'.is_whitespace()'\u{A0}'.is_whitespace() | | ch.is_control() | 控制字元:Unicode 的「其他,控制」一般類別。 | '\n'.is_control()'\u{85}'.is_control() |

數字處理

對於數字處理,可以使用以下方法:

  • ch.to_digit(radix) 用於判斷 ch 是否為基數 radix 中的數字。如果是,則傳回 Some(num),其中 numu32 型別的值。否則,傳回 None。此方法只識別 ASCII 數字,而不識別更廣泛的字元類別。
  • std::char::from_digit(num, radix)u32 數字值 num 轉換為 char(如果可能)。如果 num 可以在基數 radix 中表示為單個數字,則 from_digit 傳回 Some(ch),其中 ch 是該數字。當基數大於 10 時,ch 可能是一個小寫字母。否則,它傳回 None
  • ch.is_digit(radix) 如果 ch 是基數 radix 中的 ASCII 數字,則傳回 true。這等同於 ch.to_digit(radix) != None
assert_eq!('F'.to_digit(16), Some(15));
assert_eq!(std::char::from_digit(15, 16), Some('f'));
assert!(char::is_digit('f', 16));

字元大小寫轉換

對於字元大小寫轉換,可以使用以下方法:

  • ch.is_lowercase()ch.is_uppercase() 用於判斷 ch 是否為小寫或大寫字母字元。這些方法遵循 Unicode 的小寫和大寫衍生屬性,因此它們涵蓋非拉丁字母表(如希臘字母和斯拉夫字母),並且對 ASCII 也給出預期的結果。
  • ch.to_lowercase()ch.to_uppercase() 傳回迭代器,該迭代器根據 Unicode 預設大小寫轉換演算法產生 ch 的小寫和大寫等效字元。
let mut upper = 's'.to_uppercase();
assert_eq!(upper.next(), Some('S'));
assert_eq!(upper.next(), None);

與整數之間的轉換

Rust 的 as 運算子會將 char 轉換為任何整數型別,靜默地遮蔽掉任何高位位元。

assert_eq!('B' as u32, 66);
assert_eq!('饂' as u8, 66); // 高位位元被截斷
assert_eq!('二' as i8, -116); // 同上

字串與文書處理

Rust 的 Stringstr 型別保證只包含格式良好的 UTF-8 編碼。函式庫透過限制建立 Stringstr 值的方式以及對它們執行的方法,確保這些值在引入時是格式良好的,並且在處理過程中保持如此。所有相關方法都保護了這一保證:沒有任何安全的操作可以引入格式不良的 UTF-8。這簡化了處理文字的程式碼。

Rust 根據方法是否需要可調整大小的緩衝區,將文書處理方法放在 strString 上。由於 String 可解參照為 &str,因此在 str 上定義的所有方法都直接可用於 String。本文將介紹這兩種型別的相關方法,並按大致功能進行分組。

建立 String 值

有多種常見方法可以用來建立 String 值:

  • String::new() 傳回一個新的空字串。它一開始沒有堆積分配的緩衝區,但會在需要時進行分配。
  • String::with_capacity(n) 傳回一個新的空字串,其緩衝區預先分配了至少可容納 n 位元組的空間。如果事先知道要建立的字串長度,這個建構函式可以讓緩衝區從一開始就調整到正確的大小,而不是在建立字串的過程中不斷調整緩衝區大小。當然,如果字串的長度超過 n 位元組,緩衝區仍然會自動擴充套件。和向量一樣,字串也有 capacityreserveshrink_to_fit 方法,但通常預設的分配邏輯就足夠了。
  • slice.to_string() 分配一個新的 String,其內容是 slice 的副本。本文一直在用類別似 "literal text".to_string() 的表示式從字串字面值建立 String
  • iter.collect() 透過連線迭代器的專案來建立字串,這些專案可以是 char&strString 值。例如,要從一個字串中移除所有空格,可以這樣寫:
let spacey = "man hat tan";
let spaceless: String = spacey.chars().filter(|c| !c.is_whitespace()).collect();
assert_eq!(spaceless, "manhattan");

程式碼解析:

let spacey = "man hat tan";
let spaceless: String = spacey.chars().filter(|c| !c.is_whitespace()).collect();
assert_eq!(spaceless, "manhattan");
  1. 變數宣告與初始化:首先宣告了一個名為 spacey 的變數,並將其初始化為字串 "man hat tan"
  2. chars() 方法:對 spacey 呼叫 chars() 方法,傳回一個迭代器,該迭代器會遍歷字串中的每個 Unicode 字元(char)。
  3. filter() 方法:使用 filter() 方法過濾掉那些是空白字元(whitespace)的字元。|c| !c.is_whitespace() 是一個閉包,它接收一個字元 c 並傳回一個布林值:如果 c 不是空白字元,則傳回 true;否則傳回 false。這樣,迭代器只會保留那些不是空白字元的字元。
  4. collect() 方法:將過濾後的迭代器收集到一個新的 String 中。這裡指定了型別為 String,Rust 會自動將迭代器中的字元收集到這個新的字串中。
  5. assert_eq! 巨集:最後,使用 assert_eq! 巨集來斷言新建立的字串 spaceless 等於 "manhattan"。如果斷言失敗,程式會 panic。

簡單檢查

以下方法可以從字串切片中取得基本資訊:

  • slice.len() 傳回 slice 的長度(以位元組為單位)。
  • 當且僅當 slice.len() == 0 時,slice.is_empty() 傳回 true
  • slice[range] 傳回一個切片,該切片借用了 slice 的指定部分。可以使用部分有界或無界的範圍。例如:
let full = "bookkeeping";
assert_eq!(&full[..4], "book");
assert_eq!(&full[5..], "eeping");
assert_eq!(&full[2..4], "ok");

程式碼解析:

let full = "bookkeeping";
assert_eq!(&full[..4], "book");
assert_eq!(&full[5..], "eeping");
assert_eq!(&full[2..4], "ok");
  1. 變數宣告:宣告了一個名為 full 的變數,其值為 "bookkeeping"
  2. 切片操作:對 full 進行切片操作,分別取出不同範圍的子字串。
    • &full[..4] 取出了索引 0 至 3 的字元(共 4 個字元),結果是 "book"
    • &full[5..] 取出了索引 5 及之後的所有字元,結果是 "eeping"
    • &full[2..4] 取出了索引 2 至 3 的字元(共 2 個字元),結果是 "ok"

新增和插入文字

以下方法可以在 String 中新增文字:

  • string.push(ch)string 的末尾追加字元 ch
  • string.push_str(slice)string 的末尾追加整個 slice 的內容。
  • 將迭代器產生的專案追加到字串中。例如:
let mut also_spaceless = "con".to_string();
also_spaceless.extend("tri but ion".split_whitespace());
assert_eq!(also_spaceless, "contribution");

程式碼解析:

let mut also_spaceless = "con".to_string();
also_spaceless.extend("tri but ion".split_whitespace());
assert_eq!(also_spaceless, "contribution");
  1. 變數宣告與初始化:宣告了一個名為 also_spaceless 的可變變數(由於使用了 mut 關鍵字),並將其初始化為 "con" 的字串表示形式(透過呼叫 to_string() 方法)。
  2. extend() 方法:對可變變數呼叫了方法,並傳入了一個迭代器,該迭代器由 "tri but ion".split_whitespace() 生成。該迭代器遍歷 "tri but ion" 中的單詞(以空白分割),並將它們追加到現有的字串後面。因此,最終結果是 "contribution"
  3. assert_eq!:最後斷言結果是否符合預期,即是否等於 "contribution"

文字迭代

Rust 提供多種方式來遍歷文字。可以使用以下方法來迭代文字:

  • 使用 .chars() 方法遍歷每個 Unicode 字元。

其他方法

其他有用的方法包括檢查某個位元組偏移量是否位於字元邊界上,用於比較、排序和雜湊等。

Rust 中的字串處理

Rust 提供了一套完整且高效的字串處理機制,無論是建立、修改還是搜尋字串,都有對應的方法可以呼叫。字串在 Rust 中主要以 String&str 兩種形式存在,前者是可變的、擁有的字串型別,而後者則是不可變的字串切片。

使用 writeln! 宏建立字串

Rust 的 writeln! 宏允許我們以格式化的方式向 String 中追加內容。這與使用 format! 宏類別似,但 writeln! 宏直接操作 String 例項。

use std::fmt::Write;

let mut letter = String::new();
writeln!(letter, "Whose {} these are I think I know", "rutabagas").unwrap();
writeln!(letter, "His house is in the village though;").unwrap();
assert_eq!(letter, "Whose rutabagas these are I think I know\nHis house is in the village though;\n");

內容解密:

  1. 使用 std::fmt::Write 特徵:為了使用 writeln! 宏,需要引入 std::fmt::Write 特徵。
  2. writeln! 宏的使用writeln! 宏類別似於 println!,但它將格式化後的字串寫入到第一個引數所指定的目標中,這裡是 letter
  3. 錯誤處理:由於 writeln! 傳回一個 Result,因此需要處理可能的錯誤。在這個例子中,使用了 unwrap 方法來簡單地處理錯誤,但在實際應用中應該使用更健壯的錯誤處理機制。

字串的拼接

Rust 中可以使用 + 運算元或 += 運算元來拼接字串。

let left = "partners".to_string();
let mut right = "crime".to_string();
assert_eq!(left + " in " + &right, "partners in crime");
right += " doesn't pay";
assert_eq!(right, "crime doesn't pay");

內容解密:

  1. + 運算元的使用:當使用 + 運算元拼接字串時,左運算元會被消耗。這意味著左邊的 String 可以被重用來存放結果,從而避免不必要的記憶體分配。
  2. += 運算元的使用+= 運算元用於將右邊的字串切片追加到左邊的可變 String 後面。
  3. 效能考量:從頭開始構建字串比從尾部開始更有效率,因為後者需要頻繁地移動已有的字元。

移除字串中的文字

String 提供了幾種方法來移除其中的文字。

let mut choco = "chocolate".to_string();
assert_eq!(choco.drain(3..6).collect::<String>(), "col");
assert_eq!(choco, "choate");

內容解密:

  1. clear 方法:清空整個字串。
  2. truncate 方法:截斷字串到指定的位元組偏移量。
  3. pop 方法:移除並傳回最後一個字元。
  4. remove 方法:移除並傳回指定索引處的字元。
  5. drain 方法:移除指定範圍內的所有字元,並傳回一個迭代器。

字串搜尋與迭代的慣例

Rust 的標準函式庫中,搜尋和迭代文字的操作遵循一些命名慣例。

  • r 開頭的操作從尾部開始處理文字。
  • _n 結尾的迭代器限制了匹配的次數。
  • _indices 結尾的迭代器除了傳回值本身,還會傳回對應的位元組偏移量。

搜尋文字的模式

Rust 的標準函式庫支援多種模式來進行文字搜尋。

let haystack = "One fine day, in the middle of the night";
assert_eq!(haystack.find(','), Some(12));
assert_eq!(haystack.find("night"), Some(35));
assert_eq!(haystack.find(char::is_whitespace), Some(3));

內容解密:

  1. 多種模式支援:可以傳入單個字元、&str 或閉包作為搜尋模式。
  2. 閉包的使用:閉包可以用來定義更複雜的匹配邏輯,例如檢查是否為空白字元。
  3. std::str::Pattern 特徵:標準函式庫中的搜尋操作接受任何實作了 std::str::Pattern 特徵的型別作為模式。雖然目前這個特徵還不穩定,但它為未來的擴充套件(如支援正規表示式)提供了可能性。