summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-02-25 17:42:28 +0800
committer魏曹先生 <1992414357@qq.com>2026-02-25 17:42:28 +0800
commit53141f612ab43136b4b1db7406ac71bb97284460 (patch)
tree12b722774fb2f45a8f3d761239829e3bcbb916d6
parentfcbd18c4b7d90388a9a2b9e28555d2526727958c (diff)
Compare strings with numeric ordering for digits
-rw-r--r--systems/sheet/src/mapping.rs76
1 files changed, 66 insertions, 10 deletions
diff --git a/systems/sheet/src/mapping.rs b/systems/sheet/src/mapping.rs
index 469e083..f509c0b 100644
--- a/systems/sheet/src/mapping.rs
+++ b/systems/sheet/src/mapping.rs
@@ -645,7 +645,7 @@ impl Ord for LocalMapping {
/// Compare two `Vec<String>` according to a specific ordering:
/// 1. ASCII symbols (excluding letters and digits)
/// 2. Letters (Aa-Zz, case‑sensitive, uppercase before lowercase)
-/// 3. Digits (0‑9)
+/// 3. Digits (0‑9) - compared numerically
/// 4. All other Unicode characters (in their natural order)
///
/// The comparison is lexicographic: the first differing element determines the order.
@@ -664,15 +664,28 @@ fn compare_vec_string(a: &Vec<String>, b: &Vec<String>) -> std::cmp::Ordering {
/// Compare two individual strings with the same ordering rules.
fn compare_string(a: &str, b: &str) -> std::cmp::Ordering {
- let mut a_chars = a.chars();
- let mut b_chars = b.chars();
+ let mut a_chars = a.chars().peekable();
+ let mut b_chars = b.chars().peekable();
loop {
- match (a_chars.next(), b_chars.next()) {
- (Some(ca), Some(cb)) => {
- let ord = compare_char(ca, cb);
- if ord != Ordering::Equal {
- return ord;
+ match (a_chars.peek(), b_chars.peek()) {
+ (Some(&ca), Some(&cb)) => {
+ if ca.is_ascii_digit() && cb.is_ascii_digit() {
+ // Parse both numbers and compare numerically
+ let a_num = parse_number(&mut a_chars);
+ let b_num = parse_number(&mut b_chars);
+ match a_num.cmp(&b_num) {
+ Ordering::Equal => continue,
+ ord => return ord,
+ }
+ } else {
+ // Non-digit comparison
+ let ord = compare_char(ca, cb);
+ if ord != Ordering::Equal {
+ return ord;
+ }
+ a_chars.next();
+ b_chars.next();
}
}
(None, Some(_)) => return Ordering::Less,
@@ -682,10 +695,26 @@ fn compare_string(a: &str, b: &str) -> std::cmp::Ordering {
}
}
+/// Parse a number from the character iterator.
+/// Consumes consecutive ASCII digits and returns the parsed u64.
+/// Assumes the first character is already verified to be a digit.
+fn parse_number<I: Iterator<Item = char>>(chars: &mut std::iter::Peekable<I>) -> u64 {
+ let mut num = 0;
+ while let Some(&c) = chars.peek() {
+ if c.is_ascii_digit() {
+ num = num * 10 + (c as u64 - '0' as u64);
+ chars.next();
+ } else {
+ break;
+ }
+ }
+ num
+}
+
/// Compare two characters according to the ordering:
/// 1. ASCII symbols (non‑letter, non‑digit)
/// 2. Letters (A‑Z then a‑z)
-/// 3. Digits (0‑9)
+/// 3. Digits (0‑9) - note: digits are handled specially in compare_string
/// 4. Other Unicode
fn compare_char(a: char, b: char) -> std::cmp::Ordering {
let group_a = char_group(a);
@@ -708,7 +737,11 @@ fn compare_char(a: char, b: char) -> std::cmp::Ordering {
a.cmp(&b)
}
}
- CharGroup::Digit => a.cmp(&b), // Digits 0‑9
+ CharGroup::Digit => {
+ // Digits should be compared numerically, but this is only reached
+ // when comparing single digits (not part of a longer number).
+ a.cmp(&b)
+ }
CharGroup::Other => a.cmp(&b), // Other Unicode (natural order)
}
}
@@ -741,3 +774,26 @@ fn test_compare_char_groups() {
assert!(compare_string("a", "0") == Ordering::Less);
assert!(compare_string("9", "你") == Ordering::Less);
}
+
+#[test]
+fn test_numeric_ordering() {
+ // Test numeric ordering
+ assert!(compare_string("0", "1") == Ordering::Less);
+ assert!(compare_string("1", "0") == Ordering::Greater);
+ assert!(compare_string("9", "10") == Ordering::Less);
+ assert!(compare_string("10", "9") == Ordering::Greater);
+ assert!(compare_string("99", "100") == Ordering::Less);
+ assert!(compare_string("100", "99") == Ordering::Greater);
+ assert!(compare_string("001", "1") == Ordering::Equal); // "001" numerically equals "1"
+ assert!(compare_string("01", "1") == Ordering::Equal); // "01" numerically equals "1"
+
+ // Test mixed strings
+ assert!(compare_string("Frame-9", "Frame-10") == Ordering::Less);
+ assert!(compare_string("Frame-10", "Frame-9") == Ordering::Greater);
+ assert!(compare_string("Frame-99", "Frame-100") == Ordering::Less);
+ assert!(compare_string("Frame-100", "Frame-99") == Ordering::Greater);
+
+ // Test that numbers are compared as whole numbers, not digit-by-digit
+ assert!(compare_string("123", "23") == Ordering::Greater); // 123 > 23
+ assert!(compare_string("23", "123") == Ordering::Less); // 23 < 123
+}