7

What is the best way to replace some bytes in a byte array??

For instance i have bytesFromServer = listener.Receive(ref groupEP); and i can do BitConverter.ToString(bytesFromServer) to convert it into a readable format to return something along the lines of

48 65 6c 6c 6f 20 
74 68 65 72 65 20 
68 65 6c 70 66 75 
6c 20 70 65 6f 70 
6c 65   

I would like to replace something inside of that making "68 65 6c" to something like "68 00 00" (just as an example). There is not .Replace() on a byte[].

Would there be an easy way to convert that back into a byte[]?

Any help appreciated. Thank you!

E3pO
  • 493
  • 1
  • 9
  • 21

7 Answers7

10

You could program it.... try this for a start... this is however not robust not production like code yet...beaware of off-by-one errors I didn't fully test this...

    public int FindBytes(byte[] src, byte[] find)
    {
        int index = -1;
        int matchIndex = 0;
        // handle the complete source array
        for(int i=0; i<src.Length; i++)
        {
            if(src[i] == find[matchIndex])
            {
                if (matchIndex==(find.Length-1))
                {
                    index = i - matchIndex;
                    break;
                }
                matchIndex++;
            }
            else if (src[i] == find[0])
            {
                matchIndex = 1;
            }
            else
            {
                matchIndex = 0;
            }

        }
        return index;
    }

    public byte[] ReplaceBytes(byte[] src, byte[] search, byte[] repl)
    {
        byte[] dst = null;
        int index = FindBytes(src, search);
        if (index>=0)
        {
            dst = new byte[src.Length - search.Length + repl.Length];
            // before found array
            Buffer.BlockCopy(src,0,dst,0, index);
            // repl copy
            Buffer.BlockCopy(repl,0,dst,index,repl.Length);
            // rest of src array
            Buffer.BlockCopy(
                src, 
                index+search.Length , 
                dst, 
                index+repl.Length, 
                src.Length-(index+search.Length));
        }
        return dst;
    }

Implement as an extension method

public void Replace(this byte[] src, byte[] search, byte[] repl)
{
      ReplaceBytes(src, search, repl);
}

usage normal method:

ReplaceBytes(bytesfromServer, 
             new byte[] {0x75, 0x83 } , 
             new byte[]{ 0x68, 0x65, 0x6c});

Extension method usage:

bytesfromServer.Replace(
             new byte[] {0x75, 0x83 }, 
             new byte[]{ 0x68, 0x65, 0x6c});
Egoulet
  • 63
  • 2
  • 5
rene
  • 41,474
  • 78
  • 114
  • 152
  • I'm guessing the 9 is where it is starting to replace? What if everything before what you want to replace is not static? For instance if i wanted to change `0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x30` to `0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x39, 0x39` – E3pO Feb 27 '11 at 11:34
  • Yes, 9 is the start index. If the index may vary you have to do a search, loop over the array check if the byte or bytes match your criteia and use that as thre index... – rene Feb 27 '11 at 11:38
  • so you want to replace in an array of bytes a specific searcharray with a new array where the search array and the new array can be if different length....hmmm... – rene Feb 27 '11 at 11:41
  • Indeed. Almost like string.Replace("Hello", "Hello World"); But for a byte. – E3pO Feb 27 '11 at 11:43
  • @rene, This returns the same value i put into it... `byte[] hellokitty = new byte[] { 0x57, 0x27, 0x00, 0x31, 0x36 }; ReplaceBytes(hellokitty, new byte[] { 0x57, 0x27, 0x00, 0x31, 0x36 }, new byte[] { 0x57, 0x27, 0x00, 0x35, 0x30 }); textBox1.Text = BitConverter.ToString(hellokitty);` and if i do hellokitty = ReplaceBytes etc it makes hellokitty null. – E3pO Feb 27 '11 at 12:45
  • @E3pO I debugged it for you, I updated the code (In the Find method the src array is now fully travelled) – rene Feb 27 '11 at 13:00
  • 1
    FindBytes('bababanana', 'babanana') will fail. It will correctly match the first four bytes, then fail on i=4, resetting matchIndex = 0. Then it searches for babanana in banana, and doesn't find it. – bchurchill Jul 17 '15 at 10:29
