Czy możemy stworzyć bajt bez znaku w Javie

185

Próbuję przekonwertować podpisany bajt na niepodpisany. Problem polega na tym, że dane, które otrzymuję, są niepodpisane, a Java nie obsługuje bajtu bez znaku, więc kiedy odczytuje dane, traktuje je jako podpisane.

Próbowałem go przekonwertować za pomocą następującego rozwiązania, które otrzymałem z Stack Overflow.

public static int unsignedToBytes(byte a)
{
    int b = a & 0xFF;
    return b;
}

Ale kiedy ponownie jest konwertowany na bajty, otrzymuję te same podpisane dane. Próbuję użyć tych danych jako parametru funkcji Java, która akceptuje tylko bajt jako parametr, więc nie mogę użyć żadnego innego typu danych. Jak mogę rozwiązać ten problem?

dln
źródło
2
Guava: UnsignedBytes.toint (wartość bajtów)
jacktrades
20
java.lang.Byte.toUnsignedInt (wartość bajtu);
themarketka

Odpowiedzi:

107

Nie jestem pewien, czy rozumiem twoje pytanie.

Właśnie próbowałem tego i dla bajtu -12 (wartość ze znakiem) zwrócił liczbę całkowitą 244 (równoważną wartości bajtu bez znaku, ale wpisaną jako int):

  public static int unsignedToBytes(byte b) {
    return b & 0xFF;
  }

  public static void main(String[] args) {
    System.out.println(unsignedToBytes((byte) -12));
  }

Czy to jest to co chcesz robić?

Java nie pozwala wyrazić 244 jako bytewartość, jako że C. Aby wyrazić całkowite dodatnie powyżej Byte.MAX_VALUE(127) trzeba użyć innego rodzaju całkowitą, jak short, intlub long.

Guillaume
źródło
1
byte b = (byte)unsignedToBytes((byte) -12); teraz spróbuj wydrukować b
Jigar Joshi
101
Dlaczego zaakceptowałeś to jako prawidłową odpowiedź? Wszystko, co robi, jest dokładnie takie samo jak metoda, o której wspomniałeś w swoim pytaniu - zamień bajt na liczbę całkowitą bez znaku.
Adamski
1
Ważne jest, aby czasami podpisywać wartości, a czasem nie podpisywać, więc prawdopodobnie dlatego zaakceptował tę odpowiedź. (bajt) (b & 0xff) nie ma żadnego sensu, ale (bajt) (Math.min ((b & 0xff) * 2, 255)) ma sens, np. w grafice komputerowej spowoduje, że piksele będą reprezentowane przez bajt dwa razy jaśniejszy. :-)
iirekm
3
Można go również nazwać byteToUnsigned
Hernán Eche
195

Fakt, że prymitywy są podpisywane w Javie, nie ma znaczenia dla ich reprezentacji w pamięci / tranzycie - bajt ma zaledwie 8 bitów i to, czy interpretujesz go jako zakres podpisany, czy nie, zależy od ciebie. Nie ma magicznej flagi, która mówi „to jest podpisane” lub „to jest niepodpisane”.

Po podpisaniu prymitywów kompilator Java uniemożliwia przypisanie bajtu wartości wyższej niż +127 (lub mniejszej niż -128). Jednak nic nie powstrzyma cię przed spuszczeniem int (lub krótkiego) w celu osiągnięcia tego:

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)
byte b = (byte) 200; // 1100 1000 (-56 by Java specification, 200 by convention)

/*
 * Will print a negative int -56 because upcasting byte to int does
 * so called "sign extension" which yields those bits:
 * 1111 1111 1111 1111 1111 1111 1100 1000 (-56)
 *
 * But you could still choose to interpret this as +200.
 */
System.out.println(b); // "-56"

/*
 * Will print a positive int 200 because bitwise AND with 0xFF will
 * zero all the 24 most significant bits that:
 * a) were added during upcasting to int which took place silently
 *    just before evaluating the bitwise AND operator.
 *    So the `b & 0xFF` is equivalent with `((int) b) & 0xFF`.
 * b) were set to 1s because of "sign extension" during the upcasting
 *
 * 1111 1111 1111 1111 1111 1111 1100 1000 (the int)
 * &
 * 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)
 * =======================================
 * 0000 0000 0000 0000 0000 0000 1100 1000 (200)
 */
