1. ホーム
  2. string

[解決済み] なぜRustでは文字列の最初の文字を大文字にするのが複雑なのですか?

2022-11-08 15:38:09

疑問点

の頭文字を大文字にしたいのですが。 &str . これは単純な問題で、私は単純な解決策を望んでいます。直感的に、このようなことをするように言われます。

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

しかし &str をこのようにインデックス化することはできません。私ができる唯一の方法は、過度に複雑なようです。私は &str をイテレータに変換し、イテレータをベクトルに変換し、ベクトルの最初の項目を大文字に変換してイテレータを作成し、そこにインデックスを作成して Option を作成し、それをアンラップして大文字の最初の文字を得ます。次に、ベクトルをイテレータに変換し、それを String に変換し、それを &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;

これより簡単な方法はありますか?あるとすれば、それは何ですか?ないとしたら、なぜRustはこのように設計されているのでしょうか?

類似の質問

どのように解決するのですか?

なぜこんなに複雑なのですか?

一行ずつ分解してみましょう。

let s1 = "foobar";

でエンコードされた文字列をリテラルで作成しました。 UTF-8 . UTF-8では、1,114,112をエンコードすることができます。 コードポイント ユニコード に含まれる文字を主に入力する世界の地域から来た場合、かなりコンパクトな方法で ASCII で見られる文字を主に入力している世界の地域からすれば、かなりコンパクトな方法です。UTF-8は 可変長 であり、1 つのコードポイントが は1バイトから4バイトまで . より短いエンコーディングは ASCII のために予約されていますが 多くの漢字は UTF-8 で 3 バイトになります。 .

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

これは、ベクトル char アクターが作成されます。文字はコードポイントに直接対応する32ビットの数値です。もしASCIIだけのテキストから始めたとしたら、必要なメモリは4倍になってしまいます。の文字をたくさん持っていたとしたら アストラル平面 の文字が大量にあったとしても、それ以上は使っていないかもしれません。

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

これは最初のコードポイントを掴んで、それを大文字のバリアントに変換するように要求します。英語で育った私たちには残念なことですが、このコードポイントには は小さな文字から大きな文字への単純な一対一のマッピングとは限りません。 . 余談ですが、私たちは大文字と小文字のことを と呼んでいます。これは、昔は片方の文字がもう片方の文字の箱の上にあったからです。 .

このコードはコードポイントに対応する大文字の variant がない場合にパニックを起こします。実際、それが存在するかどうかはわかりません。また、コードポイントが複数の文字を持つ大文字の variant を持つ場合、意味的に失敗する可能性があります、 例えば、ドイツ語の ß . ßが実際にThe Real Worldで大文字になることはないかもしれないことに注意してください。これは私がいつも覚えていて検索できる唯一の例です。2017-06-29現在、実際、ドイツ語のスペリングの公式ルールは次のように更新されています。 とも "ẞ"と "SS" は有効な大文字表記です。 !

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

ここでは、文字をUTF-8に変換し、元の変数は実行時にメモリを占有しないように定数メモリに格納されていたため、それらを格納するための新しいアロケーションが必要です。

let s3 = &s2;

そして、今度はその参照先である String .

簡単な問題です

残念ながら、これは真実ではありません。おそらく私たちは、世界を エスペラント ?

推測するに char::to_uppercase はすでに適切にUnicodeを処理しています。

ええ、確かにそう願っています。残念ながら、Unicode はすべてのケースで十分ではありません。 おかげさまで huon が指摘してくれた トルコ語の で、上側 ( İ ) と小文字 ( i ) のバージョンにはドットがあります。つまり 1 の正しい大文字 i に依存します。 ロケール にも依存します。

<ブロッククオート

なぜすべてのデータ型変換が必要なのですか?

正しさや性能を重視する場合、扱うデータ型は重要だからです。A char は32ビットで、文字列はUTF-8でエンコードされています。両者は別物です。

インデックスを作成すると、マルチバイトのユニコード文字を返す可能性があります。

ここで、いくつかの不一致の用語があるかもしれません。A char マルチバイトユニコード文字です。

スライシング は、バイト単位なら可能ですが、文字境界でない場合、標準ライブラリはパニックになります。

文字を取得するための文字列のインデックスが実装されなかった理由の1つは、多くの人が文字列を ASCII 文字の配列として間違って使用するためです。文字列のインデックスを を設定します。 1 ~ 4 バイトを同じく 1 ~ 4 バイトの値で置き換えることができなければならないため、文字列の残りの部分が非常に多く跳ね回ることになるからです。

to_uppercase は大文字を返すことができます

前述したように ß は一文字で、大文字にすると 2文字 .

ソリューション

以下もご参照ください。 trentcl の回答 のように、ASCII文字だけを大文字にする方法もあります。

オリジナル

コードを書くとしたら、こんな感じ。

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("ß"));
}

しかし、私はおそらく検索して 大文字 または ユニコード をcrates.ioに追加して、私より賢い人に任せてください。

改善された

私より頭のいい人」といえば。 Veedrac が指摘する は、最初の資本のコードポイントにアクセスした後にイテレータをスライスに戻す方がおそらくより効率的であると指摘しています。これによって memcpy を使用することができます。

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(),
    }
}