从 Vec<char> 创建一个字符串
Creating a string from Vec<char>
我有一个 Vec<char>
,我需要将其转换为 &str
或 String
,但我不确定执行此操作的最佳方法。我环顾四周,我发现的所有资源似乎在某种程度上已经过时了。 this question 中的答案似乎不适用于最新版本。
我在 2015 年 3 月 19 日使用 nightly
.collect
的基于迭代器的方法应该可以工作,更新语言更改后:
char_vector.iter().cloned().collect::<String>();
(我选择将 .map(|c| *c)
替换为 .cloned()
,但两者都有效。)
如果你的vector可以消耗,你也可以用into_iter
来避免clone
fn main() {
let char_vector = vec!['h', 'e', 'l', 'l', 'o'];
let str: String = char_vector.into_iter().collect();
println!("{}", str);
}
您可以将 Vec
转换为 String
而无需进行任何分配。不过它需要相当多的不安全代码:
#![feature(raw, unicode)]
use std::raw::Repr;
use std::slice::from_raw_parts_mut;
fn inplace_to_string(v: Vec<char>) -> String {
unsafe {
let mut i = 0;
{
let ch_v = &v[..];
let r = ch_v.repr();
let p: &mut [u8] = from_raw_parts_mut(r.data as *mut u8, r.len*4);
for ch in ch_v {
i += ch.encode_utf8(&mut p[i..i+4]).unwrap();
}
}
let p = v.as_ptr();
let cap = v.capacity()*4;
std::mem::forget(v);
let v = Vec::from_raw_parts(p as *mut u8, i, cap);
String::from_utf8_unchecked(v)
}
}
fn main() {
let char_vector = vec!['h', 'ä', 'l', 'l', 'ö'];
let str: String = char_vector.iter().cloned().collect();
let str2 = inplace_to_string(char_vector);
println!("{}", str);
println!("{}", str2);
}
详细说明
这会同时创建一个可变 u8
切片和一个 char
切片到同一缓冲区(打破所有 Rust 保证)。请注意 u8
切片是 char
切片的四倍大,因为 char
总是占用 4 个字节。
let ch_v = &v[..];
let r = ch_v.repr();
let v: &mut [u8] = from_raw_parts_mut(r.data as *mut u8, r.len*4);
我们需要它来遍历 unicode 字符并将它们替换为对应的 utf8 编码字符。由于 utf8 总是比 unicode 更短或相同,我们可以保证我们永远不会覆盖任何我们还没有读过的部分。
for ch in ch_v {
i += ch.encode_utf8(&mut v[i..i+4]).unwrap();
}
因为 char
总是 unicode 并且我们的缓冲区总是恰好是 4 个字节(这是 utf8 编码的 unicode 字符需要的最大字节数),我们可以将我们的字符编码为 utf8 而无需检查是否它有效(它将始终有效)。 encode_utf8
函数 returns utf8 表示的长度。我们的索引 i
是最后写入的 utf8 字符的位置。
最后我们需要做一些清理工作。我们的向量仍然是 Vec<char>
类型。我们得到了我们需要的所有信息(指向堆分配数组和容量的指针)
let p = v.as_ptr();
let cap = v.capacity()*4;
然后我们释放前一个向量的所有义务,如释放内存。
std::mem::forget(v);
最后重新创建长度和容量正确的u8向量,直接转成String。不需要检查到 String 的转换,因为我们已经知道 utf8 是正确的,因为原始 Vec<char>
只能包含正确的 unicode 字符。
let v = Vec::from_raw_parts(p as *mut u8, i, cap);
String::from_utf8_unchecked(v)
我有一个 Vec<char>
,我需要将其转换为 &str
或 String
,但我不确定执行此操作的最佳方法。我环顾四周,我发现的所有资源似乎在某种程度上已经过时了。 this question 中的答案似乎不适用于最新版本。
我在 2015 年 3 月 19 日使用 nightly
.collect
的基于迭代器的方法应该可以工作,更新语言更改后:
char_vector.iter().cloned().collect::<String>();
(我选择将 .map(|c| *c)
替换为 .cloned()
,但两者都有效。)
如果你的vector可以消耗,你也可以用into_iter
来避免clone
fn main() {
let char_vector = vec!['h', 'e', 'l', 'l', 'o'];
let str: String = char_vector.into_iter().collect();
println!("{}", str);
}
您可以将 Vec
转换为 String
而无需进行任何分配。不过它需要相当多的不安全代码:
#![feature(raw, unicode)]
use std::raw::Repr;
use std::slice::from_raw_parts_mut;
fn inplace_to_string(v: Vec<char>) -> String {
unsafe {
let mut i = 0;
{
let ch_v = &v[..];
let r = ch_v.repr();
let p: &mut [u8] = from_raw_parts_mut(r.data as *mut u8, r.len*4);
for ch in ch_v {
i += ch.encode_utf8(&mut p[i..i+4]).unwrap();
}
}
let p = v.as_ptr();
let cap = v.capacity()*4;
std::mem::forget(v);
let v = Vec::from_raw_parts(p as *mut u8, i, cap);
String::from_utf8_unchecked(v)
}
}
fn main() {
let char_vector = vec!['h', 'ä', 'l', 'l', 'ö'];
let str: String = char_vector.iter().cloned().collect();
let str2 = inplace_to_string(char_vector);
println!("{}", str);
println!("{}", str2);
}
详细说明
这会同时创建一个可变 u8
切片和一个 char
切片到同一缓冲区(打破所有 Rust 保证)。请注意 u8
切片是 char
切片的四倍大,因为 char
总是占用 4 个字节。
let ch_v = &v[..];
let r = ch_v.repr();
let v: &mut [u8] = from_raw_parts_mut(r.data as *mut u8, r.len*4);
我们需要它来遍历 unicode 字符并将它们替换为对应的 utf8 编码字符。由于 utf8 总是比 unicode 更短或相同,我们可以保证我们永远不会覆盖任何我们还没有读过的部分。
for ch in ch_v {
i += ch.encode_utf8(&mut v[i..i+4]).unwrap();
}
因为 char
总是 unicode 并且我们的缓冲区总是恰好是 4 个字节(这是 utf8 编码的 unicode 字符需要的最大字节数),我们可以将我们的字符编码为 utf8 而无需检查是否它有效(它将始终有效)。 encode_utf8
函数 returns utf8 表示的长度。我们的索引 i
是最后写入的 utf8 字符的位置。
最后我们需要做一些清理工作。我们的向量仍然是 Vec<char>
类型。我们得到了我们需要的所有信息(指向堆分配数组和容量的指针)
let p = v.as_ptr();
let cap = v.capacity()*4;
然后我们释放前一个向量的所有义务,如释放内存。
std::mem::forget(v);
最后重新创建长度和容量正确的u8向量,直接转成String。不需要检查到 String 的转换,因为我们已经知道 utf8 是正确的,因为原始 Vec<char>
只能包含正确的 unicode 字符。
let v = Vec::from_raw_parts(p as *mut u8, i, cap);
String::from_utf8_unchecked(v)