System.out.println(b & 0xFF); // "200"

/*
 * You would typically do this *within* the method that expected an 
 * unsigned byte and the advantage is you apply `0xFF` only once
 * and than you use the `unsignedByte` variable in all your bitwise
 * operations.
 *
 * You could use any integer type longer than `byte` for the `unsignedByte` variable,
 * i.e. `short`, `int`, `long` and even `char`, but during bitwise operations
 * it would get casted to `int` anyway.
 */
void printUnsignedByte(byte b) {
    int unsignedByte = b & 0xFF;
    System.out.println(unsignedByte); // "200"
}
Adamski
źródło
5
W przypadku wielu operacji nie robi to różnicy, jednak w przypadku niektórych operacji tak. Tak czy inaczej, możesz użyć bajtu jako niepodpisanego lub użyć znaku, który jest niepodpisany.
Peter Lawrey,
62
Dostęp do tablicy z potencjalnie ujemną liczbą nie jest nieistotny.
Stefan
3
@Stefan - miałem na myśli nieistotne w kontekście ich reprezentacji w sieci.
Adamski
6
Co jest nieco nieistotne dla pytania. Ponieważ wspomniał, że musi przekazać ją do funkcji, która akceptuje tylko parametry bajtów, nie ma znaczenia pogoda, dlatego interpretujemy ją jako bajtową reprezentację jednorożca. Java zawsze będzie traktować go jako liczbę podpisaną, co może być problematyczne na przykład, gdy ta funkcja używa parametru jako indeksu. Jednak, aby być uczciwym, głosowałem również za innymi 2 odpowiedziami, ponieważ one również nie odpowiadają na pytanie.
Stefan
2
@Stefan +1 dla Ciebie. Absolutnie istotne, jeśli używasz bajtu, aby uzyskać dostęp do tablicy 256 elementów. To doskonały przykład, aby pokazać, dlaczego wszyscy powinni zacząć uczyć się C i C ++ przed przejściem na Javę lub C #
Gianluca Ghettini
46

Kompletny przewodnik dotyczący pracy z niepodpisanymi bajtami w Javie:

Niepodpisany bajt w Javie

(Źródło tej odpowiedzi.)


Język Java nie zapewnia unsignedsłowa kluczowego. A bytezgodnie ze specyfikacją języka reprezentuje wartość z zakresu od –128 do 127. Na przykład, jeśli byterzutuje się na intJavę, interpretuje pierwszy bit jako znak i używa rozszerzenia znaku .

To powiedziawszy, nic nie stoi na przeszkodzie, abyś zobaczył bytepo prostu 8 bitów i zinterpretował te bity jako wartość od 0 do 255. Pamiętaj tylko, że nic nie możesz zrobić, aby zmusić swoją interpretację do użycia metody innej osoby. Jeśli metoda przyjmuje wartość a byte, wówczas metoda ta przyjmuje wartość z zakresu od –128 do 127, chyba że wyraźnie zaznaczono inaczej.

Oto kilka przydatnych konwersji / manipulacji dla Twojej wygody:

Konwersje do / z int

// From int to unsigned byte
int i = 200;                    // some value between 0 and 255
byte b = (byte) i;              // 8 bits representing that value

// From unsigned byte to int
byte b = 123;                   // 8 bits representing a value between 0 and 255
int i = b & 0xFF;               // an int representing the same value

(Lub, jeśli korzystasz z Java 8+, użyj Byte.toUnsignedInt.)

Analiza / formatowanie

Najlepszym sposobem jest użycie powyższych konwersji:

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");

// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

Arytmetyka

Reprezentacja z 2 uzupełnieniami „po prostu działa” na dodawanie, odejmowanie i mnożenie:

// two unsigned bytes
byte b1 = (byte) 200;
byte b2 = (byte) 15;

byte sum  = (byte) (b1 + b2);  // 215
byte diff = (byte) (b1 - b2);  // 185
byte prod = (byte) (b2 * b2);  // 225

Podział wymaga ręcznej konwersji argumentów:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));
aioobe
źródło
1
„char” nie reprezentuje liczby.
wyloguj się
26
Krótko mówiąc: się mylisz .
aioobe
36

