2016-03-09 9 views
16

Ich habe eine &[u8] Scheibe über einen binären Puffer. Ich muss es analysieren, aber viele der Methoden, die ich gerne verwenden würde (wie str::find) scheinen nicht auf Slices verfügbar zu sein.Wie kann ich eine Teilsequenz in einem & [u8] Slice finden?

Ich habe gesehen, dass ich sowohl durch Pufferscheibe und mein Muster zu str mit from_utf8_unchecked() verdecken kann, aber das scheint ein wenig gefährlich (und auch wirklich hacky).

Wie kann ich eine Teilsequenz in diesem Segment finden? Ich brauche eigentlich den Index des Musters, nicht nur eine Schichtansicht der Teile, also glaube ich nicht, dass split funktionieren wird.

+3

Es Interesse ist, ist das Konzept der 'Pattern' auf beliebige Scheiben erweitert: [Kommentar] (https://github.com/rust-lang/rust/issues/27721#issuecomment-185405392), [RFC ] (https://github.com/rust-lang/rfcs/issues/984). – Shepmaster

+0

@ FrancisGagné Entschuldigung, ich meinte, ich brauche den Index des Subarrays, nicht nur ein Stück davon. Konkret suche ich nach Grenzen in einem Netzwerkpaket, um zu sehen, ob ich eine vollständige Nachricht habe. – JasonN

Antwort

11

Hier ist eine einfache Implementierung basierend auf dem windows Iterator.

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

Die find_subsequence Funktion kann auch Generika hergestellt werden:

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

Sehr schön. Ich denke, ich habe es im Grunde mit zwei verschachtelten For-Loops von Hand gemacht. Die Subarrays, nach denen ich suche, sind alle sehr klein, also wäre etwas Komplizierteres wie KMP für meine Probleme nutzlos. – JasonN

+2

Während dies eine kurze und schöne Lösung ist, beachten Sie bitte, dass der Algorithmus in O (| haystack | * | nadel |) läuft. Dies ist in den meisten Fällen nicht von Bedeutung, aber für fortgeschrittene und (asymptotisch) schnellere Algorithmen siehe [String-Suchalgorithmus (Wikipedia)] (https://en.wikipedia.org/wiki/String_searching_algorithm). –

+0

Dies ist inakzeptabel langsam. windows(). position() ist 100x langsamer als zwei verschachtelte Schleifen. – JasonN

2

Ich glaube nicht die Standard-Bibliothek, die eine Funktion für diese enthält. Einige libcs ​​haben memmem, aber im Moment wickelt die libc-Kiste das nicht ein. Sie können die twoway Kiste jedoch verwenden. rust-bio implementiert auch einige Mustervergleichsalgorithmen. Alle diese sollten schneller sein als mit haystack.windows(..).position(..)

2

Wie wäre es mit Regex on bytes? Das sieht sehr kraftvoll aus. Siehe hierzu 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); 
Verwandte Themen