Michael Mankus
Michael Mankus

Reputation: 4778

How to properly search and parse an array using a sequence of elements as the target

I have a byte array that looks something like this:

byte[] exampleArray = new byte[] 
                      { 0x01, 0x13, 0x10, 0xe2, 0xb9, 0x13, 0x10, 0x75, 0x3a, 0x13 };

My end goal is to split this array into sub array's anytime I see the sequence { 0x13, 0x10 }. So my desired result on the example array would be:

{ 0x01 }
{ 0xe2, 0xb9 }
{ 0x75, 0x3a, 0x13 }

Ideally, I would also need to know that the final array, { 0x75, 0x3a, 0x13 }, did not end with the search sequence so that I could work with that as a special case.

Any thoughts on the best approach?

Upvotes: 5

Views: 328

Answers (4)

Ondrej Tucny
Ondrej Tucny

Reputation: 27962

I would proceed with checking the current and previous array element. Like this:

int[] data = ...;

// start from second byte, to be able to touch the previous one
int i = 1;
while (i < data.Length)
{
    if (data[i-1] == 0x13 && data[i] == 0x10)
    {
        // end of subarray reached
        Console.WriteLine();
        i+=2;
    }
    else
    {
        // output the previous character, won't be used in the next iteration
        Console.Write(data[i-1].ToString("X2"));
        i++;
    }
}

// process the last (or the only) byte of the array, if left
if (i == data.Length)
{
    // apparently there wasn't a delimiter in the array's last two bytes
    Console.Write(data[i-1].ToString("X2"));
    Console.WriteLine(" - no 0x13 010");
}

Note: Console output for the sake of demonstration. Replace with actual data processing.

Upvotes: 0

Vladimir
Vladimir

Reputation: 7475

string example = Encoding.ASCII.GetString(exampleArray);
string delimiter = Encoding.ASCII.GetString(new byte[] { 0x13, 0x10 });
string[] result = example.Split(new string[] { delimiter});
string ending = Encoding.ASCII.GetString(new byte[] { 0x75, 0x3a, 0x13 });
bool ends = example.EndsWith(ending);

Upvotes: 0

George Duckett
George Duckett

Reputation: 32428

How about something like this that should work in the general case (obviously with better error checking!):

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        byte[] exampleArray = new byte[] { 1, 2, 3, 4, 5, 2, 3, 6, 7, 8 };

        var test = exampleArray.PartitionBySubset(new byte[] { 2, 3 }).ToList();
    }
}

public static class Extensions
{
    public static IEnumerable<IEnumerable<T>> PartitionBySubset<T>(this IEnumerable<T> sequence, IEnumerable<T> subset) where T : IEquatable<T>
    {
        // Get our subset into a array so we can refer to items by index and so we're not iterating it multiple times.
        var SubsetArray = subset.ToArray();
        // The position of the subset array we will check
        var SubsetArrayPos = 0;
        // A list to hold current items in the subsequence, ready to be included in the resulting sequence
        var CurrentList = new List<T>();

        foreach (var item in sequence)
        {
            // add all items (ones part of the subset will be removed)
            CurrentList.Add(item);
            if (item.Equals(SubsetArray[SubsetArrayPos]))
            {
                // This item is part of the subset array, so move to the next subset position
                SubsetArrayPos++;
                // Check whether we've checked all subset positions
                if (SubsetArrayPos == SubsetArray.Length)
                {
                    // If so, then remove the subset items from the current list
                    CurrentList.RemoveRange(CurrentList.Count - SubsetArray.Length, SubsetArray.Length);
                    // Reset the position
                    SubsetArrayPos = 0;

                    // Only return the list if it's not empty (the main list could start with a subset)
                    if (CurrentList.Count != 0)
                    {
                        // Return the list we have now since it's been ended.
                        yield return CurrentList;
                        // Create a new list ready for more items
                        CurrentList = new List<T>();
                    }
                }
            }
            else
            {
                // This item isn't part of the subset, so next time check the start.
                SubsetArrayPos = 0;
            }
        }

        // If we get to the end and have some items to return, then return them.
        if (CurrentList.Count != 0)
            yield return CurrentList;
    }
}

Upvotes: 1

Viliam
Viliam

Reputation: 678

List<byte[]> Split(byte[] bArray)
        {
            List<byte[]> result = new List<byte[]>();
            List<byte> tmp = new List<byte>();
            int i, n = bArray.Length;
            for (i = 0; i < n; i++)
            {
                if (bArray[i] == 0x13 && (i + 1 < n && bArray[i + 1] == 0x10))
                {
                    result.Add(tmp.ToArray());
                    tmp.Clear();
                    i++;
                }
                else
                    tmp.Add(bArray[i]);
            }
            if (tmp.Count > 0)
                result.Add(tmp.ToArray());
            return result;
        }

The last array can not end with sequence, any splited part does not contein separator. Only byte 0x13 can happen, so if this is importane for you, you can jast check last byte of last sub array.

Upvotes: 1

Related Questions