Rust 的 char 型別為 32 位元值,儲存 Unicode 碼位,範圍介於 0 至 0xd7ff 或 0xe000 至 0x10ffff 之間。標準函式庫提供多種方法,用於字元分類別、數字處理、大小寫轉換以及與整數的轉換。String 和 str 型別保證只包含格式良好的 UTF-8 編碼,簡化了文書處理。建立 String 值可以使用 String::new()、String::with_capacity(n)、slice.to_string() 和 iter.collect() 等方法。str 和 String 提供了豐富的字串處理方法,例如 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),其中num是u32型別的值。否則,傳回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 的 String 和 str 型別保證只包含格式良好的 UTF-8 編碼。函式庫透過限制建立 String 和 str 值的方式以及對它們執行的方法,確保這些值在引入時是格式良好的,並且在處理過程中保持如此。所有相關方法都保護了這一保證:沒有任何安全的操作可以引入格式不良的 UTF-8。這簡化了處理文字的程式碼。
Rust 根據方法是否需要可調整大小的緩衝區,將文書處理方法放在 str 或 String 上。由於 String 可解參照為 &str,因此在 str 上定義的所有方法都直接可用於 String。本文將介紹這兩種型別的相關方法,並按大致功能進行分組。
建立 String 值
有多種常見方法可以用來建立 String 值:
String::new()傳回一個新的空字串。它一開始沒有堆積分配的緩衝區,但會在需要時進行分配。String::with_capacity(n)傳回一個新的空字串,其緩衝區預先分配了至少可容納n位元組的空間。如果事先知道要建立的字串長度,這個建構函式可以讓緩衝區從一開始就調整到正確的大小,而不是在建立字串的過程中不斷調整緩衝區大小。當然,如果字串的長度超過n位元組,緩衝區仍然會自動擴充套件。和向量一樣,字串也有capacity、reserve和shrink_to_fit方法,但通常預設的分配邏輯就足夠了。slice.to_string()分配一個新的String,其內容是slice的副本。本文一直在用類別似"literal text".to_string()的表示式從字串字面值建立String。iter.collect()透過連線迭代器的專案來建立字串,這些專案可以是char、&str或String值。例如,要從一個字串中移除所有空格,可以這樣寫:
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");
- 變數宣告與初始化:首先宣告了一個名為
spacey的變數,並將其初始化為字串"man hat tan"。 chars()方法:對spacey呼叫chars()方法,傳回一個迭代器,該迭代器會遍歷字串中的每個 Unicode 字元(char)。filter()方法:使用filter()方法過濾掉那些是空白字元(whitespace)的字元。|c| !c.is_whitespace()是一個閉包,它接收一個字元c並傳回一個布林值:如果c不是空白字元,則傳回true;否則傳回false。這樣,迭代器只會保留那些不是空白字元的字元。collect()方法:將過濾後的迭代器收集到一個新的String中。這裡指定了型別為String,Rust 會自動將迭代器中的字元收集到這個新的字串中。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");
- 變數宣告:宣告了一個名為
full的變數,其值為"bookkeeping"。 - 切片操作:對
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");
- 變數宣告與初始化:宣告了一個名為
also_spaceless的可變變數(由於使用了mut關鍵字),並將其初始化為"con"的字串表示形式(透過呼叫to_string()方法)。 extend()方法:對可變變數呼叫了方法,並傳入了一個迭代器,該迭代器由"tri but ion".split_whitespace()生成。該迭代器遍歷"tri but ion"中的單詞(以空白分割),並將它們追加到現有的字串後面。因此,最終結果是"contribution"。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");
內容解密:
- 使用
std::fmt::Write特徵:為了使用writeln!宏,需要引入std::fmt::Write特徵。 writeln!宏的使用:writeln!宏類別似於println!,但它將格式化後的字串寫入到第一個引數所指定的目標中,這裡是letter。- 錯誤處理:由於
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");
內容解密:
+運算元的使用:當使用+運算元拼接字串時,左運算元會被消耗。這意味著左邊的String可以被重用來存放結果,從而避免不必要的記憶體分配。+=運算元的使用:+=運算元用於將右邊的字串切片追加到左邊的可變String後面。- 效能考量:從頭開始構建字串比從尾部開始更有效率,因為後者需要頻繁地移動已有的字元。
移除字串中的文字
String 提供了幾種方法來移除其中的文字。
let mut choco = "chocolate".to_string();
assert_eq!(choco.drain(3..6).collect::<String>(), "col");
assert_eq!(choco, "choate");
內容解密:
clear方法:清空整個字串。truncate方法:截斷字串到指定的位元組偏移量。pop方法:移除並傳回最後一個字元。remove方法:移除並傳回指定索引處的字元。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));
內容解密:
- 多種模式支援:可以傳入單個字元、
&str或閉包作為搜尋模式。 - 閉包的使用:閉包可以用來定義更複雜的匹配邏輯,例如檢查是否為空白字元。
std::str::Pattern特徵:標準函式庫中的搜尋操作接受任何實作了std::str::Pattern特徵的型別作為模式。雖然目前這個特徵還不穩定,但它為未來的擴充套件(如支援正規表示式)提供了可能性。