Jak przekonwertować liczbę int na bajt bez znaku iz powrotem

93

Muszę przekonwertować liczbę na bajt bez znaku. Liczba jest zawsze mniejsza lub równa 255, więc zmieści się w jednym bajcie.

Muszę również przekonwertować ten bajt z powrotem na tę liczbę. Jak miałbym to zrobić w Javie? Wypróbowałem kilka sposobów i żaden nie działa. Oto, co próbuję teraz zrobić:

int size = 5;
// Convert size int to binary
String sizeStr = Integer.toString(size);
byte binaryByte = Byte.valueOf(sizeStr);

a teraz przekonwertować ten bajt z powrotem na liczbę:

Byte test = new Byte(binaryByte);
int msgSize = test.intValue();

Oczywiście to nie działa. Z jakiegoś powodu zawsze konwertuje liczbę na 65. Jakieś sugestie?

ciemne niebo
źródło
Próbowałem też tej metody: crazysquirrel.com/computing/java/basics/ ... - nie działa.
darksky,

Odpowiedzi:

204

Bajt jest zawsze podpisywany w Javie. Możesz jednak uzyskać jego wartość bez znaku, łącząc ją binarnie z 0xFF:

int i = 234;
byte b = (byte) i;
System.out.println(b); // -22
int i2 = b & 0xFF;
System.out.println(i2); // 234
JB Nizet
źródło
Ciągle kończę 65 ... Dlaczego tak jest?
darksky,
1
Przechowuje żądaną liczbę w bajcie. Java traktuje go po prostu jako liczbę ze znakiem, a nie jako liczbę bez znaku. Ale każdy bit jest taki sam, jakby był liczbą bez znaku. Twoja konwersja ciągu znaków na bajty to kolejne niepowiązane pytanie. Opublikuj to osobno, w nowym pytaniu.
JB Nizet
1
użyj getInt, aby odzyskać swój int, i przekształć int jako bajt. Int to int. Nie ma znaczenia, skąd pochodzi.
JB Nizet
19
Dlaczego bitowe i z 0xFFwynikiem w postaci liczby całkowitej bez znaku? Pewne wyjaśnienie byłoby naprawdę pomocne.
user462455
2
Java 8 używa tej samej metody: public static int toUnsignedInt (bajt x) {return ((int) x) & 0xff; }
Daniel De León
57

Java 8 zapewnia Byte.toUnsignedIntkonwersję bytena intbez znaku. W JDK Oracle jest to po prostu zaimplementowane, return ((int) x) & 0xff;ponieważ HotSpot już rozumie, jak zoptymalizować ten wzorzec, ale może być wbudowany w inne maszyny wirtualne. Co ważniejsze, nie jest potrzebna żadna wcześniejsza wiedza, aby zrozumieć, do czego służy wezwanie toUnsignedInt(foo).

W sumie, Java 8 dostarcza metod do konwersji bytei shortdo unsigned inti long, a intdo niepodpisany long. Metoda konwersji bytena bez znaku shortzostała celowo pominięta, ponieważ JVM zapewnia tylko działania arytmetyczne inti longtak.

Aby przekonwertować int powrotem do bajta, wystarczy użyć obsady: (byte)someInt. Wynikająca z tego zawężona konwersja pierwotna spowoduje odrzucenie wszystkich oprócz ostatnich 8 bitów.

Jeffrey Bosboom
źródło
7

Jeśli potrzebujesz tylko przekonwertować oczekiwaną 8-bitową wartość z wartości int ze znakiem na wartość bez znaku, możesz użyć prostego przesuwania bitów:

int signed = -119;  // 11111111 11111111 11111111 10001001

/**
 * Use unsigned right shift operator to drop unset bits in positions 8-31
 */
int psuedoUnsigned = (signed << 24) >>> 24;  // 00000000 00000000 00000000 10001001 -> 137 base 10

/** 
 * Convert back to signed by using the sign-extension properties of the right shift operator
 */
