2016-03-09 10 views
16

Ho una sezione &[u8] su un buffer binario. Ho bisogno di analizzarlo, ma molti dei metodi che vorrei usare (come str::find) non sembrano disponibili su slice.Come posso trovare una sottosequenza in una sezione & [u8]?

Ho visto che riesco a coprire sia il buffer slice che il mio pattern a str usando from_utf8_unchecked() ma sembra un po 'pericoloso (e anche molto hacky).

Come posso trovare una sottosequenza in questa sezione? In realtà ho bisogno dell'indice del pattern, non solo di una vista a sezioni delle parti, quindi non credo che lo standard split funzionerà.

+3

C'è l'interesse si sta espandendo il concetto di 'pattern' a fette arbitrari: [commento] (https://github.com/rust-lang/rust/issues/27721#issuecomment-185405392), [RFC ] (https://github.com/rust-lang/rfcs/issues/984). – Shepmaster

+0

@ FrancisGagné Scusate, volevo dire che avevo bisogno dell'indice del sottoarray, non solo di una porzione di esso. Concretamente, sto cercando dei limiti in un pacchetto di rete per vedere se ho un messaggio completo. – JasonN

risposta

11

Ecco una semplice implementazione basata sull'iteratore windows.

fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> { 
    haystack.windows(needle.len()).position(|window| window == needle) 
} 

fn main() { 
    assert_eq!(find_subsequence(b"qwertyuiop", b"tyu"), Some(4)); 
    assert_eq!(find_subsequence(b"qwertyuiop", b"asd"), None); 
} 

La funzione find_subsequence può anche essere reso generico:

fn find_subsequence<T>(haystack: &[T], needle: &[T]) -> Option<usize> 
    where for<'a> &'a [T]: PartialEq 
{ 
    haystack.windows(needle.len()).position(|window| window == needle) 
} 
+0

Molto bello. Penso di averlo fatto praticamente a mano con due loop nidificati. I sottotitoli che sto cercando sono tutti molto piccoli, quindi fare qualcosa di più complesso come KMP sarebbe inutile per i miei problemi. – JasonN

+2

Sebbene si tratti di una soluzione breve e piacevole, si noti che l'algoritmo viene eseguito in O (| haystack | * | needle |). Questo non importa nella maggior parte dei casi, ma per algoritmi più avanzati e (asintoticamente) più veloci, vedi [Algoritmo di ricerca delle stringhe (Wikipedia)] (https://en.wikipedia.org/wiki/String_searching_algorithm). –

+0

Questo finisce per essere inaccettabilmente lento. windows(). position() è 100 volte più lento di due cicli annidati. – JasonN

2

non credo che la libreria standard contiene una funzione per questo. Alcune libc hanno lo memmem, ma al momento la cassa di libc non lo avvolge. È tuttavia possibile utilizzare la cassa twoway. rust-bio implementa anche alcuni algoritmi di corrispondenza del modello. Tutti dovrebbero essere più veloci dell'uso di haystack.windows(..).position(..)

2

Che ne dici di Regex on bytes? Sembra molto potente. Vedi questo rust playground demo.

// This shows how to find all null-terminated strings in a slice of bytes 
let re = Regex::new(r"(?-u)(?P<cstr>[^\x00]+)\x00").unwrap(); 
let text = b"foo\x00bar\x00baz\x00"; 

// Extract all of the strings without the null terminator from each match. 
// The unwrap is OK here since a match requires the `cstr` capture to match. 
let cstrs: Vec<&[u8]> = 
    re.captures_iter(text) 
     .map(|c| c.name("cstr").unwrap().as_bytes()) 
     .collect(); 
assert_eq!(vec![&b"foo"[..], &b"bar"[..], &b"baz"[..]], cstrs); 
Problemi correlati