[string] Rust에서 문자열의 첫 글자를 대문자로 바꾸는 이유는 무엇입니까?

의 첫 글자를 대문자로 쓰고 싶습니다 &str. 간단한 문제이고 간단한 해결책을 원합니다. 직감은 나에게 다음과 같은 것을하라고 말한다.

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

그러나 &strs는 이와 같이 인덱싱 할 수 없습니다. 내가 할 수 있었던 유일한 방법은 지나치게 복잡해 보입니다. 나는 변환 &str작성, 반복자,로 I 인덱스를 생성하는 벡터의 벡터, 대문자로 첫 번째 항목을 반복자를 변환, 반복자에 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 코드 포인트 를 매우 간결한 방식으로 인코딩 할 수 있습니다 . 1963 년에 생성 된 표준 인 ASCII 로 대부분의 문자를 입력하는 세계 지역에서 왔을 때입니다 . UTF-8은 가변 길이입니다. 이는 단일 코드 포인트가 1에서 4 바이트까지 걸릴 수 있음을 의미합니다 . 더 짧은 인코딩은 ASCII 용으로 예약되어 있지만 많은 Kanji는 UTF-8에서 3 바이트를 사용 합니다.

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

이것은 char행위자 의 벡터를 만듭니다 . 문자는 코드 포인트에 직접 매핑되는 32 비트 숫자입니다. ASCII 전용 텍스트로 시작했다면 메모리 요구 사항이 4 배가되었습니다. 만약 우리가 아스트랄 계 의 캐릭터들을 가지고 있다면, 우리는 그다지 많이 사용하지 않았을 것입니다.

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

이것은 첫 번째 코드 포인트를 잡고 대문자 변형으로 변환하도록 요청합니다. 유감스럽게도 영어로 자란 우리에게는 “소문자”를 “큰 문자”에 대한 간단한 일대일 매핑이 항상있는 것은 아닙니다 . 참고 : 과거에는 한 상자의 글자가 다른 글자 상자보다 위에 있었기 때문에 대문자와 소문자라고 부릅니다 .

이 코드는 코드 포인트에 해당하는 대문자 변형이 없을 때 패닉 상태가됩니다. 실제로 존재하는지 확실하지 않습니다. 또한 코드 포인트에 German과 같이 여러 문자가있는 대문자 변형이있는 경우 의미 상 실패 할 수 있습니다 ß. ß는 The Real World에서 실제로 대문자로 표기되지 않을 수 있습니다. 이것은 제가 항상 기억하고 검색 할 수있는 유일한 예입니다. 2017년 6월 29일로, 사실, 독일어 맞춤법의 공식 규칙이 이렇게 업데이트되었는지 모두 “ẞ”과 “SS”는 유효한 총액은 !

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

여기서는 문자를 다시 UTF-8로 변환하고 런타임에 메모리를 차지하지 않도록 원래 변수가 상수 메모리에 저장되었으므로이를 저장할 새 할당이 필요합니다.

let s3 = &s2;

그리고 이제 우리는 그것을 참조합니다 String.

간단한 문제

불행히도 이것은 사실이 아닙니다. 아마도 우리는 세상을 에스페란토 로 바꾸려고 노력해야 할까요?

나는 char::to_uppercase이미 유니 코드를 제대로 처리한다고 생각한다.

네, 확실히 그러길 바랍니다. 불행히도 모든 경우에 유니 코드만으로는 충분하지 않습니다. 대문자 ( İ )와 소문자 ( i ) 버전 모두에 점이 있는 터키어 I 를 지적 해 주신 huon에게 감사드립니다 . 즉 더 없다,있다 편지의 적절한 총액 ; 소스 텍스트 의 로케일 에 따라 다릅니다 .i

모든 데이터 유형 변환이 필요한 이유는 무엇입니까?

작업중인 데이터 유형은 정확성과 성능이 걱정 될 때 중요하기 때문입니다. A char는 32 비트이고 문자열은 UTF-8로 인코딩됩니다. 그들은 다른 것들입니다.

인덱싱은 멀티 바이트 유니 코드 문자를 반환 할 수 있습니다.

여기에 일치하지 않는 용어가있을 수 있습니다. A char 멀티 바이트 유니 코드 문자입니다.

바이트 단위로 이동하면 문자열을 슬라이싱 할 수 있지만 문자 경계에 있지 않으면 표준 라이브러리가 패닉 상태가됩니다.

문자를 얻기 위해 문자열 인덱싱이 구현되지 않은 이유 중 하나는 많은 사람들이 문자열을 ASCII 문자 배열로 오용하기 때문입니다. 문자 를 설정 하기 위해 문자열을 인덱싱하는 것은 결코 효율적일 수 없습니다. 1-4 바이트를 1-4 바이트 값으로 대체 할 수 있어야 나머지 문자열이 상당히 많이 바운스됩니다.

to_uppercase 대문자를 반환 할 수 있습니다.

위에서 언급 ß했듯이은 대문자로 입력하면 두 문자 가되는 단일 문자입니다 .

솔루션

ASCII 문자 만 대문자로하는 trentcl의 답변 도 참조하십시오 .

실물

코드를 작성해야한다면 다음과 같이 보일 것입니다.

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


답변

이것보다 더 쉬운 방법이 있습니까? 그렇다면 무엇입니까? 그렇지 않다면 Rust가 이런 방식으로 디자인 된 이유는 무엇입니까?

글쎄, 예 그리고 아니오. 귀하의 코드는 다른 답변에서 지적했듯이 정확하지 않으며 བོད་ སྐད་ ལ་와 같은 것을 제공하면 당황합니다. 따라서 Rust의 표준 라이브러리로 이것을하는 것은 처음에 생각했던 것보다 훨씬 더 어렵습니다.

그러나 Rust는 코드 재사용을 장려하고 라이브러리를 쉽게 가져올 수 있도록 설계되었습니다. 따라서 문자열을 대문자로 표기하는 관용적 방법은 실제로 매우 맛있습니다.

extern crate inflector;
use inflector::Inflector;

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


답변

입력을 ASCII 전용 문자열로 제한 할 수 있다면 특별히 복잡하지 않습니다.

녹 1.23 이후, strmake_ascii_uppercase방법을 (오래된 녹 버전에서, 그것은을 통해 사용할 수있었습니다 AsciiExt특성). 이것은 상대적으로 쉽게 ASCII 전용 문자열 조각을 대문자로 바꿀 수 있음을 의미합니다.

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

이 켜집니다 "taylor""Taylor"있지만 켜지지 않습니다 "édouard""Édouard". ( 놀이터 )

주의해서 사용하십시오.


답변

이것이 제가이 문제를 해결 한 방법입니다. 대문자로 변환하기 전에 self가 ASCII가 아닌지 확인해야했습니다.

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

산출

Bruno
B
?
ß

བོད་སྐད་ལ


답변

@Shepmaster의 개선 된 버전보다 약간 느리지 만 더 관용적 인 버전이 있습니다 .

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


답변

나는 이렇게했다 :

fn str_cap(s: &str) -> String {
  format!("{}{}", (&s[..1].to_string()).to_uppercase(), &s[1..])
}

ASCII 문자열이 아닌 경우 :

fn str_cap(s: &str) -> String {
  format!("{}{}", s.chars().next().unwrap().to_uppercase(),
  s.chars().skip(1).collect::<String>())
}


답변