6

Improving on rene's code, I created a while loop for it to replace all occurences:

public static byte[] ReplaceBytes(byte[] src, byte[] search, byte[] repl)
{
    byte[] dst = null;
    byte[] temp = null;
    int index = FindBytes(src, search);
    while (index >= 0)
    {
        if (temp == null)
            temp = src;
        else
            temp = dst;

        dst = new byte[temp.Length - search.Length + repl.Length];

        // before found array
        Buffer.BlockCopy(temp, 0, dst, 0, index);
        // repl copy
        Buffer.BlockCopy(repl, 0, dst, index, repl.Length);
        // rest of src array
        Buffer.BlockCopy(
            temp,
            index + search.Length,
            dst,
            index + repl.Length,
            temp.Length - (index + search.Length));


        index = FindBytes(dst, search);
    }
    return dst;
}

This method will work, but if the source bytes is too huge, I prefer to have a "windowing" function to process the bytes chunk by chunk. Else it will take a huge amount of memory.

4

How about Array.Copy?

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
1

Unfortunately there are issues with all of the posts (as already pointed out in comments). There is a correct answer in this other question

I needed a solution so for myself and wrote the following code. This is also more flexible in using enumerable and multiple search replace terms.

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


public class ByteTools
{
    static void ByteReplaceTests()
    {
        var examples = new(string source, string search, string replace)[]
            {
                ("bababanana", "babanana", "apple"),
                ("hello guys", "hello", "hello world"),
                ("apple", "peach", "pear"),
                ("aaaa", "a", "abc"),
                ("pear", "pear", ""),
                ("good morning world", "morning", "morning"),
                ("ababab", "ab", "ababab"),
                ("ababab", "abab", "ab"),
                ("", "aa", "bb"),
            };

        int i = 0;
        foreach (var (source, search, replace) in examples)
        {
            var stringReplaceResults = source.Replace(search, replace);

            var sourceByte = Encoding.ASCII.GetBytes(source);
            var searchByte = Encoding.ASCII.GetBytes(search);
            var replaceByte = Encoding.ASCII.GetBytes(replace);
            //converts string values to bytes, does the replace, then converts back to string
            var byteReplaceResults = Encoding.ASCII.GetString(
                ByteReplace(sourceByte, (searchByte, replaceByte)).ToArray());

            Console.WriteLine($"{i}: {source}, {search}, {replace}");
            Console.WriteLine($"       String.Replace() => {stringReplaceResults}");
            Console.WriteLine($"       BytesReplace()   => {byteReplaceResults}");
            i++;
        }
    }

    static IEnumerable<byte> ByteReplace(IEnumerable<byte> source, params (byte[] search, byte[] replace)[] replacements)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));
        if (replacements == null)
            throw new ArgumentNullException(nameof(replacements));
        if (replacements.Any(r => r.search == null || r.search.Length == 0))
            throw new ArgumentOutOfRangeException(nameof(replacements), "Search parameter cannot be null or empty");
        if (replacements.Any(r => r.replace == null))
            throw new ArgumentOutOfRangeException(nameof(replacements), "Replace parameter cannot be null");

        var maxMatchSize = replacements.Select(r => r.search.Length).Max();
        var bufferSize = maxMatchSize * 2;
        var buffer = new byte[bufferSize];
        int bufferStart = 0;
        int bufferPosition = 0;

        byte[] nextBytes()
        {
            foreach ((byte[] search, byte[] replace) in replacements)
            {
                if (ByteStartsWith(buffer, bufferStart, bufferPosition - bufferStart, search))
                {
                    bufferStart += search.Length;
                    return replace;
                }
            }

            var returnBytes = new byte[] { buffer[bufferStart] };
            bufferStart++;
            return returnBytes;
        }

        foreach (var dataByte in source)
        {
            buffer[bufferPosition] = dataByte;
            bufferPosition++;

            if (bufferPosition - bufferStart >= maxMatchSize)
            {
                foreach (var resultByte in nextBytes())
                    yield return resultByte;
            }

            if (bufferPosition == bufferSize - 1)
            {
                Buffer.BlockCopy(buffer, bufferStart, buffer, 0, bufferPosition - bufferStart);
                bufferPosition -= bufferStart;
                bufferStart = 0;
            }
        }

        while (bufferStart < bufferPosition)
        {
            foreach (var resultByte in nextBytes())
                yield return resultByte;
        }
    }
    static bool ByteStartsWith(byte[] data, int dataOffset, int dataLength, byte[] startsWith)
    {
        if (data == null)
            throw new ArgumentNullException(nameof(data));

        if (startsWith == null)
            throw new ArgumentNullException(nameof(startsWith));

        if (dataLength < startsWith.Length)
            return false;

        for (int i = 0; i < startsWith.Length; i++)
        {
            if (data[i + dataOffset] != startsWith[i])
                return false;
        }

        return true;
    }
}
webwake
  • 1,154
  • 1
  • 13
  • 26
