Dlaczego jest tak zagmatwany?
Podzielmy to, linia po linii
let s1 = "foobar";
Stworzyliśmy ciąg literału zakodowany w UTF-8 . UTF-8 pozwala kodować 1,114,112 punktów kodowych z Unicode w sposób, który jest dość zwarta, jeśli pochodzą z regionu świata, że typy w większości znaków znalezionych w kodzie ASCII , standard utworzony w 1963. UTF-8 jest zmienna długość kodowanie, co oznacza, że pojedynczy punkt kodowy może zająć od 1 do 4 bajtów . Krótsze kodowanie jest zarezerwowane dla ASCII, ale wiele Kanji zajmuje 3 bajty w UTF-8 .
let mut v: Vec<char> = s1.chars().collect();
Tworzy to wektor char
aktorów. Znak to 32-bitowa liczba, która jest bezpośrednio mapowana do punktu kodowego. Jeśli zaczęliśmy od tekstu tylko ASCII, czterokrotnie zwiększyliśmy nasze wymagania dotyczące pamięci. Gdybyśmy mieli kilka postaci z planu astralnego , być może nie używaliśmy dużo więcej.
v[0] = v[0].to_uppercase().nth(0).unwrap();
Spowoduje to pobranie pierwszego punktu kodowego i zażądanie konwersji na wariant z dużymi literami. Na nieszczęście dla tych z nas, którzy dorastali mówiąc po angielsku, nie zawsze istnieje proste odwzorowanie „małej litery” na „dużą” . Uwaga dodatkowa: nazywamy je dużymi i małymi literami, ponieważ w tamtych czasach jedno pudełko z literami znajdowało się nad drugim .
Ten kod wywoła panikę, gdy punkt kodowy nie ma odpowiadającego mu wariantu z dużymi literami. Właściwie nie jestem pewien, czy takie istnieją. Może również semantycznie zawieść, gdy punkt kodowy ma wariant z wielkimi literami, który ma wiele znaków, na przykład niemiecki ß
. Zauważ, że ß może nigdy nie być pisane wielką literą w The Real World, jest to jedyny przykład, który zawsze pamiętam i którego szukam. W rzeczywistości od 2017-06-29 oficjalne zasady pisowni niemieckiej zostały zaktualizowane, aby zarówno „ẞ”, jak i „SS” były poprawnymi wielkimi literami !
let s2: String = v.into_iter().collect();
Tutaj konwertujemy znaki z powrotem do UTF-8 i wymagamy nowej alokacji, aby je przechowywać, ponieważ oryginalna zmienna była przechowywana w stałej pamięci, aby nie zajmować pamięci w czasie wykonywania.
let s3 = &s2;
A teraz odniesiemy się do tego String
.
To prosty problem
Niestety to nieprawda. Może powinniśmy podjąć próbę nawrócenia świata na esperanto ?
Zakładam, że char::to_uppercase
już poprawnie obsługuje Unicode.
Tak, mam taką nadzieję. Niestety, Unicode nie we wszystkich przypadkach wystarcza. Dzięki Huon dla wskazując na tureckiej I , gdzie zarówno górna ( İ ) i małe litery ( I ) wersje mają kropkę. Oznacza to, że nie ma jednej właściwej wielkości litery i
; zależy to również od ustawień regionalnych tekstu źródłowego.
dlaczego potrzeba wszystkich konwersji typu danych?
Ponieważ typy danych, z którymi pracujesz, są ważne, gdy martwisz się o poprawność i wydajność. A char
ma 32 bity, a łańcuch jest zakodowany w formacie UTF-8. To są różne rzeczy.
indeksowanie może zwrócić wielobajtowy znak Unicode
W tym miejscu może występować niedopasowana terminologia. A char
to wielobajtowy znak Unicode.
Cięcie łańcucha jest możliwe, jeśli idziesz bajt po bajcie, ale standardowa biblioteka będzie panikować, jeśli nie jesteś na granicy znaków.
Jednym z powodów, dla których indeksowanie łańcucha w celu uzyskania znaku nigdy nie zostało zaimplementowane, jest to, że tak wiele osób niewłaściwie używa ciągów znaków jako tablic znaków ASCII. Indeksowanie ciągu znaków w celu ustawienia znaku nigdy nie mogłoby być wydajne - musiałbyś być w stanie zastąpić 1-4 bajty wartością, która również ma 1-4 bajty, powodując, że reszta ciągu odbija się dość często.
to_uppercase
może zwrócić wielką literę
Jak wspomniano powyżej, ß
to pojedynczy znak, który po zapisaniu wielkimi literami staje się dwoma znakami .
Rozwiązania
Zobacz także odpowiedź trentcl, która zawiera tylko wielkie litery w znakach ASCII.
Oryginalny
Gdybym miał napisać kod, wyglądałby tak:
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("ß"));
}
Ale prawdopodobnie wyszukałbym wielkie litery lub unicode w crates.io i pozwoliłbym zająć się tym komuś mądrzejszemu ode mnie.
Ulepszony
Mówiąc o „kimś mądrzejszym ode mnie”, Veedrac wskazuje, że prawdopodobnie bardziej wydajne jest przekonwertowanie iteratora z powrotem na plasterek po uzyskaniu dostępu do pierwszych dużych punktów kodu . Pozwala to memcpy
na resztę bajtów.
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(),
}
}
ß
przypadku interpretacji języka niemieckiego należy pisać wielkimi literami . Wskazówka: to nie jest pojedynczy znak. Nawet stwierdzenie problemu może być skomplikowane. Na przykład niewłaściwe byłoby pisanie dużej litery pierwszego znaku nazwiskavon Hagen
. To wszystko jest aspektem życia w globalnym świecie, w którym od tysięcy lat istniały rozbieżne kultury z różnymi praktykami, a my staramy się zgnieść to wszystko na 8 bitów i 2 linie kodu.