2016-07-19 4 views
4

Der erwartete Ansatz von String.truncate(usize) schlägt fehl, weil Unicode-Zeichen nicht berücksichtigt werden (was verwirrend ist, wenn man bedenkt, dass Rust Zeichenketten als Unicode behandelt).Wie kann ich eine Zeichenfolge abschneiden, um maximal N Zeichen zu haben?

let mut s = "ボルテックス".to_string(); 
s.truncate(4); 

Faden 'Behauptung' bei Panik geraten 'fehlgeschlagen: self.is_char_boundary (new_len)'

Zusätzlich truncate modifiziert das Original-Zeichenkette, die nicht immer erwünscht ist.

Das Beste, was ich habe, ist zu char s konvertieren und in eine String sammeln.

fn truncate(s: String, max_width: usize) -> String { 
    s.chars().take(max_width).collect() 
} 

z.B.

fn main() { 
    assert_eq!(truncate("ボルテックス".to_string(), 0), ""); 
    assert_eq!(truncate("ボルテックス".to_string(), 4), "ボルテッ"); 
    assert_eq!(truncate("ボルテックス".to_string(), 100), "ボルテックス"); 
    assert_eq!(truncate("hello".to_string(), 4), "hell"); 
} 

Allerdings fühlt sich das sehr schwer an.

+7

Unicode ist verdammt kompliziert. Sind Sie sicher, dass Sie 'char' (was Codepunkten entspricht) als Einheit und nicht als Graphem-Cluster verwenden möchten? – delnan

+2

Tatsächlich ist die andere Richtung genauso gültig: Setzen Sie eine Grenze für die Anzahl * Bytes *, die die UTF-8-Kodierung benötigt (Sie müssen einige Zeichen entfernen, um ganze Zeichen zu entfernen.) — nehmen Sie so viele Zeichen wie möglich N Bytes). Dies entspricht zwar nicht der Wahrnehmung von Charakterzählungen durch Menschen, ist jedoch sinnvoll, wenn die Einschränkung speichermotiviert ist (z. B. die Größe einer Datenbankspalte). – delnan

Antwort

11

Achten Sie darauf, lesen und verstehen delnan's point:

Unicode kompliziert ist freaking. Sind Sie sicher, dass Sie char (was entspricht Codepunkten) als Einheit und nicht Grapheme-Cluster?

Der Rest dieser Antwort vorausgesetzt, dass Sie einen guten Grund für die Verwendung vonchar und nicht Grapheme haben.

die verblüffende ist unter Berücksichtigung Rust Strings als Unicode behandelt

Dies ist nicht korrekt; Rost behandelt Strings als UTF-8. In UTF-8 wird jeder Codepunkt einer variablen Anzahl von Bytes zugeordnet. Es gibt keinen O(1) Algorithmus, um "6 Zeichen" in "N Bytes" umzuwandeln, also versteckt die Standardbibliothek das nicht vor Ihnen.

Sie können char_indices verwenden, um durch den String Zeichen für Zeichen zu treten und den Byte-Index des Zeichens erhalten:

fn truncate(s: &str, max_chars: usize) -> &str { 
    match s.char_indices().nth(max_chars) { 
     None => s, 
     Some((idx, _)) => &s[..idx], 
    } 
} 

fn main() { 
    assert_eq!(truncate("ボルテックス", 0), ""); 
    assert_eq!(truncate("ボルテックス", 4), "ボルテッ"); 
    assert_eq!(truncate("ボルテックス", 100), "ボルテックス"); 
    assert_eq!(truncate("hello", 4), "hell"); 
} 

Dies gibt auch eine Scheibe, die Sie auswählen können, in eine neue Zuordnung zu bewegen, wenn Sie brauchen oder String an Ort und Stelle:

+0

Wie unterscheidet sich 'char_indices()' von der Verwendung von 'chars()'? –

+2

@Peter 'chars' gibt nur die Zeichen zurück. 'char_indices' ist im Konzept ähnlich zu' chars(). enumerate() ', außer dass es den tatsächlichen Index des 'u8' zurückgibt, an dem das Zeichen im ursprünglichen 'str' beginnt. – LinearZoetrope

+1

'.skip (max_chars) .next()' → '.nth (max_chars)'. – Veedrac

Verwandte Themen