Vec<u8>如何实现修剪?

问题描述 投票:0回答:4

Rust 为字符串提供了一种修剪方法:str.trim() 删除前导和尾随空格。我想要一个对字节字符串执行相同操作的方法。它应该采用

Vec<u8>
并删除前导和尾随空格(空格,0x20 和 htab,0x09)。

写一个

trim_left()
很容易,你可以只用一个带有
skip_while()
的迭代器:Rust Playground

fn main() {
    let a: &[u8] = b"     fo o ";
    let b: Vec<u8> = a.iter().map(|x| x.clone()).skip_while(|x| x == &0x20 || x == &0x09).collect();
    println!("{:?}", b);
}

但是为了修剪正确的字符,如果在找到空格后列表中没有其他字母,我需要向前看。

string arrays rust trim
4个回答
7
投票

这是一个返回切片的实现,而不是像

Vec<u8>
那样返回一个新的
str::trim()
。它也在
[u8]
上实现,因为它比
Vec<u8>
更通用(您可以廉价地从向量中获取切片,但是从切片创建向量的成本更高,因为它涉及堆分配和复制)。

trait SliceExt {
    fn trim(&self) -> &Self;
}

impl SliceExt for [u8] {
    fn trim(&self) -> &[u8] {
        fn is_whitespace(c: &u8) -> bool {
            *c == b'\t' || *c == b' '
        }

        fn is_not_whitespace(c: &u8) -> bool {
            !is_whitespace(c)
        }

        if let Some(first) = self.iter().position(is_not_whitespace) {
            if let Some(last) = self.iter().rposition(is_not_whitespace) {
                &self[first..last + 1]
            } else {
                unreachable!();
            }
        } else {
            &[]
        }
    }
}

fn main() {
    let a = b"     fo o ";
    let b = a.trim();
    println!("{:?}", b);
}

如果你真的需要在

Vec<u8>
后面有一个
trim()
,你可以在切片上调用
into()
将它变成一个
Vec<u8>

fn main() {
    let a = b"     fo o ";
    let b: Vec<u8> = a.trim().into();
    println!("{:?}", b);
}

5
投票

这是一个比其他答案简单得多的版本。

pub fn trim_ascii_whitespace(x: &[u8]) -> &[u8] {
    let from = match x.iter().position(|x| !x.is_ascii_whitespace()) {
        Some(i) => i,
        None => return &x[0..0],
    };
    let to = x.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap();
    &x[from..=to]
}

奇怪的是这不在标准库中。我本以为这是一项常见的任务。

无论如何,它是一个完整的文件/特征(带有测试!),您可以复制/粘贴。

use std::ops::Deref;

/// Trait to allow trimming ascii whitespace from a &[u8].
pub trait TrimAsciiWhitespace {
    /// Trim ascii whitespace (based on `is_ascii_whitespace()`) from the
    /// start and end of a slice.
    fn trim_ascii_whitespace(&self) -> &[u8];
}

impl<T: Deref<Target=[u8]>> TrimAsciiWhitespace for T {
    fn trim_ascii_whitespace(&self) -> &[u8] {
        let from = match self.iter().position(|x| !x.is_ascii_whitespace()) {
            Some(i) => i,
            None => return &self[0..0],
        };
        let to = self.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap();
        &self[from..=to]
    }
}

#[cfg(test)]
mod test {
    use super::TrimAsciiWhitespace;

    #[test]
    fn basic_trimming() {
        assert_eq!(b" A ".trim_ascii_whitespace(), b"A");
        assert_eq!(b" AB ".trim_ascii_whitespace(), b"AB");
        assert_eq!(b"A ".trim_ascii_whitespace(), b"A");
        assert_eq!(b"AB ".trim_ascii_whitespace(), b"AB");
        assert_eq!(b" A".trim_ascii_whitespace(), b"A");
        assert_eq!(b" AB".trim_ascii_whitespace(), b"AB");
        assert_eq!(b" A B ".trim_ascii_whitespace(), b"A B");
        assert_eq!(b"A B ".trim_ascii_whitespace(), b"A B");
        assert_eq!(b" A B".trim_ascii_whitespace(), b"A B");
        assert_eq!(b" ".trim_ascii_whitespace(), b"");
        assert_eq!(b"  ".trim_ascii_whitespace(), b"");
    }
}

0
投票

我们要做的就是找到第一个非空白字符的索引,一次从头往前数,另一次从尾数倒数。

fn is_not_whitespace(e: &u8) -> bool {
    *e != 0x20 && *e != 0x09
}

fn main() {
    let a: &[u8] = b"     fo o ";

    // find the index of first non-whitespace char
    let begin = a.iter()
        .position(is_not_whitespace);

    // find the index of the last non-whitespace char
    let end = a.iter()
        .rev()
        .position(is_not_whitespace)
        .map(|j| a.len() - j);

    // build it
    let vec = begin.and_then(|i| end.map(|j| a[i..j].iter().collect()))
        .unwrap_or(Vec::new());

    println!("{:?}", vec);
}

0
投票

结合 Francis GagnéTimmmm 给出的解决方案,我们有:

/// Trait to allow trimming ascii whitespace from a &[u8].
pub trait SliceExt {
    fn trim(&self) -> &Self;
}

impl SliceExt for [u8] {
    /// https://stackoverflow.com/questions/31101915/how-to-implement-trim-for-vecu8
    /// 
    /// Trim ascii whitespace (based on is_ascii_whitespace())
    /// from the start and end of &\[u8\].
    /// 
    /// Returns &\[u8\] with leading and trailing whitespace removed.
    fn trim(&self) -> &[u8] {
        let from = match self.iter().position(|x| !x.is_ascii_whitespace()) {
            Some(i) => i,
            None => return &self[0..0],
        };
        let to = self.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap();
        &self[from..=to]
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn basic_trimming() {
        // cargo test -- basic_trimming
        assert_eq!(b" A ".trim(), b"A");
        assert_eq!(b" AB ".trim(), b"AB");
        assert_eq!(b"A ".trim(), b"A");
        assert_eq!(b"AB ".trim(), b"AB");
        assert_eq!(b" A".trim(), b"A");
        assert_eq!(b" AB".trim(), b"AB");
        assert_eq!(b" A B ".trim(), b"A B");
        assert_eq!(b"A B ".trim(), b"A B");
        assert_eq!(b" A B".trim(), b"A B");
        assert_eq!(b" ".trim(), b"");
        assert_eq!(b"  ".trim(), b"");
        assert_eq!(b"\nA\n".trim(), b"A");
        assert_eq!(b"\nA  B\r\n".trim(), b"A  B");
        assert_eq!(b"\r\nA  B\r\n".trim(), b"A  B");
    }
}

参观Rust 游乐场

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.