1
    public static byte[] ReplaceBytes(byte[] src, byte[] search, byte[] repl)
    {
        if (repl == null) return src;
        int index = FindBytes(src, search);
        if (index < 0) return src;
        byte[] dst = new byte[src.Length - search.Length + repl.Length];
        Buffer.BlockCopy(src, 0, dst, 0, index);
        Buffer.BlockCopy(repl, 0, dst, index, repl.Length);
        Buffer.BlockCopy(src, index + search.Length, dst, index + repl.Length,src.Length - (index + search.Length));
        return dst;
    }

    public static int FindBytes(byte[] src, byte[] find)
    {
        if(src==null|| find==null|| src.Length==0|| find.Length == 0 || find.Length> src.Length) return -1;
        for (int i = 0; i < src.Length - find.Length +1 ; i++)
        {
            if (src[i] == find[0])
            {
               for(int m=1;m< find.Length;m++)
               {
                    if (src[i + m] != find[m]) break;
                    if (m == find.Length - 1) return i;
               }
            }
        }
        return -1;
    }

this may be a good method , i have test in lots of codes.

lulianqi
  • 17
  • 2
0

Something i pieced together... Going to test it soon. Credits from How do you convert Byte Array to Hexadecimal String, and vice versa?

     public byte[] ReplaceBytes(byte[] src, string replace, string replacewith)
    {
        string hex = BitConverter.ToString(src);
        hex = hex.Replace("-", "");
        hex = hex.Replace(replace, replacewith);
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }
Community
  • 1
  • 1
E3pO
  • 493
  • 1
  • 9
  • 21
  • 4
    it is a little bit of a waste to convert from a byte array to a string and back to byte array... – rene Feb 27 '11 at 12:17
  • This could match a hex pattern across two bytes 00-10-20 if you did a replace for 01 it would straddle the first two bytes. – webwake Feb 27 '18 at 14:49
0

If you want to replace all the occurrence, you can use the below code:

using System;

class Program
{
    static void ReplaceAll(byte[] source, byte[] oldBytes, byte[] newBytes) 
    {
        for (int i = 0; i < source.Length - oldBytes.Length + 1; i++) 
        {
            bool match = true; 
            for (int j = 0; j < oldBytes.Length; j++) 
            {
                if (source[i + j] != oldBytes[j]) 
                {
                    match = false; 
                    break; 
                }
            }
            if (match) 
            {
                Array.Copy(newBytes, 0, source, i, newBytes.Length); 
            }
        }
    }
    
    static void Main()
    {
        byte[] original = {90, 97, 99, 97, 98, 99}; // original byte array
        byte[] oldBytes = {97, 98, 99}; // old byte array to be replaced
        byte[] newBytes = {100, 101, 102}; // new byte array to replace with
        ReplaceAll(original, oldBytes, newBytes); 
        Console.WriteLine(BitConverter.ToString(original));
    }
}
SLdragon
  • 1,477
  • 16
  • 19