-2

I came to a problem with writing negative byte values into binary file using FileOutputStream, where negative byte values, ex. -1 is written into file but it takes there two bytes and they seem to me to be completely nonsense.

In my application I have toByteArray() on some objects returning byte[] so I can write it into a file. Even though it "works" on many objects I got object that gets serialized to byte array where there are some negative bytes (new byte[] {0, 0, 0, 1, -1, -1, -1, -1, 1, 3, -48, -5, 10}) and when I write this object into a file the negative ones are written there as "c0 a2" bytes.

Test case:

public void testWRITE() {
    String fileName = "TEST_FILE.lldb";
    String secondFileName = "SECOND_FILE.lldb";
    FileOutputStream fos;
    FileOutputStream fos2;
    try {
        fos = new FileOutputStream(fileName, false);
        fos2 = new FileOutputStream(secondFileName, false);

        // Writing this to file writes bytes "c2 a0" in place of -1 byte
        FileChannel ch = fos.getChannel();
        ch.position(0);
        ch.write(ByteBuffer.wrap(new byte[] {0, 0, 0, 1, -1, -1, -1, -1, 1, 3, -48, -5, 10}));


        // Writing this to file writes "ff" in of -1 byte
        Pojo pojo = new Pojo();
        FileChannel ch2 = fos2.getChannel();
        ch2.position(0);
        ch2.write(ByteBuffer.wrap(pojo.getBytes()));

        fos.close();
        fos2.close();

    } catch (IOException e) {
        fail();
    }
}

where Pojo class is simple POJO as

public class Pojo {

    private Integer negativeNumber;

    public Pojo() {
        this.negativeNumber = -1;
    }

    public byte[] getBytes() {
        return Pojo.bytesFromInt(this.negativeNumber);
    }

    public static byte[] bytesFromInt(int value) {
         return new byte[] {
            (byte)(value >>> 24),
            (byte)(value >>> 16),
            (byte)(value >>> 8),
            (byte)value};
    }}

As I am counting on a fact that when I write one byte to a file it will be just one byte I can't proceed further with my work as it's a building stone of my library.

Wrong bytes instead of negative numbers Wrong bytes instead of negative numbers

Writing serialized POJO with negative integer converted to byte array Writing serialized POJO with negative integer converted to byte array

Is that even considered as expected behaviour of FileOutputStream? What am I missing?

El El
  • 27
  • 5
  • There is no such thing as a byte bigger than 127 in Java. They are signed, so their values are -128..127. All you are seeing here is sign extension. – user207421 Dec 05 '19 at 01:03
  • Are you sure that is the code which is generating that output? When I run that code, the resulting file contains: 00 00 00 01 ff ff ff ff 01 03 d0 fb 0a – VGR Dec 05 '19 at 01:10
  • I am certain about it I even tried it on friends machine with same result. What jdk did you use? That's all I can think of could be causing this. And user207421s answear is just not correct but whatever. – El El Dec 05 '19 at 01:17
  • I used OpenJDK 11.0.3, but I doubt that’s the issue. If a FileChannel or FileOutputStream were corrupting bytes, I think it would have gained a lot of attention by now. Are you making use of Strings anywhere? ‘c2 a0’ looks like a UTF-8 sequence. – VGR Dec 05 '19 at 02:36

2 Answers2

0

Everything is written correctly.

When you write negative numbers you are writing them in two's complement form.

So writing -5 = 1111 1011 which is FB in hex. That is what you are seeing.

And FF = 1111 1111 = -1.

Here is the modified code I used to check your results. Note that I added a logical file letter to the filenames. Perhaps I am doing something wrong.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;

public class Writing {
   public static void main(String[] args) {
      testWRITE();

   }

   public static void testWRITE() {
      String fileName = "F:/TEST_FILE.lldb";
      String secondFileName = "F:/SECOND_FILE.lldb";
      FileOutputStream fos;
      FileOutputStream fos2;
      try {
         fos = new FileOutputStream(fileName, false);
         fos2 = new FileOutputStream(secondFileName, false);

         // Writing this to file writes bytes "0c2 a0" in place of -1 byte
         FileChannel ch = fos.getChannel();
         ch.position(0);
         System.out.println("Writing to " + fileName);
         ByteBuffer b = ByteBuffer.wrap(
               new byte[] { 0, 0, 0, 1, -1, -1, -1, -1, 1, 3, -48, -5, 10
               });

         System.out.println(Arrays.toString(b.array()));
         ch.write(b);
         // Writing this to file writes "ff" in of -1 byte
         Pojo pojo = new Pojo();
         FileChannel ch2 = fos2.getChannel();
         ch2.position(0);
         System.out.println("Writing to " + secondFileName);
         ch2.write(ByteBuffer.wrap(pojo.getBytes()));
         System.out.println(Arrays.toString(pojo.getBytes()));

         fos.close();
         fos2.close();

         System.out.println();
         FileInputStream fs = new FileInputStream(fileName);
         FileInputStream fs2 = new FileInputStream(secondFileName);
         System.out.println("Reading all bytes from " + fileName);
         byte[] bs = fs.readAllBytes();
         System.out.println(Arrays.toString(bs));
         System.out.println("Reading all bytes from " + secondFileName);
         bs = fs2.readAllBytes();
         System.out.println(Arrays.toString(bs));
      }
      catch (IOException e) {
         System.out.println(e.getMessage());
      }
   }
}

class Pojo {

   private Integer negativeNumber;

   public Pojo() {
      this.negativeNumber = -1;
   }

   public byte[] getBytes() {
      return Pojo.bytesFromInt(this.negativeNumber);
   }

   public static byte[] bytesFromInt(int value) {
      return new byte[] {
            (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8),
            (byte) value
      };
   }
}

Here is my output.

Writing to F:/TEST_FILE.lldb
[0, 0, 0, 1, -1, -1, -1, -1, 1, 3, -48, -5, 10]
Writing to F:/SECOND_FILE.lldb
[-1, -1, -1, -1]

Reading all bytes from F:/TEST_FILE.lldb
[0, 0, 0, 1, -1, -1, -1, -1, 1, 3, -48, -5, 10]
Reading all bytes from F:/SECOND_FILE.lldb
[-1, -1, -1, -1]

WJS
  • 36,363
  • 4
  • 24
  • 39
  • No it's not mate. That byte array I inicialized has 13 bytes yet in file there are 19 as negative numbers are written really oddly. – El El Dec 05 '19 at 01:22
  • I wrote out the bytes as they were defined before you wrote them to the files. Then I read them back in as byte arrays and printed them out. What was written and what was read were identical. – WJS Dec 05 '19 at 01:26
0

Well this is kinda awkward as I am not just a beginner programmer but yes I was wrong there. When I read that file back to memory there are really just those bytes I had written into file before. But to support my post and attitude, I wasn't really running any other code only that that was posted. But viewing that file using Notepad++ with HEXEditor extension really shows me "FF" and "C2 A0" and I can't figure out why even though when I read file back to memory the bytes are "just bytes" but when I saw "C2 A0" in file I did not even think of reading it back to memory to check it. CaptainObvious mentioned doing UTF-8 conversion "somewhere" but if that's the case it's just not in my code.

El El
  • 27
  • 5