如何trim space 小于n次?

How to trim space less than n times?

如何消除每行开头的n个space?

例如,当trim 4 space:

const INPUT:&str = "    4\n  2\n0\n\n      6\n";
const OUTPUT:&str = "4\n2\n0\n\n  6\n";
#[test]
fn main(){
    assert_eq!(&trim_deindent(INPUT,4), OUTPUT)
}

我正要评论textwrap::dedent,但后来我注意到"2",它少于4个空格。所以你希望它继续删除空格,如果有直到 4.

只是写一个快速解决方案,它可能看起来像这样:

您的断言将通过,但请注意以 \r\n 结尾的行将被转换为 \n,因为 lines 不提供区分 \n 的方法和 \r\n.

fn trim_deindent(text: &str, max: usize) -> String {
    let mut new_text = text
        .lines()
        .map(|line| {
            let mut max = max;
            line.chars()
                // Skip while `c` is a whitespace and at most `max` spaces
                .skip_while(|c| {
                    if max == 0 {
                        false
                    } else {
                        max -= 1;
                        c.is_whitespace()
                    }
                })
                .collect::<String>()
        })
        .collect::<Vec<_>>()
        .join("\n");

    // Did the original `text` end with a `\n` then add it again
    if text.ends_with('\n') {
        new_text.push('\n');
    }

    new_text
}

如果你想同时保留 \n\r\n 那么你可以通过更复杂的方式扫描字符串,从而避免使用 lines.

fn trim_deindent(text: &str, max: usize) -> String {
    let mut new_text = String::new();

    let mut line_start = 0;
    loop {
        let mut max = max;

        // Skip `max` spaces
        let after_space = text[line_start..].chars().position(|c| {
            // We can't use `is_whitespace` here, as that will skip past `\n` and `\r` as well
            if (max == 0) || !is_horizontal_whitespace(c) {
                true
            } else {
                max -= 1;
                false
            }
        });

        if let Some(after_space) = after_space {
            let after_space = line_start + after_space;

            let line = &text[after_space..];
            // Find `\n` or use the line length (if it's the last line)
            let end = line
                .chars()
                .position(|c| c == '\n')
                .unwrap_or_else(|| line.len());

            // Push the line (including the line ending) onto `new_text`
            new_text.push_str(&line[..=end]);

            line_start = after_space + end + 1;
        } else {
            break;
        }
    }

    new_text
}

#[inline]
fn is_horizontal_whitespace(c: char) -> bool {
    (c != '\r') && (c != '\n') && c.is_whitespace()
}