Skip to content

String::from_utf16le

String methods · Strings

Level: reference · for working programmers

One line: Decodes little-endian UTF-16 from a &[u8] — bytes, not u16s, so the endianness is stated rather than assumed.

pub fn from_utf16le(v: &[u8]) -> Result<String, FromUtf16Error>

Stable since 1.98.0.

When UTF-16 arrives over a wire or out of a file it is a byte stream, and two bytes make a code unit only once you know which order they are in. This is the explicit little-endian reading; from_utf16be is the other.

It fails on an odd number of bytes as well as on an unpaired surrogate — a trailing half-unit cannot be decoded.

It does not consume a byte-order mark. A file starting FF FE decodes to a leading U+FEFF zero-width no-break space that will not print but will break comparisons. Strip it explicitly.

Stable since Rust 1.98, so check your MSRV.

Example

string_from_utf16le.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

fn main() {
    // "hi" little-endian: 0x68 0x00, 0x69 0x00
    let le = [0x68, 0x00, 0x69, 0x00];
    println!("{:?}", String::from_utf16le(&le));

    // The same bytes read big-endian are different characters entirely.
    println!("{:?}", String::from_utf16be(&le));

    // An odd byte count cannot be decoded.
    println!("{:?}", String::from_utf16le(&[0x68, 0x00, 0x69]).is_err());

    // A BOM is decoded, not consumed.
    let with_bom = [0xFF, 0xFE, 0x68, 0x00];
    let decoded = String::from_utf16le(&with_bom).unwrap();
    println!("{:?} first char {:?}", decoded, decoded.chars().next());
    println!("stripped: {:?}", decoded.strip_prefix('\u{FEFF}'));

    // Round trip through bytes.
    let text = "héllo";
    let bytes: Vec<u8> = text.encode_utf16().flat_map(u16::to_le_bytes).collect();
    println!("{}", String::from_utf16le(&bytes).unwrap() == text);
}

Verified output of string_from_utf16le.rs — regenerated by tools/run_examples.py, never hand-typed.

Ok("hi")
Ok("栀椀")
true
"\u{feff}h" first char Some('\u{feff}')
stripped: Some("h")
true

See also

String::from_utf16le in the standard library ↗

Po polsku

To ta kolejność, którą w praktyce spotyka się najczęściej — Windows zapisuje UTF-16 jako little-endian, więc plik z Notatnika czy eksport „Unicode text” z Excela wchodzi do Rusta właśnie tędy. Prawdziwa pułapka nie tkwi jednak w kolejności bajtów, tylko w BOM-ie: from_utf16le go dekoduje, ale go nie zjada, więc początkowe FF FE zostaje w łańcuchu znaków jako U+FEFF — znak zerowej szerokości, którego nie widać na ekranie, a który psuje każde porównanie, starts_with i klucz w mapie. Widać go dokładnie w jednym miejscu: {:?} wypisuje "\u{feff}h", podczas gdy zwykłe {} pokaże po prostu h, więc do polowania na ten znak używaj formatowania debugowego, a usuwaj go jawnie przez strip_prefix('\u{FEFF}'). Faktycznie zgłaszane błędy są tylko dwa: nieparzysta liczba bajtów i niesparowany surogat.

Szukaj po polsku: znacznik BOM · niewidzialny znak na początku pliku · UTF-16LE w Windows · rust String::from_utf16le · rust strip BOM U+FEFF