繁体   English   中英

如何将str改组到位

[英]How to shuffle a str in place

我想在Rust中使用一个字符串,但我似乎错过了一些东西。 修复可能是微不足道的......

use std::rand::{Rng, thread_rng};

fn main() {
    // I want to shuffle this string...
    let mut value: String = "SomeValue".to_string();
    let mut bytes = value.as_bytes();
    let mut slice: &mut [u8] = bytes.as_mut_slice();

    thread_rng().shuffle(slice);

    println!("{}", value); 
}

我得到的错误是

<anon>:8:36: 8:41 error: cannot borrow immutable dereference of `&`-pointer `*bytes` as mutable
<anon>:8         let mut slice: &mut [u8] = bytes.as_mut_slice();
                                            ^~~~~

我读到了关于String :: as_mut_vec()但它不安全,所以我宁愿不使用它。

没有很好的方法可以做到这一点,部分原因是UTF-8字符串编码的性质,部分原因是由于Unicode和文本的固有属性。

至少有三层可以在UTF-8字符串中混洗的东西:

  • 原始字节
  • 编码的码点
  • 字形

除非字符串完全是ASCII,否则对原始字节进行混洗可能会将无效的UTF-8字符串作为输出。 非ASCII字符被编码为多个字节的特殊序列,并且随机抽取它们几乎肯定不会在最后以正确的顺序获得它们。 因此,改组字节通常不好。

改组代码点(Rust中的char )更有意义,但仍然存在“特殊序列”的概念,其中所谓的组合字符可以分层到单个字母上添加变音符号等(例如像ä这样的字母可以是写为a加U + 0308,表示所述码点的分音符 )。 因此,随机字符不会产生无效的UTF-8字符串,但它可能会破坏这些代码点序列并提供无意义的输出。

这让我想到了字形:构成单个可见字符的代码点序列(如ä在写为一个或两个代码点时仍然是单个字形)。 这将给出最可靠明智的答案。

然后,一旦你决定要改组哪个洗牌策略就可以:

  • 如果保证字符串纯粹是ASCII,那么使用.shuffle对字节进行混洗是明智的(使用ASCII假设,这相当于其他字符串)
  • 否则,没有标准的就地操作方法,可以将元素作为迭代器(代码点的.chars()或字形的.graphemes(true) ),将它们放入带有.collect::<Vec<_>>()的向量中.collect::<Vec<_>>() ,将向量移动,然后将所有内容收集回一个新的String ,例如.iter().map(|x| *x).collect::<String>()

处理代码点和字形的难度是因为UTF-8不将它们编码为固定宽度,因此无法将随机码点/字形输出并将其插入其他地方,或以其他方式有效地交换两个元素...而不仅仅是解码一切都变成了外部Vec

不合适是不幸的,但字符串很难。

(如果你的字符串保证是ASCII码,然后用一种类似Ascii提供ascii将是一个很好的方式让事情简单,在类型级)。


作为三件事差异的一个例子,看看:

fn main() {
    let s = "U͍̤͕̜̲̼̜n̹͉̭͜ͅi̷̪c̠͍̖̻o̸̯̖de̮̻͍̤";
    println!("bytes: {}", s.bytes().count());
    println!("chars: {}", s.chars().count());
    println!("graphemes: {}", s.graphemes(true).count());
}

它打印:

bytes: 57
chars: 32
graphemes: 7

生成您自己的 ,它演示了将多个组合字符放在一个字母上。)

汇总上面的建议:

use std::rand::{Rng, thread_rng};

fn str_shuffled(s: &str) -> String {
    let mut graphemes = s.graphemes(true).collect::<Vec<&str>>();
    let mut gslice = graphemes.as_mut_slice();
    let mut rng = thread_rng();
    rng.shuffle(gslice);
    gslice.iter().map(|x| *x).collect::<String>()
}


fn main() {
    println!("{}", str_shuffled("Hello, World!"));
    println!("{}", str_shuffled("selam dünya"));
    println!("{}", str_shuffled("你好世界"));
    println!("{}", str_shuffled("γειά σου κόσμος"));
    println!("{}", str_shuffled("Здравствулте мир"));

}

我也是Rust的初学者,但是怎么样:

fn main() {
    // I want to shuffle this string...
    let value = "SomeValue".to_string();
    let mut bytes = value.into_bytes();

    bytes[0] = bytes[1]; // Shuffle takes place.. sorry but std::rand::thread_rng is not available in the Rust installed on my current machine.

    match String::from_utf8(bytes) { // Should not copy the contents according to documentation.
        Ok(s) => println!("{}", s),
        _ => println!("Error occurred!")
    }
}

还要记住,当摆弄字节序列时,Rust默认字符串编码是UTF-8。 ;)


这是一个很好的建议,引导我到以下解决方案,谢谢!

use std::rand::{Rng, thread_rng};

fn main() {
    // I want to shuffle this string...
    let value: String = "SomeValue".to_string();
    let mut bytes = value.into_bytes();

    thread_rng().shuffle(&mut *bytes.as_mut_slice());

    match String::from_utf8(bytes) { // Should not copy the contents according to documentation.
        Ok(s) => println!("{}", s),
        _ => println!("Error occurred!")
    }
}

rustc 0.13.0-nightly(ad9e75938 2015-01-05 00:26:28 +0000)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM