Ich möchte den ersten Buchstaben von a groß schreiben &str
. Es ist ein einfaches Problem und ich hoffe auf eine einfache Lösung. Die Intuition sagt mir, dass ich so etwas tun soll:
let mut s = "foobar";
s[0] = s[0].to_uppercase();
Aber &str
s kann nicht so indiziert werden. Der einzige Weg, auf dem ich es geschafft habe, scheint übermäßig kompliziert zu sein. Ich konvertiere den &str
in einen Iterator, konvertiere den Iterator in einen Vektor, in Großbuchstaben das erste Element im Vektor, wodurch ein Iterator erstellt wird, in den ich indiziere, und einen Option
, den ich auspacke, um mir den Großbuchstaben zu geben. Dann konvertiere ich den Vektor in einen Iterator, den ich in einen konvertiere, den ich in einen String
konvertiere &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;
Gibt es einen einfacheren Weg als diesen und wenn ja, welchen? Wenn nicht, warum ist Rust so konzipiert?
ß
wenn es als Deutsch interpretiert wird. Hinweis: Es ist kein einzelnes Zeichen. Auch die Problemstellung kann kompliziert sein. Zum Beispiel wäre es unangemessen, das erste Zeichen des Nachnamens groß zu schreibenvon Hagen
. Dies alles ist ein Aspekt des Lebens in einer globalen Welt, in der es seit Tausenden von Jahren unterschiedliche Kulturen mit unterschiedlichen Praktiken gibt, und wir versuchen, all diese in 8 Bit und 2 Codezeilen zusammenzufassen.char::to_uppercase
zwar dieses Problem, aber Sie werfen seine Bemühungen weg, indem Sie nur den ersten Codepunkt (nth(0)
) anstelle aller Codepunkte nehmen, aus denen die Großschreibung bestehtAntworten:
Warum ist es so verworren?
Lassen Sie es uns Zeile für Zeile aufschlüsseln
let s1 = "foobar";
Wir haben eine Literalzeichenfolge erstellt, die in UTF-8 codiert ist . Mit UTF-8 können wir die 1.114.112 Codepunkte von Unicode auf eine ziemlich kompakte Weise codieren, wenn Sie aus einer Region der Welt stammen, in der hauptsächlich Zeichen eingegeben werden, die in ASCII , einem 1963 erstellten Standard, gefunden wurden. UTF-8 ist eine variable Länge Codierung, was bedeutet, dass ein einzelner Codepunkt 1 bis 4 Bytes benötigen kann . Die kürzeren Codierungen sind für ASCII reserviert, aber viele Kanji benötigen in UTF-8 3 Bytes .
let mut v: Vec<char> = s1.chars().collect();
Dies erzeugt einen Vektor von
char
Akteuren. Ein Zeichen ist eine 32-Bit-Zahl, die direkt einem Codepunkt zugeordnet ist. Wenn wir mit Nur-ASCII-Text begonnen haben, haben wir unseren Speicherbedarf vervierfacht. Wenn wir eine Reihe von Charakteren aus der Astralebene hätten , hätten wir vielleicht nicht viel mehr verwendet.v[0] = v[0].to_uppercase().nth(0).unwrap();
Dadurch wird der erste Codepunkt erfasst und die Konvertierung in eine Großbuchstabenvariante angefordert. Leider gibt es für diejenigen von uns, die mit Englisch aufgewachsen sind, nicht immer eine einfache Eins-zu-Eins-Zuordnung eines "kleinen Buchstabens" zu einem "großen Buchstaben" . Randnotiz: Wir nennen sie Groß- und Kleinbuchstaben, weil sich früher eine Buchstabenschachtel über der anderen Buchstabenschachtel befand .
Dieser Code gerät in Panik, wenn ein Codepunkt keine entsprechende Großbuchstabenvariante hat. Ich bin mir nicht sicher, ob diese tatsächlich existieren. Es kann auch semantisch fehlschlagen, wenn ein Codepunkt eine Großbuchstabenvariante mit mehreren Zeichen enthält, z. B. Deutsch
ß
. Beachten Sie, dass ß in der realen Welt möglicherweise nie groß geschrieben wird. Dies ist das einzige Beispiel, an das ich mich immer erinnern und nach dem ich suchen kann. Ab dem 29.06.2017 wurden die offiziellen Regeln der deutschen Rechtschreibung aktualisiert, sodass sowohl "ẞ" als auch "SS" gültige Großschreibung sind !let s2: String = v.into_iter().collect();
Hier konvertieren wir die Zeichen zurück in UTF-8 und benötigen eine neue Zuordnung, um sie zu speichern, da die ursprüngliche Variable im konstanten Speicher gespeichert wurde, um zur Laufzeit keinen Speicher zu belegen.
let s3 = &s2;
Und jetzt nehmen wir einen Hinweis darauf
String
.Dies ist leider nicht wahr. Vielleicht sollten wir uns bemühen, die Welt zum Esperanto zu konvertieren ?
Ja, das hoffe ich sehr. Leider reicht Unicode nicht in allen Fällen aus. Vielen Dank an huon für den Hinweis auf das türkische I , bei dem sowohl die Groß- ( İ ) als auch die Kleinbuchstaben ( i ) einen Punkt haben. Das heißt, es nicht ist eine angemessene Kapitalisierung des Briefes
i
; Dies hängt auch vom Gebietsschema des Quelltextes ab.Weil die Datentypen, mit denen Sie arbeiten, wichtig sind, wenn Sie sich um Korrektheit und Leistung sorgen. A
char
ist 32-Bit und eine Zeichenfolge ist UTF-8-codiert. Sie sind verschiedene Dinge.Möglicherweise gibt es hier eine nicht übereinstimmende Terminologie. A
char
ist ein Multi-Byte-Unicode-Zeichen.Das Schneiden einer Zeichenfolge ist möglich, wenn Sie Byte für Byte arbeiten. Die Standardbibliothek gerät jedoch in Panik, wenn Sie sich nicht an einer Zeichengrenze befinden.
Einer der Gründe, warum die Indizierung einer Zeichenfolge zum Abrufen eines Zeichens nie implementiert wurde, ist, dass so viele Benutzer Zeichenfolgen als Arrays von ASCII-Zeichen missbrauchen. Das Indizieren einer Zeichenfolge zum Festlegen eines Zeichens könnte niemals effizient sein - Sie müssten in der Lage sein, 1-4 Bytes durch einen Wert zu ersetzen, der ebenfalls 1-4 Bytes beträgt, was dazu führt, dass der Rest der Zeichenfolge ziemlich viel herumspringt.
Wie oben erwähnt,
ß
handelt es sich um ein einzelnes Zeichen, das bei Großschreibung zu zwei Zeichen wird .Lösungen
Siehe auch die Antwort von trentcl, bei der nur ASCII-Zeichen in Großbuchstaben geschrieben werden.
Original
Wenn ich den Code schreiben müsste, würde er so aussehen:
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("ß")); }
Aber ich würde wahrscheinlich auf crates.io nach Großbuchstaben oder Unicode suchen und jemanden schlauer als mich damit umgehen lassen.
Verbessert
Veedrac spricht von "jemandem, der schlauer ist als ich" und weist darauf hin, dass es wahrscheinlich effizienter ist, den Iterator nach dem Zugriff auf die ersten Großbuchstaben wieder in ein Slice umzuwandeln. Dies ermöglicht einen
memcpy
Rest der Bytes.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(), } }
quelle
Ja und nein. Ihr Code ist, wie die andere Antwort hervorhob, nicht korrekt und gerät in Panik, wenn Sie ihm so etwas wie བོད་ སྐད་ ལ་ geben. Dies ist mit der Standardbibliothek von Rust noch schwieriger als ursprünglich angenommen.
Rust wurde jedoch entwickelt, um die Wiederverwendung von Code zu fördern und das Einbringen von Bibliotheken zu vereinfachen. Die idiomatische Art, einen String groß zu schreiben, ist also eigentlich ziemlich schmackhaft:
extern crate inflector; use inflector::Inflector; let capitalized = "some string".to_title_case();
quelle
.to_sentence_case()
.Es ist nicht besonders kompliziert, wenn Sie Ihre Eingabe auf reine ASCII-Zeichenfolgen beschränken können.
Da Rust 1.23
str
einemake_ascii_uppercase
Methode hat (in älteren Rust-Versionen war sie über dasAsciiExt
Merkmal verfügbar ). Dies bedeutet, dass Sie relativ einfach nur ASCII-Zeichenfolgen in Großbuchstaben schreiben können:fn make_ascii_titlecase(s: &mut str) { if let Some(r) = s.get_mut(0..1) { r.make_ascii_uppercase(); } }
Dies wird sich
"taylor"
in"Taylor"
, aber es lässt sich nicht einschalten"édouard"
zu"Édouard"
. ( Spielplatz )Mit Vorsicht verwenden.
quelle
r
veränderlich? Ich sehe dass
ist veränderlichstr
. Ohhhh ok: Ich habe die Antwort auf meine eigene Frage:get_mut
(hier mit einem Bereich genannt) kehrt explizit zurückOption<&mut>
.Auf diese Weise habe ich dieses Problem gelöst. Beachten Sie, dass ich überprüfen musste, ob self nicht ascii ist, bevor ich in Großbuchstaben umwandelte.
trait TitleCase { fn title(&self) -> String; } impl TitleCase for &str { fn title(&self) -> String { if !self.is_ascii() || self.is_empty() { return String::from(*self); } let (head, tail) = self.split_at(1); head.to_uppercase() + tail } } pub fn main() { println!("{}", "bruno".title()); println!("{}", "b".title()); println!("{}", "🦀".title()); println!("{}", "ß".title()); println!("{}", "".title()); println!("{}", "བོད་སྐད་ལ".title()); }
Ausgabe
quelle
Hier ist eine Version, die etwas langsamer ist als die verbesserte Version von @ Shepmaster, aber auch idiomatischer :
fn capitalize_first(s: &str) -> String { let mut chars = s.chars(); chars .next() .map(|first_letter| first_letter.to_uppercase()) .into_iter() .flatten() .chain(chars) .collect() }
quelle
Ich habe es so gemacht:
fn str_cap(s: &str) -> String { format!("{}{}", (&s[..1].to_string()).to_uppercase(), &s[1..]) }
Wenn es sich nicht um eine ASCII-Zeichenfolge handelt:
fn str_cap(s: &str) -> String { format!("{}{}", s.chars().next().unwrap().to_uppercase(), s.chars().skip(1).collect::<String>()) }
quelle