从String中删除单个尾随换行而不进行克隆

Wes*_*Wes 12 string rust

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

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

    // TODO: Remove trailing newline if present
    Ok(input)
}
Run Code Online (Sandbox Code Playgroud)

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

在关于如何在字符串末尾实际删除单个换行符之后,我最终使用了trim_right_matches.然而,返回一个&str.我试图用它Cow来处理这个但是错误仍然说input变量不够长.

fn read_with_prompt<'a>(prompt: &str) -> io::Result<Cow<'a, str>> {
    let stdout = io::stdout();
    let reader = io::stdin();
    let mut input = String::new();
    print!("{}", prompt);
    stdout.lock().flush().unwrap();
    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
        }
    })))
}
Run Code Online (Sandbox Code Playgroud)

错误:

error[E0515]: cannot return value referencing local variable `input`
  --> src/lib.rs:13:5
   |
13 |       Ok(Cow::Borrowed(input.trim_right_matches(|c| {
   |       ^                ----- `input` is borrowed here
   |  _____|
   | |
14 | |         if !trimmed && c == '\n' {
15 | |             trimmed = true;
16 | |             true
...  |
20 | |         }
21 | |     })))
   | |________^ returns a value referencing data owned by the current function
Run Code Online (Sandbox Code Playgroud)

基于以前的这些问题,似乎这是不可能的.是唯一一个分配删除了尾部换行符的新字符串的选项吗?这似乎应该有修剪不复制它的字符串的方式(在C你只需更换'\n''\0').

mal*_*rbo 17

你可以使用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);
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这不处理 \r\n 换行符,并且还假设最后一个字符是换行符(这在问题中可能是正确的,但在一般情况下并非如此)。 (4认同)

San*_*der 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);
}
Run Code Online (Sandbox Code Playgroud)

  • “如果某人的密码末尾有空格,则应保留该空格。” 但是trim_right()也会修剪空格。 (5认同)

Sve*_*ach 7

删除单个尾随换行符而不重新分配字符串的跨平台方法是:

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

这将删除字符串中的任意一个"\n""\r\n"从字符串的末尾删除,但不附加空格。

  • 有点烦人的是我们必须这样做而不是将其融入到语言中..但我想它确实允许操作系统优化?我对所有这些东西都很陌生。 (3认同)

Mat*_*aun 5

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)
}
Run Code Online (Sandbox Code Playgroud)

一些测试:

#[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");
}
Run Code Online (Sandbox Code Playgroud)

  • 测试中表现出色! (8认同)