W Javie nie ma prymitywnych bajtów bez znaku. Zwykle używa się większego typu:

int anUnsignedByte = (int) aSignedByte & 0xff;
Peter Knego
źródło
Czy obsada jest niezbędna?
nich,
Może to być niejawna obsada, ale jest obsada w obu przypadkach. I ta obsada ma podpisane rozszerzenie. I to jest problem. Jeśli wykonasz jawną obsadę, możesz przynajmniej zobaczyć, że tak się dzieje.
foo,
4

Dodatkowa uwaga, jeśli chcesz ją wydrukować, możesz po prostu powiedzieć

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
Kyle Kinkade
źródło
6
dlaczego tak skomplikowany? println(b & 0xff)wystarczy
phuclv
0

Jeśli uważasz, że szukasz czegoś takiego.

public static char toUnsigned(byte b) {
    return (char) (b >= 0 ? b : 256 + b);
}
Tobias Johansson
źródło
0

Adamski udzielił najlepszej odpowiedzi, ale nie jest ona kompletna, więc przeczytaj jego odpowiedź, ponieważ wyjaśnia szczegóły, których nie jestem.

Jeśli masz funkcję systemową, która wymaga przekazania bajtu niepodpisanego, możesz przekazać bajt podpisany, ponieważ automatycznie potraktuje go jako bajt niepodpisany.

Więc jeśli funkcja systemowa wymaga czterech bajtów, na przykład 192 168 0 1 jako bajty niepodpisane, możesz przekazać -64 -88 0 1, a funkcja będzie nadal działać, ponieważ przekazanie ich do funkcji spowoduje ich cofnięcie podpisu .

Jest jednak mało prawdopodobne, aby ten problem występował, ponieważ funkcje systemowe są ukryte za klasami w celu zapewnienia zgodności między platformami, chociaż niektóre metody odczytu java.io zwracają nieobsługiwane bajty jako int.

Jeśli chcesz, aby to działało, spróbuj zapisać podpisane bajty w pliku i odczytać je jako bajty niepodpisane.

Ewe Loon
źródło
1
Nie ma czegoś takiego jak bajty podpisane lub niepodpisane.
Vlastimil Ovčáčík
Jak dokładnie pisałeś i czytałeś bajty w swoim przykładzie?
Vlastimil Ovčáčík
0

Możesz także:

public static int unsignedToBytes(byte a)
{
    return (int) ( ( a << 24) >>> 24);
}    

Wyjaśnienie:

powiedzmy a = (byte) 133;

W pamięci jest przechowywany jako: „1000 0101” (0x85 w systemie szesnastkowym)

Tak więc jego reprezentacja tłumaczy unsigned = 133, podpisany = -123 (jako uzupełnienie 2)

<< 24

Po wykonaniu przesunięcia w lewo o 24 bity w lewo, wynikiem jest teraz 4-bajtowa liczba całkowita reprezentowana przez:

„10000101 00000000 00000000 00000000” (lub „0x85000000” w systemie szesnastkowym)

Następnie mamy

(a << 24) >>> 24

i przesuwa się ponownie na prawych 24 bitach, ale wypełnia się wiodącymi zerami. Wynika z tego, że:

„00000000 00000000 00000000 10000101” (lub „0x00000085” w systemie szesnastkowym)

i to jest niepodpisana reprezentacja równa 133.

Jeśli spróbujesz rzucić, a = (int) a; to co by się stało, zachowa reprezentację bajtu z dopełnieniem 2 i zapisze go jako int również jako dopełnienie 2:

(int) „10000101” ---> „11111111 11111111 11111111 10000101”

A to tłumaczy się jako: -123

mark_infinite
źródło
2
W 2019 r. Nie jest to konieczne. Po prostu użyj java.lang.Byte.toUnsignedInt(byte value). A jeśli nie używasz jeszcze Java 8, zaktualizuj jak najszybciej. Java 7 i wcześniejsze wersje są już wycofane.
Stephen C
0

Próbuję użyć tych danych jako parametru funkcji Java, która przyjmuje jako parametr tylko bajt

Nie różni się to zasadniczo od funkcji akceptującej liczbę całkowitą, do której chcesz przekazać wartość większą niż 2 ^ 32-1.

