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

vs91vp4v  于 2023-01-26  发布在  其他
关注(0)|答案(6)|浏览(239)

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

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)
}

仅删除单个尾随换行符的原因是,此函数还将用于提示输入密码(适当使用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
        }
    })))
}

错误:

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

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

f87krz0w

f87krz0w1#

您可以使用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);
}
uujelgoq

uujelgoq2#

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

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

这将从字符串末尾去掉"\n""\r\n",但不去掉其他空格。

eivnm1vs

eivnm1vs3#

带有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");
}
2jcobegt

2jcobegt4#

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

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);
}
2lpgd968

2lpgd9685#

编辑:我刚刚意识到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

o4hqfura

o4hqfura6#

如果您已经拥有了一个String,那么您不需要仅仅为了去除尾随的新行而进行新的分配。
下面是一个跨平台示例,它删除了多个尾随的\r\n\n

fn strip_trailing_nl(input: &mut String) {
    let new_len = input
        .char_indices()
        .rev()
        .find(|(_, c)| !matches!(c, '\n' | '\r'))
        .map_or(0, |(i, _)| i + 1);
    if new_len != input.len() {
        input.truncate(new_len);
    }
}

现在,让我们测试一下(操场链接:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ec2f6f60bdde32ccfeb8fa0c63a06f54):

#[test]
fn this_works() {
    let mut s = "\n".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "");

    let mut s = "\r\n".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "");

    let mut s = "Hello, World".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "Hello, World");

    let mut s = "Hello, World\n".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "Hello, World");

    let mut s = "Hello, World\r\n".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "Hello, World");

    let mut s = "Hello, World\n\n\r\n\r\n".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "Hello, World");

    let mut s = "".to_string();
    strip_trailing_nl(&mut s);
    assert_eq!(s, "");
}

相关问题