int backToSigned = (psuedoUnsigned << 24) >> 24; // back to original bit pattern

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Jeśli używasz czegoś innego niż inttyp podstawowy, musisz oczywiście dostosować wielkość przesunięcia: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Pamiętaj też, że nie możesz używać bytetypu, ponieważ spowoduje to podpisanie wartości, o której wspominali inni respondenci. Najmniejszym typem pierwotnym, którego można użyć do reprezentowania 8-bitowej wartości bez znaku, byłby a short.

dm78
źródło
3

W Integer.toString(size)nawróceni wezwanie do reprezentowania char swojej całkowitej, czyli char '5'. Reprezentacją ASCII tego znaku jest wartość 65.

Najpierw musisz przeanalizować ciąg z powrotem do wartości całkowitej, np. Używając Integer.parseInt , aby odzyskać oryginalną wartość int.

StringPodsumowując, w przypadku konwersji ze znakiem / bez znaku najlepiej jest pominąć obraz i użyć manipulacji bitami, jak sugeruje @JB.

Péter Török
źródło
Więc tak? (Nadal String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
mam
@Nayefc, zaktualizowałem moją odpowiedź, tak jak pisałeś swój komentarz :-)
Péter Török
W porządku, dzięki! Z jakiegoś powodu NADAL drukuje 65. Co to znaczy? Jeśli pokazuje tylko 65, co jest faktycznie przechowywane w bajcie, to nie rozumiem. Sprawdź również moje pytanie, zredagowałem je i dodałem sekcję o konwersji ciągów :)
darksky
@Nayefc ponownie String.getBytes()zwraca wartości ASCII znaków zawartych w tekście, a nie wartości liczbowe cyfr. Musisz przeanalizować ciąg na wartość liczbową, jak zasugerowałem powyżej.
Péter Török,
1
@Nayefc, więc to właściwie zupełnie inna kwestia. AFAIS to powinno działać. Utwórz nowe pytanie, w tym rzeczywiste dane wejściowe i wyjściowe konwersji oraz pełny kod do odtworzenia przypadku.
Péter Török,
3

Rozwiązanie działa dobrze (dzięki!), Ale jeśli chcesz uniknąć rzutowania i pozostawić pracę niskiego poziomu JDK, możesz użyć DataOutputStream, aby zapisać swoje int i DataInputStream, aby je odczytać. Są one automatycznie traktowane jako niepodpisane bajtów to:

Do konwersji int na bajty binarne;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int val = 250;
dos.write(byteVal);
...
dos.flush();

Czytanie ich z powrotem w:

// important to use a (non-Unicode!) encoding like US_ASCII or ISO-8859-1,
// i.e., one that uses one byte per character
ByteArrayInputStream bis = new ByteArrayInputStream(
   bos.toString("ISO-8859-1").getBytes("ISO-8859-1"));
DataInputStream dis = new DataInputStream(bis);
int byteVal = dis.readUnsignedByte();

Esp. przydatne do obsługi binarnych formatów danych (np. płaskie formaty wiadomości itp.)

Gregor
źródło
3

Z wyjątkiem charwszystkich innych liczbowych typów danych w Javie są podpisane.

Jak wspomniano w poprzedniej odpowiedzi, wartość bez znaku można uzyskać, wykonując andoperację z 0xFF. W tej odpowiedzi wyjaśnię, jak to się dzieje.

int i = 234;
byte b = (byte) i;
System.out.println(b);  // -22

int i2 = b & 0xFF;      
// This is like casting b to int and perform and operation with 0xFF

System.out.println(i2); // 234

Jeśli twój komputer jest 32-bitowy, inttyp danych wymaga 32-bitów do przechowywania wartości. bytepotrzebuje tylko 8-bitów.

intZmienna ijest przedstawiony w pamięci w sposób następujący (w postaci 32-bitowej liczby całkowitej).

0{24}11101010

Następnie bytezmienna bjest reprezentowana jako:

11101010

Ponieważ bytes są bez znaku, ta wartość reprezentuje-22 . (Wyszukaj uzupełnienie do 2, aby dowiedzieć się więcej o tym, jak reprezentować ujemne liczby całkowite w pamięci)

