rust 如何找到另一个字符串的字符串切片的起始偏移量?[duplicate]

ws51t4hk  于 2023-01-05  发布在  其他
关注(0)|答案(2)|浏览(106)
    • 此问题在此处已有答案**:

How to get the byte offset between &str(2个答案)
三年前关闭了。
给定一个字符串和一个引用某个子字符串的切片,是否有可能找到切片的起始和结束索引?
我有一个ParseString函数,它接受一个字符串的引用,并试图根据一些语法来解析它:

ParseString(inp_string: &str) -> Result<(), &str>

如果解析正常,结果就是Ok(()),但是如果有错误,通常是在某个子字符串中,错误示例是Err(e),其中e是该子字符串的一个切片。
当给定发生错误的子字符串时,我想说类似于"Error from characters x to y"的话,其中x和y是错误子字符串的开始和结束索引。
我不想直接在Err中编码错误的位置,因为我嵌套了这些调用,并且嵌套切片中的偏移量可能与顶级字符串中的some切片不对应。

cbjzeqam

cbjzeqam1#

只要所有的字符串切片都是从同一个字符串缓冲区借用的,就可以用简单的指针算法计算偏移量,需要使用以下方法:

  • str::as_ptr():返回指向字符串切片开始的指针
  • 一种求两个指针之差的方法。目前,最简单的方法是将两个指针都转换为usize(这总是一个空操作),然后减去它们。在1.47.0+中,有一个方法offset_from(),稍微好一点。

下面是工作代码(Playground):

fn get_range(whole_buffer: &str, part: &str) -> (usize, usize) {
    let start = part.as_ptr() as usize - whole_buffer.as_ptr() as usize;
    let end = start + part.len();
    (start, end)
}

fn main() {
    let input = "Everyone ♥ Ümläuts!";
    
    let part1 = &input[1..7];
    println!("'{}' has offset {:?}", part1, get_range(input, part1));
    
    let part2 = &input[7..16];
    println!("'{}' has offset {:?}", part2, get_range(input, part2));
}
cig3rfwq

cig3rfwq2#

Rust实际上曾经有一个不稳定的方法来完成这个任务,但是it was removed due to being obsolete,考虑到the replacement didn't remotely have the same functionality,这有点奇怪。
也就是说,实现并不是很大,所以您只需将以下代码添加到代码的某个位置:

pub trait SubsliceOffset {
    /**
    Returns the byte offset of an inner slice relative to an enclosing outer slice.

    Examples

    ```ignore
    let string = "a\nb\nc";
    let lines: Vec<&str> = string.lines().collect();
    assert!(string.subslice_offset_stable(lines[0]) == Some(0)); // &"a"
    assert!(string.subslice_offset_stable(lines[1]) == Some(2)); // &"b"
    assert!(string.subslice_offset_stable(lines[2]) == Some(4)); // &"c"
    assert!(string.subslice_offset_stable("other!") == None);
    ```
    */
    fn subslice_offset_stable(&self, inner: &Self) -> Option<usize>;
}

impl SubsliceOffset for str {
    fn subslice_offset_stable(&self, inner: &str) -> Option<usize> {
        let self_beg = self.as_ptr() as usize;
        let inner = inner.as_ptr() as usize;
        if inner < self_beg || inner > self_beg.wrapping_add(self.len()) {
            None
        } else {
            Some(inner.wrapping_sub(self_beg))
        }
    }
}

如果不需要支持Rust的旧版本,可以删除_stable后缀;它只是为了避免与现已删除的subslice_offset方法发生名称冲突。

相关问题