To brzmi jak zależy od tego, jak funkcja jest zdefiniowana i udokumentowana; Widzę trzy możliwości:

  1. Może jawnie udokumentować, że funkcja traktuje bajt jako wartość bez znaku, w którym to przypadku funkcja prawdopodobnie powinna zrobić to, czego się spodziewasz, ale wydaje się, że została źle zaimplementowana. W przypadku liczby całkowitej funkcja prawdopodobnie zadeklaruje parametr jako liczbę całkowitą bez znaku, ale nie jest to możliwe w przypadku bajtu.

  2. Może udokumentować, że wartość tego argumentu musi być większa niż (lub być może równa) zero, w którym to przypadku niewłaściwie używasz funkcji (przekazując parametr spoza zakresu), oczekując, że zrobi więcej, niż to zostało zaprojektowane robić. Przy pewnym poziomie obsługi debugowania można oczekiwać, że funkcja wyrzuci wyjątek lub nie powiedzie się asercja.

  3. Dokumentacja może nic nie mówić, w takim przypadku parametr ujemny jest, no cóż, parametrem ujemnym, a to, czy ma to jakiekolwiek znaczenie, zależy od tego, co robi funkcja. Jeśli to nie ma znaczenia, być może funkcja powinna być naprawdę zdefiniowana / udokumentowana jako (2). Jeśli ma to znaczenie w sposób nieoczywisty (np. Wartości nieujemne są używane do indeksowania do tablicy, a wartości ujemne są używane do indeksowania z powrotem od końca tablicy, więc -1 oznacza ostatni element), dokumentacja powinna powiedzieć, co to znaczy oznacza i spodziewałbym się, że i tak nie chcesz tego robić.

Kevin Martin
źródło
Hmmm, myślę, że właśnie opublikowałem odpowiedź, która była przeznaczona na inne pytanie dotyczące podpisu bajtów, ale przypuszczam, że nadal jest to trochę istotne tutaj ...
Kevin Martin
-1

Jeśli masz funkcję, której należy przekazać bajt podpisany, czego oczekujesz, jeśli przekażesz bajt niepodpisany?

Dlaczego nie możesz użyć innego typu danych?

Zwykle można użyć bajtu jako bajtu niepodpisanego z prostymi tłumaczeniami lub bez tłumaczenia. Wszystko zależy od tego, jak jest używany. Musisz wyjaśnić, co zamierzasz z tym zrobić.

Peter Lawrey
źródło
-1

Chociaż może wydawać się denerwujące (pochodzące z C), że Java nie zawierała bajtu niepodpisanego w języku, to naprawdę nie jest wielka sprawa, ponieważ prosta operacja „b & 0xFF” daje niepodpisaną wartość (podpisanego) bajtu b w (rzadkim) sytuacje, w których jest to rzeczywiście potrzebne. Bity tak naprawdę się nie zmieniają - tylko interpretacja (co jest ważne tylko podczas wykonywania na przykład niektórych operacji matematycznych na wartościach).

Pion
źródło
wyglądają na odpowiedzi innych, uważasz, że Twoja odpowiedź jest najlepsza / pomocna? opisz trochę i dodaj go w komentarzach
Jubin Patel
8
To nie jest rzadkie tylko dlatego, że się nie natknąłeś. Spróbuj wdrożyć protokół, a natkniesz się na to milion razy. Irytujące jest to, że zdecydowana większość przypadków użycia, z którymi się spotkałem, dotyczy bajtów, a ty chcesz zajmować się bajtami niepodpisanymi (ponieważ są to bajty, a nie liczby). Szaloną rzeczą jest to, że DOWOLNA operacja bitowa przekształci ją na liczbę całkowitą, co oznacza, że ​​wszelkie „ujemne” wartości będą całkowicie różne po rozszerzeniu. Tak, można to obejść, zawsze maskując, ale jest to strata czasu, procesor i powoduje naprawdę niejasne błędy, jeśli zapomnisz.
Thor84no
Zgadzam się z Thor84no: bajty nie są liczbami i nie powinny mieć znaku. Z drugiej strony, ponieważ nie są to liczby, nie powinniśmy nawet mieć / używać operatorów + i -. Używanie tylko operatorów bitowych działa dobrze, z drugiej strony operatory shift nie działają tak, jak byśmy tego chcieli, i rzeczywiście java promuje przesunięty bajt na liczbę całkowitą.
user1708042
1
@ VlastimilOvčáčík To dosłownie niemożliwe w tym przypadku, to jest poruszające. JESZCZE powtarzacie x & 0xFFwszędzie, gdzie jest to potrzebne, lub powtarzacie coś takiego jak behaveLikeAnUnsignedByte(x)wszędzie. Jest to potrzebne dla każdego miejsca, w którym używasz wartości bajtu lub tablicy bajtów, które muszą być niepodpisane, nie ma możliwego sposobu na uniknięcie tego powtórzenia. Nie można napisać implementacji protokołu, który odczytuje i zapisuje wartości bajtów za pomocą tylko jednego odwołania do zmiennej bajtowej. Twój uproszczony pogląd może wyjaśniać, dlaczego nigdy nie chcieli go naprawić.
Thor84no
-1

