una separar saber reemplazar por palabra mayuscula letras letra funcion eliminar caracteres caracter cadenas cadena string rust uppercase

string - separar - ¿Por qué es mayúscula la primera letra de una cadena tan complicada en Rust?



separar palabra en letras python (3)

Me gustaría poner en mayúscula la primera letra de a &str . Es un problema simple y espero una solución simple. La intuición me dice que haga algo como esto:

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

Pero &str s no se puede indexar así. La única forma en que he podido hacerlo parece demasiado complicada. Convierto el &str en un iterador, convierto el iterador en un vector, en mayúscula el primer elemento del vector, que crea un iterador, que indexo, creando una Option , que desenvuelvo para darme la primera letra mayúscula . Luego convierto el vector en un iterador, que convierto en una String , que convierto en a &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;

¿Hay una manera más fácil que esta, y si es así, qué? Si no, ¿por qué Rust está diseñado de esta manera?

Pregunta similar


¿Hay una manera más fácil que esta, y si es así, qué? Si no, ¿por qué Rust está diseñado de esta manera?

Pues sí y no. Su código es, como señaló la otra respuesta, no correcto, y entrará en pánico si le da algo como བོད་ སྐད་ ལ་. Así que hacer esto con la biblioteca estándar de Rust es aún más difícil de lo que inicialmente pensaste.

Sin embargo, Rust está diseñado para fomentar la reutilización del código y facilitar la incorporación de bibliotecas. Entonces, la forma idiomática de capitalizar una cadena es realmente bastante sabrosa:

extern crate inflector; use inflector::Inflector; let capitalized = "some string".to_title_case();


¿Por qué es tan complicado?

Desglosemos, línea por línea

let s1 = "foobar";

Hemos creado una cadena literal que está codificada en UTF-8 . UTF-8 nos permite codificar los 1,114,112 puntos de código de Unicode de una manera bastante compacta si vienes de una región del mundo que escribe principalmente caracteres encontrados en ASCII , un estándar creado en 1963. UTF-8 es una longitud variable codificación, lo que significa que un solo punto de código puede tomar de 1 a 4 bytes . Las codificaciones más cortas están reservadas para ASCII, pero muchos Kanji toman 3 bytes en UTF-8 .

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

Esto crea un vector de personajes. Un carácter es un número de 32 bits que se asigna directamente a un punto de código. Si comenzamos con texto solo ASCII, hemos cuadruplicado nuestros requisitos de memoria. Si tuviéramos un montón de personajes del plano astral , entonces tal vez no hayamos usado mucho más.

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

Esto toma el primer punto de código y solicita que se convierta a una variante en mayúscula. Desafortunadamente para aquellos de nosotros que crecimos hablando inglés, no siempre hay un mapeo simple de una "letra pequeña" a una "letra grande" . Nota al margen: los llamamos mayúsculas y minúsculas porque una caja de letras estaba por encima de la otra caja de letras en el día .

Este código entrará en pánico cuando un punto de código no tenga una variante en mayúscula correspondiente. No estoy seguro de si existen, en realidad. También podría fallar semánticamente cuando un punto de código tiene una variante en mayúscula que tiene varios caracteres, como el ß alemán. Tenga en cuenta que es posible que ß nunca se capitalice en The Real World, este es el ejemplo que siempre puedo recordar y buscar. A partir del 29/06/2017, de hecho, las reglas oficiales de ortografía alemana se han actualizado para que tanto "ẞ" como "SS" sean mayúsculas válidas .

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

Aquí convertimos los caracteres nuevamente en UTF-8 y requerimos una nueva asignación para almacenarlos, ya que la variable original se almacenó en memoria constante para no ocupar memoria en tiempo de ejecución.

let s3 = &s2;

Y ahora tomamos una referencia a esa String .

Es un problema simple

Desafortunadamente, esto no es verdad. ¿Quizás deberíamos tratar de convertir el mundo al Esperanto ?

Supongo que char::to_uppercase ya maneja correctamente Unicode.

Sí, ciertamente lo espero. Desafortunadamente, Unicode no es suficiente en todos los casos. Gracias a Huon por señalar el I turco , donde las versiones en mayúscula ( İ ) y minúscula ( i ) tienen un punto. Es decir, no hay una mayúscula adecuada de la letra i ; también depende de la locale del texto fuente.

¿Por qué la necesidad de todas las conversiones de tipos de datos?

Porque los tipos de datos con los que está trabajando son importantes cuando le preocupa la corrección y el rendimiento. Un char tiene 32 bits y una cadena está codificada en UTF-8. Son cosas diferentes.

la indexación podría devolver un carácter Unicode de varios bytes

Puede haber alguna terminología no coincidente aquí. Un char es un carácter Unicode de varios bytes.

Es posible cortar una cadena si va byte a byte, pero la biblioteca estándar entrará en pánico si no está en un límite de caracteres.

Una de las razones por las que la indexación de una cadena para obtener un carácter nunca se implementó es porque mucha gente usa mal las cadenas como matrices de caracteres ASCII. La indexación de una cadena para establecer un carácter nunca podría ser eficiente: tendría que poder reemplazar 1-4 bytes con un valor que también sea 1-4 bytes, lo que hace que el resto de la cadena rebote bastante.

to_uppercase podría devolver un carácter en mayúscula

Como se mencionó anteriormente, ß es un solo carácter que, cuando se escribe con mayúscula, se convierte en dos caracteres .

Soluciones

Vea también la respuesta de trentcl que solo escribe en mayúscula los caracteres ASCII.

Original

Si tuviera que escribir el código, se vería así:

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

Pero probablemente buscaría uppercase o unicode en crates.io y dejaría que alguien más inteligente que yo lo maneje.

Mejorado

Hablando de "alguien más inteligente que yo", Veedrac señala que probablemente sea más eficiente convertir el iterador nuevamente en un segmento después de acceder a los primeros puntos de código de capital. Esto permite una memcpy del resto de los 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(), } }


No es especialmente complicado si puede limitar su entrada a cadenas solo ASCII.

Desde Rust 1.23, str tiene un método make_ascii_uppercase (en versiones anteriores de Rust, estaba disponible a través del rasgo AsciiExt ). Esto significa que puede poner mayúsculas a los segmentos de cadena solo ASCII con relativa facilidad:

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

Esto convertirá "taylor" en "Taylor" , pero no convertirá "édouard" en "Édouard" . ( playground )

Usar con precaución.