3

How can I replace a subarray of a byte array?

I need something similar with replace method for strings:

"I want pizza".replace("pizza", "replace method for byte arrays")

but it should work for byte[].

webpersistence
  • 894
  • 3
  • 12
  • 29
  • what kind of data is in the byte array you are handling? – Renato Apr 21 '20 at 16:21
  • one possibility would be to wrap the bytes in a input stream and then do as suggested in this answer: https://stackoverflow.com/questions/7743534/filter-search-and-replace-array-of-bytes-in-an-inputstream/11158499 – Renato Apr 21 '20 at 16:24
  • @Renato it's binary data from image files – webpersistence Apr 21 '20 at 16:43
  • 1
    A dirty trick you could try would be to simply do `new String(bytes)` and pray it will work, then use String's `replace()` with Strings built in the same way, then get back your bytes with `str.getBytes()`. But with an image file, I think you will run into illegal byte sequences for pretty much any String encoding, so that's likely unsafe. – Renato Apr 21 '20 at 17:35
  • The algorithm for bytes would be the same as for characters, so you could copy the Java implementation and change the types to bytes: http://www.docjar.com/html/api/java/lang/String.java.html – Renato Apr 21 '20 at 17:41
  • @Renato the algorithm from your link uses regex. I do not think it will work – webpersistence Apr 21 '20 at 18:01
  • 1
    @Renato if you use an encoding such as ISO_8859_1 which has a one-to-one mapping between a byte and a char, then you can do that. Yes, ugly. – Johannes Kuhn Apr 21 '20 at 18:18

2 Answers2

3

Assuming you want to replace an n-byte subarray with another n-byte subarray, you can use System.arraycopy.

For example:

System.arraycopy(theNewBytes, startPosition,
                 theArrayYouWantToUpdate, startPosition1, length);

If you want to replace n bytes with some different number of bytes, you would need to create a new array anyway:

  • Create the new array with the desired length (prefix length + new portion length + suffix length)
  • Copy the prefix from the existing array, using arrayCopy
  • Copy the new portion
  • Copy the suffix from the existing array.
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
-1

It is possible to use the power of the almighty String to do this :)

static byte[] replace(byte[] src, byte[] find, byte[] replace) {
    String replaced = cutBrackets(Arrays.toString(src))
            .replace(cutBrackets(Arrays.toString(find)), cutBrackets(Arrays.toString(replace)));

    return Arrays.stream(replaced.split(", "))
            .map(Byte::valueOf)
            .collect(toByteArray());
}

private static String cutBrackets(String s) {
    return s.substring(1, s.length() - 1);
}

private static Collector<Byte, ?, byte[]> toByteArray() {
    return Collector.of(ByteArrayOutputStream::new, ByteArrayOutputStream::write, (baos1, baos2) -> {
        try {
            baos2.writeTo(baos1);
            return baos1;
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }, ByteArrayOutputStream::toByteArray);
}

Using custom Collector.toByteArray from this answer

Test results:

byte[] arr = {10, 20, 30, 40, 50, 51, 52, 53, 54, 62, 63};
byte[] find = {10, 20, 30};
byte[] replace = {21, 22, 23, 31, 32, 33};

System.out.println(Arrays.toString(replace(arr, find, replace)));

Output:

[21, 22, 23, 31, 32, 33, 40, 50, 51, 52, 53, 54, 62, 63]
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
  • Why are you using Strings here? Will the conversion from bytes work? – webpersistence Apr 21 '20 at 18:04
  • I did it just for fun to check if this could be implemented. Yes, conversion of bytes array with `Arrays.toString()` works fine – Nowhere Man Apr 21 '20 at 18:08
  • 1
    This is a rather expensive way of doing it: not only is this converting to a string (taking a lot more memory than the original), it's also using regex, because `String.replace` actually invokes `replaceAll`, after quoting its arguments. You can reasonably easily implement an `indexOf` method that finds the start position of a subarray in another array (fancy algorithms exist, but `String.indexOf` is actually naive); then use `System.arrayCopy`. – Andy Turner Apr 21 '20 at 18:47
  • @AndyTurner I was and am aware of all those overheads :) The point was to reuse as much of existing String functionality as possible. Sorry if it was not that funny. – Nowhere Man Apr 21 '20 at 19:41
  • @AlexRudenko the point isn't necessarily that *you* need to be made aware of them, but readers of your answer do. – Andy Turner Apr 21 '20 at 19:54