144 votes

Pourquoi est mise en majuscule de la première lettre d'une chaîne de si compliqué dans la Rouille?

J'aimerais mettre en majuscule la première lettre d'un &str. C'est un simple problème et j'espère que pour une solution simple. L'Intuition me dit de faire quelque chose comme ceci:

let mut s = "foobar";
s[0] = s[0].to_uppercase();

Mais &strs ne peut pas être indexé comme ça. La seule façon que j'ai été capable de faire, il semble trop compliquée à comprendre. - Je convertir l' &str d'un itérateur, convertir l'itérateur d'un vecteur, en majuscule le premier élément du vecteur, ce qui crée un itérateur, qui, je l'index dans, la création d'un Option, dont j'ai déballer pour me donner de la haute-tubé première lettre. Puis-je convertir le vecteur dans un itérateur, qui je le convertir en String, ce qui je convertir &str.

let s1 = "foobar";
let mut v: Vec<char> = s1.chars().collect();
v[0] = v[0].to_uppercase().nth(0).unwrap();
let s2: String = v.into_iter().collect();
let s3 = &s2;

Est-il un moyen plus simple que cela, et si oui, quoi? Si non, pourquoi est-Rouille conçu de cette façon?

Question similaire

186voto

Shepmaster Points 1732

Pourquoi est-elle si compliquée?

Nous allons casser vers le bas, ligne par ligne

let s1 = "foobar";

Nous avons créé une chaîne littérale qui est encodé en UTF-8. UTF-8 nous permet d'encoder le 1,114,112 les points de code de l'Unicode dans une manière qui est assez compact si vous venez d'une région du monde que les types de la plupart des caractères en ASCII, qui est une norme créée en 1963. UTF-8 est d'une longueur variable de codage, ce qui signifie qu'un seul point de code peut prendre de 1 à 4 octets. La plus courte des codages sont réservés pour l'ASCII, mais beaucoup de Kanji prendre 3 octets en UTF-8.

let mut v: Vec<char> = s1.chars().collect();

Cela crée un vecteur d' characteurs. Un caractère est un nombre de 32 bits qui mappe directement à un point de code. Si nous avons commencé avec de l'ASCII uniquement du texte, nous avons quadruplé nos besoins en mémoire. Si nous avions un tas de caractères à partir de l'astral, alors peut-être que nous n'avons pas utilisé beaucoup plus.

v[0] = v[0].to_uppercase().nth(0).unwrap();

Cet attrape le premier point de code et demande qu'il soit converti en majuscules variante. Malheureusement pour ceux d'entre nous qui ont grandi en parlant anglais, il n'y a pas toujours un simple one-to-one mapping d'une "petite lettre" à une "grande lettre". Note de côté: nous les appelons des haut - et bas-de-casse , car une boîte de lettres a été au-dessus de l'autre boîte de lettres de retour dans la journée.

Ce code de panique lorsqu'un point de code n'a pas de correspondant en majuscules variante. Je ne suis pas sûr si ceux-ci existent, en fait. Il pourrait aussi sémantiquement échouent lorsqu'un point de code a une majuscule variante qui a de multiples personnages, tels que le german ß. Notez que ß peuvent ne jamais être capitalisés dans Le Monde Réel, c'est le juste exemple, je peux toujours vous rappeler et de la recherche. Comme de 2017-06-29, en fait, les règles officielles de l'orthographe allemande ont été mis à jour afin que les deux "ss" et "SS" sont valables capitalisations!

let s2: String = v.into_iter().collect();

Ici nous avons convertir les caractères de retour en UTF-8 et nécessitent une nouvelle allocation pour les stocker, comme la variable d'origine a été stocké dans la mémoire constante afin de ne pas encombrer la mémoire au moment de l'exécution.

let s3 = &s2;

Et maintenant, nous prenons une référence pour qui String.

C'est un problème simple

Malheureusement, ce n'est pas vrai. Peut-être que nous devrions essayer de convertir le monde de l'Espéranto?

Je présume char::to_uppercase déjà correctement gère l'Unicode.

Oui, je l'espère bien. Malheureusement, l'Unicode n'est pas suffisant dans tous les cas. Grâce à huon pour souligner le turc je, où à la fois la partie supérieure (I) et les minuscules (i) les versions ont un point. Qui est, il n'est pas un bon de capitalisation de la lettre i; elle dépend de la locale de la le texte source ainsi.

pourquoi la nécessité pour tous les conversions de types de données?

Parce que les types de données qui vous travaillez important lorsque vous êtes inquiet au sujet de la justesse et de la performance. Un char 32-bits et une chaîne de caractères est codé en UTF-8. Ce sont des choses différentes.

l'indexation pourrait revenir un multi-octets, de caractères Unicode

Il peut être incompatibles terminologie ici. Un char est un multi-octets de caractères Unicode.

Découpage d'une chaîne de caractères est possible si vous allez octet-par-octet, mais la bibliothèque standard de panique si vous n'êtes pas sur un personnage de la frontière.

L'une des raisons qui l'indexation d'une chaîne de caractères pour obtenir un caractère n'a jamais été mis en œuvre est parce que beaucoup de gens l'utilisation abusive des chaînes comme des tableaux de caractères ASCII. L'indexation d'une chaîne de caractères pour définir un personnage ne pourra jamais être efficace, vous deviez être en mesure de remplacer de 1 à 4 octets avec une valeur qui est également de 1 à 4 octets, entraînant le reste de la chaîne de rebondir autour de beaucoup.

to_uppercase pourrait revenir un caractère majuscule

Comme mentionné ci-dessus, ß est un caractère unique qui, lorsque capitalisé, devient deux personnages.

Solutions

Voir aussi trentcl la réponse de laquelle seuls les majuscules des caractères ASCII.

D'origine

Si j'avais à écrire le code, ça ressemble:

fn some_kind_of_uppercase_first_letter(s: &str) -> String {
    let mut c = s.chars();
    match c.next() {
        None => String::new(),
        Some(f) => f.to_uppercase().chain(c).collect(),
    }
}

fn main() {
    println!("{}", some_kind_of_uppercase_first_letter("joe"));
    println!("{}", some_kind_of_uppercase_first_letter("jill"));
    println!("{}", some_kind_of_uppercase_first_letter("von Hagen"));
    println!("{}", some_kind_of_uppercase_first_letter("ß"));
}

Mais je serais probablement de recherche pour les majuscules ou unicode sur des caisses.io et de laisser quelqu'un de plus intelligent que moi à gérer.

L'amélioration de

En parlant de "quelqu'un de plus intelligent que moi", Veedrac souligne qu'il est probablement plus efficace pour convertir l'itérateur de retour dans une tranche après la première capitale codepoints sont accessibles. Cela permet une memcpy du reste des octets.

fn some_kind_of_uppercase_first_letter(s: &str) -> String {
    let mut c = s.chars();
    match c.next() {
        None => String::new(),
        Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
    }
}

20voto

trentcl Points 371

Ce n'est pas spécialement compliquée si vous êtes en mesure de limiter votre entrée ASCII uniquement des chaînes de caractères.

Depuis la Rouille 1.23, str a make_ascii_uppercase méthode (dans les anciennes Rouille versions, il était disponible par le biais de l' AsciiExt de trait). Cela signifie que vous pouvez majuscules ASCII chaîne en tranches avec une relative facilité:

fn make_ascii_titlecase(s: &mut str) {
    if let Some(r) = s.get_mut(0..1) {
        r.make_ascii_uppercase();
    }
}

C' "taylor" en "Taylor", mais il ne fera pas tourner "édouard" en "Édouard". (aire de jeux)

À utiliser avec prudence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X