W Javie nie ma bajtu bez znaku, ale jeśli chcesz wyświetlić bajt, możesz:

int myInt = 144;

byte myByte = (byte) myInt;

char myChar = (char) (myByte & 0xFF);

System.out.println("myChar :" + Integer.toHexString(myChar));

Wynik:

myChar : 90

Aby uzyskać więcej informacji, sprawdź, jak wyświetlić wartość szesnastkową / bajtową w Javie .

Jyo the Whiff
źródło
Nie ma potrzeby definiowania tego samemu. java.lang.Byte.toUnsignedInt(byte value);istnieje po to.
Alexander - Przywróć Monikę
-2

Zgodnie z ograniczeniami w Javie bajt bez znaku jest prawie niemożliwy w obecnym formacie typu danych. Możesz wybrać inne biblioteki innego języka dla tego, co wdrażasz, a następnie możesz do nich zadzwonić za pomocą JNI .

Pritesh Jain
źródło
Nie sądzę, żeby chciał go zapisać jako bajt podpisany. Otrzymuje go jako bajt podpisany i chce go zapisać jako int, co jest całkowicie poprawne. Jego problemem jest to, że gdziekolwiek pobiera dane wejściowe, reprezentuje wartość od 0 do 255 jako bajt, ale Java interpretuje to jako dwójkę dopełniającą podpisaną wartość, ponieważ Java nie obsługuje podpisanych bajtów.
Zac
-2

Tak i nie. Przekopałem się z tym problemem. Jak rozumiem to:

Faktem jest, że Java podpisała interger -128 do 127. Możliwe jest przedstawienie niepodpisanego w java z:

public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;
}

Jeśli na przykład dodasz numer podpisany -12, aby się nie podpisać, otrzymasz 244. Ale możesz użyć tego numeru ponownie w podpisanym, musisz go zmienić z powrotem na podpisany i będzie to ponownie -12.

Jeśli spróbujesz dodać 244 do bajtu java, otrzymasz outOfIndexException.

Twoje zdrowie..

Sindri Þór
źródło
3
Nie ma potrzeby definiowania tego samemu. java.lang.Byte.toUnsignedInt(byte value);istnieje po to.
Alexander - Przywróć Monikę
-3

Jeśli chcesz napisać bajty bez znaku w Javie, po prostu odejmij 256 od liczby, którą jesteś zainteresowany. Spowoduje to uzupełnienie dwóch o wartość ujemną, czyli pożądaną liczbę w bajtach bez znaku.

Przykład:

int speed = 255; //Integer with the desired byte value
byte speed_unsigned = (byte)(speed-256);
//This will be represented in two's complement so its binary value will be 1111 1111
//which is the unsigned byte we desire.

Musisz użyć takich brudnych hacków podczas korzystania z leJOS do programowania klocka NXT .

XapaJIaMnu
źródło
Zdajesz sobie sprawę, że wartość binarna 255 to również 1111 1111, więc odejmowanie nie jest konieczne, prawda?
Nick White
@NickWhite, tak w formacie binarnym. Ale java używa dopełnienia 2, gdzie 255 to nie 11111111
XapaJIaMnu
Przepraszam, ale to po prostu źle. Spróbuj eksperymentów. Wartość w speed_unsignedjest podpisana. Wydrukuj i zobacz. (I - 256tutaj nic nie osiąga.)
Stephen C