要比较两个字符串(不区分大小写),似乎需要先将字符串转换为小写版本:
let a_lower = a.to_lowercase();
let b_lower = b.to_lowercase();
a_lower.cmp(&b_lower)
有没有一种方法可以比较字符串而忽略大小写,且不需要创建临时的小写字符串呢?即通过迭代字符,在那里执行转换为小写并比较结果的方法?
要比较两个字符串(不区分大小写),似乎需要先将字符串转换为小写版本:
let a_lower = a.to_lowercase();
let b_lower = b.to_lowercase();
a_lower.cmp(&b_lower)
有没有一种方法可以比较字符串而忽略大小写,且不需要创建临时的小写字符串呢?即通过迭代字符,在那里执行转换为小写并比较结果的方法?
如果您只使用ASCII字符,可以使用eq_ignore_ascii_case
:
assert!("Ferris".eq_ignore_ascii_case("FERRIS"));
虽然没有内置的方法,但你可以编写一个方法来实现你所描述的功能,假设你只关心ASCII输入。
use itertools::{EitherOrBoth::*, Itertools as _}; // 0.9.0
use std::cmp::Ordering;
fn cmp_ignore_case_ascii(a: &str, b: &str) -> Ordering {
a.bytes()
.zip_longest(b.bytes())
.map(|ab| match ab {
Left(_) => Ordering::Greater,
Right(_) => Ordering::Less,
Both(a, b) => a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()),
})
.find(|&ordering| ordering != Ordering::Equal)
.unwrap_or(Ordering::Equal)
}
正如下面的一些评论所指出的,对于UTF-8编码,忽略大小写的比较在未对整个字符串进行操作时无法正常工作,即使对整个字符串进行操作,某些情况下也存在多个大小写转换的表示形式,可能会产生意外的结果。
考虑到这些警告,以下代码可处理许多额外情况(例如大多数带重音符号的拉丁字符),并根据您的需求可能是令人满意的。
fn cmp_ignore_case_utf8(a: &str, b: &str) -> Ordering {
a.chars()
.flat_map(char::to_lowercase)
.zip_longest(b.chars().flat_map(char::to_lowercase))
.map(|ab| match ab {
Left(_) => Ordering::Greater,
Right(_) => Ordering::Less,
Both(a, b) => a.cmp(&b),
})
.find(|&ordering| ordering != Ordering::Equal)
.unwrap_or(Ordering::Equal)
}
str::chars
зҡ„д»»дҪ•ж–№жі•йғҪж— жі•жӯЈзЎ®жҜ”иҫғUnicodeеӯ—з¬ҰдёІгҖӮ - mcartonstr::chars
迭代码点,但是因为预组合,有可能存在规范等效但在技术层面有不同内容的字符串。chars
不会考虑到这些信息。另一个大问题是大小写转换是与语言环境相关的,例如I
的小写形式是i
... 除非你使用土耳其语,那么它的小写形式就是 ı
。我相信在那里还有其他陷阱。 - Masklinnstring.to_lowercase()
是否考虑了语言环境,以便一个字符可以变成多个字符? - Thomas S.支持UNICODE的最佳方式是使用to_lowercase()
或to_uppercase()
。
这是因为UNICODE有许多注意事项,而这些函数可以处理大多数情况。但也有一些特定于语言环境的字符串无法正确处理。
let left = "first".to_string();
let right = "FiRsT".to_string();
assert!(left.to_lowercase() == right.to_lowercase());
可以迭代并返回第一个不相等的字符,因此实际上您只分配了一个字符。但是,使用chars
函数进行迭代不能处理UNICODE可能会遇到的所有情况。
有关详细信息,请参见Peter Hall的answer。
如果仅使用ASCII,则最有效的方法是使用eq_ignore_ascii_case(根据Ibraheem Ahmed的answer)。这不会分配/复制临时文件。
只有在您的代码控制至少一侧比较并且您确定它仅包含ASCII时,才能使用此方法。
assert!("Ferris".eq_ignore_ascii_case("FERRIS"));
Rust的大小写转换函数在处理区域设置方面尽力而为,但并不支持所有区域设置。为了实现适当的国际化支持,您需要寻找其他能够完成此任务的crate。