Wtedy, jeśli rzucisz, intto nadal będzie, -22ponieważ rzucanie zachowuje znak liczby.

1{24}11101010

Rzutowana 32-bitwartość bwykonania andoperacji z 0xFF.

 1{24}11101010 & 0{24}11111111
=0{24}11101010

Wtedy otrzymasz 234odpowiedź.

Ramesh-X
źródło
1

Chociaż jest już za późno, chciałbym wnieść swój wkład w tę sprawę, ponieważ może to wyjaśnić, dlaczego rozwiązanie podane przez JB Nizet działa. Natknąłem się na ten mały problem podczas pracy z parserem bajtów i samodzielną konwersją ciągów. Podczas kopiowania z typu całkowitego o większym rozmiarze do typu całkowitego o mniejszym rozmiarze, zgodnie z tym, co mówi ten dokument java:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 Zwężająca się konwersja liczby całkowitej ze znakiem na typ całkowy T po prostu odrzuca wszystko oprócz n najniższego bitów porządkowych, gdzie n to liczba bitów użytych do reprezentacji typu T. Oprócz możliwej utraty informacji o wielkości wartości liczbowej może to spowodować, że znak wartości wynikowej będzie różny od znaku wartości wejściowej .

Możesz być pewien, że bajt jest typem całkowitym, zgodnie z tym dokumentem java https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html bajt: Typ danych bajtowych to 8-bitowe podpisane dwa uzupełnienie liczby całkowitej.

Tak więc w przypadku rzutowania liczby całkowitej (32 bity) na bajt (8 bitów) wystarczy skopiować ostatnie (najmniej znaczące 8 bitów) tej liczby całkowitej do podanej zmiennej bajtowej.

int a = 128;
byte b = (byte)a; // Last 8 bits gets copied
System.out.println(b);  // -128

Druga część historii dotyczy sposobu, w jaki operandy jednoargumentowe i binarne Java promują operandy. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 Konwersja prymitywów poszerzających (§5.1.2) jest stosowana do konwersji jednego lub obu operandów zgodnie z opisem według następujących zasad:

Jeśli jeden z operandów jest typu double, drugi jest konwertowany na double.

W przeciwnym razie, jeśli jeden z operandów jest typu float, drugi jest konwertowany na float.

W przeciwnym razie, jeśli jeden z operandów jest typu long, drugi jest konwertowany na long.

W przeciwnym razie oba operandy są konwertowane na typ int.

Zapewniamy, że jeśli pracujesz z typem całkowitym int i / lub niższym, zostanie on podwyższony do typu int.

// byte b(0x80) gets promoted to int (0xFF80) by the & operator and then
// 0xFF80 & 0xFF (0xFF translates to 0x00FF) bitwise operation yields 
// 0x0080
a = b & 0xFF;
System.out.println(a); // 128

Ja też podrapałem się po głowie :). Rgettman ma na to dobrą odpowiedź. Operatory bitowe w Javie tylko dla liczb całkowitych i długich?

KaziQ
źródło
0

Jeśli chcesz użyć prymitywnych klas opakowania, zadziała to, ale wszystkie typy java są domyślnie podpisane.

public static void main(String[] args) {
    Integer i=5;
    Byte b = Byte.valueOf(i+""); //converts i to String and calls Byte.valueOf()
    System.out.println(b);
    System.out.println(Integer.valueOf(b));
}
Romeo
źródło
0

Obsługa bajtów i liczb całkowitych bez znaku z BigInteger:

byte[] b = ...                    // your integer in big-endian
BigInteger ui = new BigInteger(b) // let BigInteger do the work
int i = ui.intValue()             // unsigned value assigned to i
pgrandjean
źródło
To konwertuje na liczbę całkowitą SIGNED, bez znaku ...
Pedro
-1

w java 7

public class Main {
    public static void main(String[] args) {
        byte b =  -2;
        int i = 0 ;
        i = ( b & 0b1111_1111 ) ;
        System.err.println(i);
    }
}

wynik: 254

Tristan De Smet
źródło