Konwertuj tablicę bajtów na liczbę całkowitą w Javie i odwrotnie


139

Chcę przechowywać niektóre dane w tablicach bajtów w Javie. Zasadniczo tylko liczby, które mogą zająć do 2 bajtów na liczbę.

Chciałbym wiedzieć, jak mogę przekonwertować liczbę całkowitą na 2-bajtową tablicę bajtów i odwrotnie. Znalazłem wiele rozwiązań googlujących, ale większość z nich nie wyjaśnia, co dzieje się w kodzie. Jest wiele zmiennych rzeczy, których tak naprawdę nie rozumiem, więc doceniłbym podstawowe wyjaśnienie.


4
Ile nie rozumiesz o bitowego przesunięcia? Wygląda na to, że tak naprawdę pytanie brzmi „co powoduje przesunięcie bitowe” bardziej niż konwersja na tablice bajtowe, tak naprawdę - jeśli naprawdę chcesz zrozumieć, jak przebiegałaby konwersja.
— Jon Skeet

1
(Dla wyjaśnienia, nie przeszkadza mi żadne pytanie, ale warto wyjaśnić, na które pytanie naprawdę chcesz odpowiedzieć. Prawdopodobnie otrzymasz odpowiedź, która będzie dla ciebie bardziej przydatna.)
— Jon Skeet

Okej, rozumiem! Dzięki za uwagę. Wiem, do czego służy przesunięcie bitowe, po prostu nie rozumiałem jeszcze, do czego służy przy konwersji tablic bajtowych.
— Chris,

3
@prekageo i Jeff Mercado Dzięki za dwie odpowiedzi. prekageo dobrze wyjaśnił, jak to się robi, fajny link! To sprawia, że ​​jest to dla mnie dużo jaśniejsze. Rozwiązanie Jeffa Mercadosa rozwiązało problem, który miałem.
— Chris,

Odpowiedzi:


230

Użyj klas znajdujących się w java.nioprzestrzeni nazw, w szczególności klasy ByteBuffer. Może wykonać całą pracę za Ciebie.

byte[] arr = { 0x00, 0x01 };
ByteBuffer wrapped = ByteBuffer.wrap(arr); // big-endian by default
short num = wrapped.getShort(); // 1

ByteBuffer dbuf = ByteBuffer.allocate(2);
dbuf.putShort(num);
byte[] bytes = dbuf.array(); // { 0, 1 }

2
Czy to zbyt drogie, jeśli tablica bajtów zawiera tylko 1 lub 2 liczby całkowite? Nie jestem pewien co do kosztu budowy pliku ByteBuffer.
— Meow Cat 2012

Jak często pracujesz z danymi binarnymi w 2-4 bajtowych fragmentach? Naprawdę? Rozsądna implementacja działałaby z nią w fragmentach BUFSIZ (zwykle 4kb) lub korzystała z innych bibliotek IO, które ukrywają ten szczegół. W ramach tej platformy znajduje się cała biblioteka, która jest przeznaczona do pracy z buforami danych. Robisz niedźwiedzią przysługę sobie i innym opiekunom swojego kodu, gdy wdrażasz typowe operacje bez dobrego powodu (czy to perf, czy inna krytyczna operacja). Te bufory są po prostu opakowaniami, które działają na tablicach, nic więcej.
— Jeff Mercado

Jak możesz utworzyć instancję klasy abstrakcyjnej?

1
@JaveneCPPMcGowan W tej odpowiedzi nie ma bezpośredniej instancji. Jeśli masz na myśli metody fabryczne wrapi allocate, nie zwracają one wystąpienia klasy abstrakcyjnej ByteBuffer.
— Marko Topolnik

Nie jest rozwiązaniem dla 3-bajtowego kroku. Możemy dostać Char, Short, Int. Przypuszczam, że mógłbym dopełnić do 4 bajtów i za każdym razem odrzucić czwarty, ale wolałbym tego nie robić.
— Jan

128
byte[] toByteArray(int value) {
     return  ByteBuffer.allocate(4).putInt(value).array();
}

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

int fromByteArray(byte[] bytes) {
     return ByteBuffer.wrap(bytes).getInt();
}
// packing an array of 4 bytes to an int, big endian, minimal parentheses
// operator precedence: <<, &, | 
// when operators of equal precedence (here bitwise OR) appear in the same expression, they are evaluated from left to right
int fromByteArray(byte[] bytes) {
     return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
}

// packing an array of 4 bytes to an int, big endian, clean code
int fromByteArray(byte[] bytes) {
     return ((bytes[0] & 0xFF) << 24) | 
            ((bytes[1] & 0xFF) << 16) | 
            ((bytes[2] & 0xFF) << 8 ) | 
            ((bytes[3] & 0xFF) << 0 );
}

Podczas pakowania bajtów ze znakiem w int, każdy bajt musi być zamaskowany, ponieważ jest rozszerzany do 32 bitów (a nie rozszerzany o zero) ze względu na arytmetyczną regułę promocji (opisaną w JLS, Conversions and Promotions).

Jest z tym związana interesująca łamigłówka opisana w Java Puzzlers („A Big Delight in Every Byte”) autorstwa Joshua Blocha i Neala Gaftera. Podczas porównywania wartości bajtu z wartością int, bajt jest rozszerzany ze znakiem do int, a następnie ta wartość jest porównywana z drugą wartością int

byte[] bytes = (…)
if (bytes[0] == 0xFF) {
   // dead code, bytes[0] is in the range [-128,127] and thus never equal to 255
}

Należy zauważyć, że wszystkie typy liczbowe są podpisane w języku Java, z wyjątkiem typu char będącego 16-bitową liczbą całkowitą bez znaku.


