Utilisation de LINQ pour rechercher dans un tableau d’octets tous les sous-tableaux démarrant / s’arrêtant avec certains octets

Je traite avec une application de port COM et nous avons une structure de paquet de longueur variable définie avec laquelle je parle avec un micro-contrôleur. Le paquet a des délimiteurs pour les octets de début et de fin. Le problème est que parfois le tampon de lecture peut contenir des caractères superflus. Il semble que je vais toujours avoir le paquet entier, juste quelques bavardages supplémentaires avant / après les données réelles. J’ai donc un tampon auquel j’ajoute des données chaque fois que de nouvelles données sont reçues du port COM. Quel est le meilleur moyen de rechercher dans cette mémoire tampon toutes les occurrences possibles de mon paquet? Par exemple:

Dites que mon délimiteur de paquet est 0xFF et que j’ai un tableau en tant que tel

 { 0x00, 0xFF, 0x02, 0xDA, 0xFF, 0x55, 0xFF, 0x04 } 

Comment créer une fonction / une instruction LINQ qui renvoie tous les sous-tableaux commençant et se terminant par le délimiteur (presque comme un corrélateur à glissements avec des caractères génériques)?

L’échantillon renverrait les 3 tableaux suivants:

 {0xFF, 0x02, 0xDA, 0xFF}, {0xFF, 0x55, 0xFF}, and {0xFF, 0x02, 0xDA, 0xFF, 0x55, 0xFF} 

    Voici comment faire cela avec LINQ …

     int[] list = new int[] { 0x00, 0xFF, 0x02, 0xDA, 0xFF, 0x55, 0xFF, 0x04 }; int MAXLENGTH = 10; var windows = list.Select((element, i) => list.Skip(i).Take(MAXLENGTH)); var matched = windows.Where(w => w.First() == 0xFF); var allcombinations = matched.SelectMany(m => Enumerable.Range(1, m.Count()) .Select(i => m.Take(i)).Where(x => x.Count() > 2 && x.Last() == 0xFF)); 

    Ou en utilisant des index:

     int length = list.Count(); var indexes = Enumerable.Range(0, length) .SelectMany(i => Enumerable.Range(3, Math.Min(length-i, MAXLENGTH)) .Select(count => new {i, count})); var results = indexes.Select(index => list.Skip(index.i).Take(index.count)) .Where(x => x.First() == 0xFF && x.Last() == 0xFF); 

    Bien que la réponse de Trystan soit techniquement correcte, il fait beaucoup de copies du tableau d’origine en même temps. Si la masortingce de départ est grande et comporte de nombreux délimiteurs, cela devient vite énorme. Cette approche évite la consommation de mémoire massive en utilisant uniquement le tableau d’origine et un tableau pour le segment en cours d’évaluation.

     public static List> GetSubArrays(this byte[] array, byte delimeter) { if (array == null) throw new ArgumentNullException("array"); List> retval = new List>(); for (int i = 0; i < array.Length; i++) { if (array[i] == delimeter) { for (int j = i + 1; j < array.Length; j++) { if (array[j] == delimeter) { retval.Add(new ArraySegment(array, i + 1, j - i - 1)); } } } } return retval; } 

    Peut être utilisé tel quel:

     static void Main(ssortingng[] args) { byte[] arr = new byte[] { 0x00, 0xFF, 0x02, 0xDA, 0xFF, 0x55, 0xFF, 0x04 }; List> retval = GetSubArrays(arr, 0xFF); // this also works (looks like LINQ): //List> retval = arr.GetSubArrays(0xFF); byte[] buffer = new byte[retval.Select(x => x.Count).Max()]; foreach (var x in retval) { Buffer.BlockCopy(x.Array, x.Offset, buffer, 0, x.Count); Console.WriteLine(Ssortingng.Join(", ", buffer.Take(x.Count).Select(b => b.ToSsortingng("X2")).ToArray())); } Console.ReadLine(); } 

    Si vous voulez vraiment utiliser LINQ, cela devrait fonctionner assez rapidement (même si ce n’est pas aussi rapide qu’une bonne boucle for old):

     public static IEnumerable GetPackets(this IList buffer, T delimiter) { // gets delimiters' indexes var delimiterIdxs = Enumerable.Range(0, buffer.Count()) .Where(i => buffer[i].Equals(delimiter)) .ToArray(); // creates a list of delimiters' indexes pair (startIdx,endIdx) var dlmtrIndexesPairs = delimiterIdxs.Take(delimiterIdxs.Count() - 1) .SelectMany( (startIdx, idx) => delimiterIdxs.Skip(idx + 1) .Select(endIdx => new { startIdx, endIdx }) ); // creates array of packets var packets = dlmtrIndexesPairs.Select(p => buffer.Skip(p.startIdx) .Take(p.endIdx - p.startIdx + 1) .ToArray()) .ToArray(); return packets; } 

    Je n’essaierais pas de faire cela avec linq alors voici une méthode régulière qui renvoie le même résultat que vous le souhaitiez.

     public List GetSubArrays(byte[] array, byte delimeter) { if (array == null) throw new ArgumentNullException("array"); List subArrays = new List(); for (int i = 0; i < array.Length; i++) { if (array[i] == delimeter && i != array.Length - 1) { List subList = new List() { delimeter }; for (int j = i+1; j < array.Length; j++) { subList.Add(array[j]); if (array[j] == delimeter) { subArrays.Add(subList.ToArray()); } } } } return subArrays; } 

    S'il doit s'agir d'une expression lambda sur place, remplacez simplement la première ligne par (byte[] array, byte delimeter) => (sans les modificateurs de méthode et le nom) et appelez-le ainsi.

    Bien que la structure du délimiteur semble un peu vague, je ne voudrais pas utiliser linq et faire quelque chose comme ci-dessous (pas de tests approfondis effectués). Il retournera tous les sous-ensembles (d’octets entourés par le délimiteur), sans inclure le délimiteur (c’est une donnée, pourquoi l’inclure?). Il ne retourne pas non plus l’union des résultats, mais cela peut toujours être assemblé manuellement.

     public IEnumerable GetArrays(byte[] data, byte delimiter) { List arrays = new List(); int start = 0; while (start >= 0 && (start = Array.IndexOf(data, delimiter, start)) >= 0) { start++; if (start >= data.Length - 1) { break; } int end = Array.IndexOf(data, delimiter, start); if (end < 0) { break; } byte[] sub = new byte[end - start]; Array.Copy(data, start, sub, 0, end - start); arrays.Add(sub); start = end; } return arrays; } 

    Vous pouvez le faire en utilisant un agrégateur Linq, mais c’est beaucoup moins simple que les autres solutions suggérées ici. Vous devez également append un cas particulier pour couvrir l’extension des tableaux déjà terminés, comme vous l’avez suggéré ci-dessus.

     byte[] myArray = new byte[] { 0x00, 0xFF, 0x02, 0xDA, 0xFF, 0x55, 0xFF, 0x04 }; var arrayList = myArray.Aggregate( new { completedLists = new List>(), activeList = new List() }, (seed, s) => { if (s == 0xFF) { if (seed.activeList.Count == 0) { seed.activeList.Add(s); } else { seed.activeList.Add(s); var combinedLists = new List>(); foreach (var l in seed.completedLists) { var combinedList = new List(l); combinedList.AddRange(seed.activeList.Skip(1)); combinedLists.Add(combinedList); } seed.completedLists.AddRange(combinedLists); seed.completedLists.Add(new List(seed.activeList)); seed.activeList.Clear(); seed.activeList.Add(s); } } else { if (seed.activeList.Count > 0) seed.activeList.Add(s); } return seed; }).completedLists;