首页 > 解决方案 > 如何修剪空间少于n次?

问题描述

如何在每行的开头消除多达 n 个空格?

例如,当修剪 4 个空格时:

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

标签: rusttexttrimming

解决方案


我正要评论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()
}

推荐阅读