Myślę, że te & 0xFFsą niepotrzebne.
— Ori Popowski,

11
@LeifEricson Uważam, że & 0xFFs są konieczne, ponieważ nakazuje JVM konwersję bajtu ze znakiem na liczbę całkowitą z ustawionymi tylko tymi bitami. W przeciwnym razie bajt -1 (0xFF) zamieni się w int -1 (0xFFFFFFFF). Mogę się mylić, a nawet jeśli jestem, to nie boli i wyjaśnia sprawy.
— coderforlife

4
& 0xFF jest rzeczywiście obowiązkowe. byte b = 0; b |= 0x88; System.out.println(Integer.toString(b, 16)); //Output: -78 System.out.println(Integer.toString(b & 0xFF, 16)); //Output: 88
— HBN

1
@ptntialunrlsd Właściwie nie. Przed wykonaniem & operacji na byte0xFF ( int), JVM rzuci byteto intz rozszerzeniem na 1 lub rozszerzeniem 0 zgodnie z początkowym bitem. W Javie nie ma bajtu bez znaku , bytesą one zawsze podpisane.
— Nier

2
Kiedy parse int z tablicy bajtów, należy zwrócić uwagę na wielkość tablicy bajtów, jeśli jest większy niż 4 bajty, zgodnie z DOC ByteBuffer.getInt(): Reads the next four bytes at this buffer's current positiontylko pierwsze 4 bajty będą przetwarzane, która nie powinna być, co chcesz.
— Bin

56

Możesz również użyć BigInteger dla bajtów o zmiennej długości. Możesz przekształcić go w długi, int lub krótki, w zależności od potrzeb.

new BigInteger(bytes).intValue();

lub do oznaczenia polaryzacji:

new BigInteger(1, bytes).intValue();

Aby odzyskać bajty, wystarczy:

new BigInteger(bytes).toByteArray()

1
Zauważ, że od 1.8, to intValueExactnieintValue
— Abhijit Sarkar

5

Podstawowa implementacja wyglądałaby tak:

public class Test {
    public static void main(String[] args) {
        int[] input = new int[] { 0x1234, 0x5678, 0x9abc };
        byte[] output = new byte[input.length * 2];

        for (int i = 0, j = 0; i < input.length; i++, j+=2) {
            output[j] = (byte)(input[i] & 0xff);
            output[j+1] = (byte)((input[i] >> 8) & 0xff);
        }

        for (int i = 0; i < output.length; i++)
            System.out.format("%02x\n",output[i]);
    }
}

Aby zrozumieć rzeczy, możesz przeczytać ten artykuł WP: http://en.wikipedia.org/wiki/Endianness

Zostanie wyświetlony powyższy kod źródłowy 34 12 78 56 bc 9a. Pierwsze 2 bajty ( 34 12) reprezentują pierwszą liczbę całkowitą itd. Powyższy kod źródłowy koduje liczby całkowite w formacie little endian.


2
/** length should be less than 4 (for int) **/
public long byteToInt(byte[] bytes, int length) {
        int val = 0;
        if(length>4) throw new RuntimeException("Too big to fit in int");
        for (int i = 0; i < length; i++) {
            val=val<<8;
            val=val|(bytes[i] & 0xFF);
        }
        return val;
    }

0

Ktoś z wymaganiem, w którym muszą czytać z bitów, powiedzmy, że musisz czytać tylko z 3 bitów, ale potrzebujesz liczby całkowitej ze znakiem, a następnie użyj następującego:

data is of type: java.util.BitSet

new BigInteger(data.toByteArray).intValue() << 32 - 3 >> 32 - 3

Magiczną liczbę 3można zastąpić liczbą używanych bitów (nie bajtów) .


0

Jak często, guawa ma to, czego potrzebujesz.

Aby przejść od tablicy bajtów do int Ints.fromBytesArray:, doc tutaj

Aby przejść od int do tablicy bajtów:, Ints.toByteArraydoc tutaj


-7

Myślę, że to najlepszy tryb do przesyłania na int

   public int ByteToint(Byte B){
        String comb;
        int out=0;
        comb=B+"";
        salida= Integer.parseInt(comb);
        out=out+128;
        return out;
    }

najpierw zamień bajt na String

comb=B+"";

następnym krokiem jest zamiana na int

out= Integer.parseInt(comb);

ale bajt jest w wściekłości od -128 do 127 z tego powodu, myślę, że lepiej jest użyć wściekłości od 0 do 255 i wystarczy zrobić to:

out=out+256;

To jest źle. Rozważmy bajt 0x01. Twoja metoda zwróci 129, co jest błędne. 0x01 powinno wypisać liczbę całkowitą 1. Należy dodać 128 tylko wtedy, gdy liczba całkowita otrzymana z parseInt jest ujemna.
— disklosr

Chodziło mi o to, że powinieneś dodać 256, a nie 128. Nie mogłem tego później edytować.
— disklosr

zmieniony post, aby dodać 256, ponieważ może to być przydatne dla innych!
— apmartin1991

To powoduje dużo rzutowania i tworzy nowe obiekty (pomyśl o zrobieniu tego na pętlach for), które mogą obniżyć wydajność, sprawdź metodę Integer.toString (), aby uzyskać wskazówki dotyczące parsowania liczb.
— Marcos Vasconcelos

również, gdy publikujesz kod na stackoverflow, chodzi o to, aby kod pocztowy miał sens. Kod, który ma łatwy sens, musi mieć zrozumiałe identyfikatory. A w przypadku stackoverflow, zrozumiałe koniecznie oznacza po angielsku .
— Mike Nakis
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.