从字符串中删除单个尾随换行而不克隆

时间:2016-06-17 18:25:00

标签: string rust

我写了一个函数来提示输入并返回结果。在此版本中,返回的字符串包含用户的尾随换行符。我想要删除该换行符(并且只是换行符):

fn read_with_prompt(prompt: &str) -> io::Result<String> {
    let stdout = io::stdout();
    let mut reader = io::stdin();
    let mut input = String::new();
    print!("{}", prompt);
    stdout.lock().flush().unwrap();
    try!(reader.read_line(&mut input));

    // TODO: Remove trailing newline if present
    Ok(input)
}

仅删除单个尾随换行符的原因是此函数还将用于提示输入密码(适当使用termios以停止回显),如果某人的密码有尾随空格,则应保留此密码

在关于如何在字符串末尾实际删除单个换行符之后,我最终使用了trim_right_matches。但是返回&str。我尝试使用Cow来解决此问题,但错误仍然表明input变量的存活时间不够长。

fn read_with_prompt<'a>(prompt: &str) -> io::Result<Cow<'a, str>> {
    let stdout = io::stdout();
    let mut reader = io::stdin();
    let mut input = String::new();
    print!("{}", prompt);
    stdout.lock().flush().unwrap();
    try!(reader.read_line(&mut input));

    let mut trimmed = false;
    Ok(Cow::Borrowed(input.trim_right_matches(|c| {
        if !trimmed && c == '\n' {
            trimmed = true;
            true
        }
        else {
            false
        }
    })))
}

错误:

src/main.rs:613:22: 613:27 error: `input` does not live long enough
src/main.rs:613     Ok(Cow::Borrowed(input.trim_right_matches(|c| {
                                     ^~~~~
src/main.rs:604:79: 622:2 note: reference must be valid for the lifetime 'a as
 defined on the block at 604:78...
src/main.rs:604 fn read_with_prompt<'a, S: AsRef<str>>(prompt: S) -> io::Resul
t<Cow<'a, str>> {
src/main.rs:605     let stdout = io::stdout();
src/main.rs:606     let mut reader = io::stdin();
src/main.rs:607     let mut input = String::new();
src/main.rs:608     print!("{}", prompt.as_ref());
src/main.rs:609     stdout.lock().flush().unwrap();
                ...
src/main.rs:607:35: 622:2 note: ...but borrowed value is only valid for the bl
ock suffix following statement 2 at 607:34
src/main.rs:607     let mut input = String::new();
src/main.rs:608     print!("{}", prompt.as_ref());
src/main.rs:609     stdout.lock().flush().unwrap();
src/main.rs:610     try!(reader.read_line(&mut input));
src/main.rs:611
src/main.rs:612     let mut trimmed = false;
                ...

基于previous questions along these lines,似乎这是不可能的。是唯一一个分配删除了尾部换行符的新字符串的选项吗?似乎应该有一种方法来修剪字符串而不复制它(在C中你只需用'\n'替换'\0')。

5 个答案:

答案 0 :(得分:11)

您可以使用String::popString::truncate

fn main() {
    let mut s = "hello\n".to_string();
    s.pop();
    assert_eq!("hello", &s);

    let mut s = "hello\n".to_string();
    let len = s.len();
    s.truncate(len - 1);
    assert_eq!("hello", &s);
}

答案 1 :(得分:8)

比接受的解决方案更通用的解决方案,适用于任何类型的行结尾:

fn main() {
    let mut s = "hello\r\n".to_string();
    let len_withoutcrlf = s.trim_right().len();
    s.truncate(len_withoutcrlf);
    assert_eq!("hello", &s);
}

答案 2 :(得分:4)

strip_suffix

这将删除一个尾随的 \r\n\n

fn strip_trailing_newline(input: &str) -> &str {
    input
        .strip_suffix("\r\n")
        .or(input.strip_suffix("\n"))
        .unwrap_or(&input)
}

一些测试:

#[test]
fn strip_newline_works(){
    assert_eq!(strip_trailing_newline("Test0\r\n\r\n"), "Test0\r\n");
    assert_eq!(strip_trailing_newline("Test1\r\n"), "Test1");
    assert_eq!(strip_trailing_newline("Test2\n"), "Test2");
    assert_eq!(strip_trailing_newline("Test3"), "Test3");
}

答案 3 :(得分:1)

一种在不重新分配字符串的情况下剥离单个尾随换行符的跨平台方法是:

fn trim_newline(s: &mut String) {
    if s.ends_with('\n') {
        s.pop();
        if s.ends_with('\r') {
            s.pop();
        }
    }
}

这将从字符串的末尾剥离"\n""\r\n",但不附加空格。

答案 4 :(得分:0)

编辑:我刚刚意识到OP希望制作字符串的副本......所以只需注意这实际上复制了字符串。 :(

我是 Rust 新手,所以我不知道这个功能是什么时候引入的,但可以考虑使用 String::lines 方法。看起来它应该以可靠的方式跨平台工作,并且在我的本地开发中,它的行为似乎符合 OP 的要求。

use std::io::stdin;

fn main() {
    println!("Enter a line of text:");
    let mut buf = String::new();
    stdin().read_line(&mut buf).expect("Failed to read input.");
    let my_str = buf.lines()
        .next().expect("Could not read entry.");
    println!("You entered: [{}]", my_str);
}

参考:https://doc.rust-lang.org/stable/std/string/struct.String.html#method.lines