web-dev-qa-db-ja.com

Rustで文字列の最初の文字を大文字にするのが複雑なのはなぜですか?

&strの最初の文字を大文字にします。これは単純な問題であり、単純な解決策を望んでいます。直観はこのようなことをするように私に言います:

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

ただし、&strsはこのようにインデックス付けできません。私がそれを行うことができた唯一の方法は、過度に複雑に思えます。 &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はこのように設計されていますか?

同様の質問

51
marshallm

なぜ複雑なのですか?

行ごとに分解しましょう

let s1 = "foobar";

TF-8 でエンコードされたリテラル文字列を作成しました。 UTF-8を使用すると、1,114,112をエンコードできます コードポイント of nicode[〜#〜] ascii [〜#〜] 、1963年に作成された標準。UTF-8は可変長エンコーディングであり、単一のコードポイントは、 1〜4バイト です。短いエンコードはASCII用に予約されていますが、 多くの漢字はUTF-8で3バイトかかります です。

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

これにより、charactersのベクトルが作成されます。文字とは、コードポイントに直接マップされる32ビットの数値です。 ASCIIのみのテキストから始めた場合、メモリ要件は4倍になりました。 アストラルプレーン の文字がたくさんある場合は、それ以上使用していない可能性があります。

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

これは、最初のコードポイントを取得し、大文字のバリアントに変換することを要求します。残念ながら、英語を話すように育った私たちにとって、 「小さな文字」から「大きな文字」への単純な1対1のマッピングとは限りません があります。サイドノート:私たちはそれらを大文字と小文字と呼んでいます 1つの文字の箱がその日に戻って他の文字の箱の上にあったため .

コードポイントに対応する大文字のバリアントがない場合、このコードはパニックになります。実際にそれらが存在するかどうかはわかりません。コードポイントに、ドイツ語のßなど、複数の文字を含む大文字のバリアントがある場合、意味的に失敗することもあります。実世界では、ßが実際に大文字にならない場合があることに注意してください。これは、常に覚えて検索できる例です。実際、2017-06-29現在、ドイツ語のスペルの公式ルールが更新されているため、 both "ẞ"および " SS "は有効な大文字です

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

元の変数は実行時にメモリを占有しないように定数メモリに保存されていたため、ここで文字をUTF-8に変換し直して、新しい文字を保存する必要があります。

let s3 = &s2;

そして今、そのStringへの参照を取得します。

簡単な問題です

残念ながら、これは真実ではありません。おそらく、世界を エスペラント ?に変換するよう努力する必要があります。

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

はい、私は確かにそう願っています。残念ながら、Unicodeはすべての場合に十分ではありません。 指摘する音トルコ語I のおかげで、上部(İ)と小文字(i)バージョンにはドットがあります。つまり、文字iの適切な大文字化oneはありません。ソーステキストの locale にも依存します。

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

正確性とパフォーマンスが心配な場合は、使用しているデータ型が重要だからです。 charは32ビットで、文字列はUTF-8でエンコードされています。それらは異なるものです。

インデックス付けはマルチバイトのUnicode文字を返す可能性があります

ここにいくつかの不一致の用語があるかもしれません。 charマルチバイトUnicode文字です。

バイト単位で行けば文字列のスライスが可能ですが、文字境界にいない場合は標準ライブラリがパニックします。

文字列にインデックスを付けて文字を取得することが実装されなかった理由の1つは、文字列をASCII文字の配列として誤用しているためです。文字列のset文字は決して効率的ではありません。1〜4バイトを1〜4バイトの値に置き換える必要があり、文字列の残りの部分がかなり跳ね返ります。

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

上記のように、ßは1文字であり、大文字にすると、2文字になります

解決策

trentcl's answer も参照してください。これは大文字のみ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("ß"));
}

しかし、私はおそらく 大文字 または nicode を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(),
    }
}
74
Shepmaster

これより簡単な方法はありますか?そうでない場合、なぜRustはこのように設計されていますか?

はい、はい、いいえ。あなたのコードは、他の答えが指摘したように、正しくなく、བོད་སྐད་ལ་のようなものを与えるとパニックになります。したがって、Rustの標準ライブラリを使用してこれを行うことは、当初考えていたよりもさらに困難です。

ただし、Rustは、コードの再利用を促進し、ライブラリを簡単に取り込むことができるように設計されています。

extern crate inflector;
use inflector::Inflector;

let capitalized = "some string".to_title_case();
17
user8174234

入力をASCIIのみの文字列に制限できる場合は特に複雑ではありません。

Rust 1.23、strにはmake_ascii_uppercaseメソッド(以前のRustバージョンでは、AsciiExtトレイトを介して使用できました)。これは、比較的簡単にASCIIのみの文字列スライスを大文字にできることを意味します。

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

これにより、"taylor" into "Taylor"が、それは"édouard" into "Édouard"。 ( 遊び場

注意して使用してください。

6
trentcl