在開發程式的時候我們時常會需要判斷某個字串的開頭或結尾是否符合某個字串,例如判斷網址是否以「http」或是「https」開頭時,或是判斷檔案名稱是否以「.jpg」或是「.png」結尾時。然而在上述提到的例子中,通訊協定或是檔案副檔名都是可忽略大小寫的,Rust程式語言雖然有提供eq_ignore_ascii_case方法,卻沒有提供starts_with_ignore_ascii_caseends_with_ignore_ascii_case等方法,在判斷上述例子時需要程式設計師自行先把字串做轉大寫或是轉小寫的動作才能進行判斷。



如以下程式,可以判斷一個檔案路徑的副檔名是否為png。

if path.to_lowercase().ends_with(".png") {
    ...
}

以上程式,因為我們不知道path的副檔名是pngPNG,還是PnG,因此只好先將其透過to_lowercase方法來轉成小寫,如此一來只要用ends_with來判斷它是否以.png結尾就好。

然而,對字串使用to_lowercase方法,會另外產生新的字串出來,這會讓程式記憶體用得更多,程式效能也會變得比較不好。

所以,比較好的作法是,先取得path的副檔名部份的字串切片,再利用Rust程式語言的字串內建的eq_ignore_ascii_case方法,來做判斷。程式改寫如下:

if path[(path.len() - ".png".len())..].eq_ignore_ascii_case(".png") {
    ...
}

這樣寫起來有點麻煩,而且還有可能因path本身沒有到達.png的長度,而導致程式發生panic,因此在將path轉成更小範圍的字串切片前還需進行長度的檢查。

Starts/Ends With Caseless

「Starts/Ends With Caseless」是筆者開發的套件,提供了StartsWithCaselessEndsWithCaseless特性,可以擴充有實作AsRef<str>特性的型別,使它們擁有starts_with_caseless_asciistarts_with_caselessends_with_caseless_asciiends_with_caseless等方法。

Crates.io

https://crates.io/crates/starts-ends-with-caseless

Cargo.toml

starts-ends-with-caseless = "*"

使用方法

文章一開始舉的例子,可以用EndsWithCaseless特性改寫成以下程式:

extern crate starts_ends_with_caseless;

use starts_ends_with_caseless::EndsWithCaseless;

...

if path.ends_with_caseless_ascii(".png") {
    ...
}

方法名稱後綴有_ascii和無_ascii的差異在於,無_ascii的方法會用來忽略Unicode定義的大小寫,而有_ascii的方法則只會用來忽略ASCII定義的大小寫。

extern crate starts_ends_with_caseless;

use starts_ends_with_caseless::EndsWithCaseless;

assert_eq!(false, "123 Maße".ends_with_caseless_ascii("MASSE"));

assert_eq!(true, "123 Maße".ends_with_caseless("MASSE"));

此外,如果有多個前綴或是後綴需要判斷的話,這個套件還提供StartsWithCaselessMultipleEndsWithCaselessMultiple特性,可以擴充有實作AsRef<str>特性的型別,使它們擁有starts_with_caseless_ascii_multiplestarts_with_caseless_multipleends_with_caseless_ascii_multipleends_with_caseless_multiple等方法。

這些方法可以傳入一個由AsRef<str>特性所組成的陣列或切片作為要判斷的前綴或後綴,回傳的資料型別是Option<usize>,也就是如果提供的前綴或後綴有匹配到的話,會回傳其位於陣列或切片中的索引位置。

例如:

extern crate starts_ends_with_caseless;

use starts_ends_with_caseless::EndsWithCaselessMultiple;

assert_eq!(Some(1), "photo.jpg".ends_with_caseless_ascii_multiple(&[".png", ".jpg", ".gif"]));

以上程式,可以快速判斷出檔名中的副檔名是否為png、jpg還是gif。