วิธีการแปลง Int เป็นไบต์ที่ไม่ได้ลงนามและย้อนกลับ


93

ฉันต้องการแปลงตัวเลขเป็นไบต์ที่ไม่ได้ลงชื่อ จำนวนจะน้อยกว่าหรือเท่ากับ 255 เสมอดังนั้นจึงจะพอดีกับหนึ่งไบต์

ฉันต้องแปลงไบต์นั้นกลับเป็นตัวเลขนั้นด้วย ฉันจะทำสิ่งนั้นใน Java ได้อย่างไร ฉันลองมาหลายวิธีแล้วไม่ได้ผล นี่คือสิ่งที่ฉันพยายามทำตอนนี้:

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

และตอนนี้เพื่อแปลงไบต์นั้นกลับเป็นตัวเลข:

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

เห็นได้ชัดว่าสิ่งนี้ไม่ได้ผล 65ด้วยเหตุผลบางอย่างก็มักจะแปลงจำนวนลงไป ข้อเสนอแนะใด ๆ ?


ฉันลองวิธีนี้ด้วย: crazysquirrel.com/computing/java/basics/… - ไม่ได้ผล
darksky

คำตอบ:


205

ไบต์จะถูกลงนามใน Java เสมอ คุณอาจได้รับค่าที่ไม่ได้ลงนามโดยไบนารีและด้วย 0xFF แม้ว่า:

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

ฉันยังอายุ 65 ... ทำไมถึงเป็นอย่างนั้น?
darksky

1
เป็นการจัดเก็บหมายเลขที่คุณต้องการเป็นไบต์ Java เพียงแค่พิจารณาว่าเป็นหมายเลขที่ลงนามแทนที่จะเป็นหมายเลขที่ไม่ได้ลงชื่อ แต่ทุกบิตจะเหมือนกับว่าเป็นหมายเลขที่ไม่ได้ลงชื่อ การแปลงสตริงของคุณเป็นไบต์เป็นอีกคำถามที่ไม่เกี่ยวข้อง โพสต์แยกต่างหากในคำถามใหม่
JB Nizet

1
ใช้ getInt เพื่อกลับ int ของคุณและแปลง int เป็นไบต์ int คือ int มาจากไหนไม่สำคัญ
JB Nizet

19
เหตุใด bitwise และ0xFFผลลัพธ์จึงเป็นจำนวนเต็มที่ไม่ได้ลงชื่อ คำอธิบายบางอย่างจะเป็นประโยชน์มาก
user462455

2
Java 8 ใช้วิธีการเดียวกัน: public static int toUnsignedInt (byte x) {return ((int) x) & 0xff; }
Daniel De León

57

Java 8 จัดเตรียมByte.toUnsignedIntการแปลงbyteเป็นintโดยการแปลงที่ไม่ได้ลงชื่อ ใน JDK ของ Oracle สิ่งนี้ถูกนำไปใช้เพียงreturn ((int) x) & 0xff;เพราะ HotSpot เข้าใจวิธีการปรับรูปแบบนี้ให้เหมาะสมแล้ว แต่อาจถูกกำหนดไว้ใน VMs อื่น ๆ ที่สำคัญไม่จำเป็นต้องมีความรู้มาก่อนเพื่อทำความเข้าใจว่าการเรียกร้องให้toUnsignedInt(foo)ทำอะไร

รวม Java 8 มีวิธีการแปลงbyteและshortจะได้รับการรับรองintและlongและจะได้รับการรับรองint longวิธีการแปลงbyteเป็นไม่ได้ลงนามshortถูกละไว้โดยเจตนาเนื่องจาก JVM ให้เฉพาะเลขคณิตเท่านั้นintและlongอย่างไรก็ตาม

การแปลงเป็น int (byte)someIntกลับไปไบต์เพียงใช้หล่อ: การแปลงแบบดั้งเดิมที่แคบลงซึ่งเป็นผลลัพธ์จะทิ้งทั้งหมดยกเว้น 8 บิตสุดท้าย


7

หากคุณต้องการเพียงแค่แปลงค่า 8 บิตที่คาดไว้จาก int ที่ลงชื่อเป็นค่าที่ไม่ได้ลงชื่อคุณสามารถใช้การเปลี่ยนบิตแบบง่าย:

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

หากใช้อย่างอื่นที่ไม่ใช่intประเภทพื้นฐานคุณจะต้องปรับจำนวนกะอย่างชัดเจน: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

นอกจากนี้โปรดทราบว่าคุณไม่สามารถใช้bytetype ได้การทำเช่นนั้นจะทำให้ได้ค่าที่มีลายเซ็นตามที่ผู้ตอบอื่นกล่าว ประเภทดั้งเดิมที่เล็กที่สุดที่คุณสามารถใช้เพื่อแสดงค่า 8 บิตที่ไม่ได้ลงนามจะเป็น a short.


3

แปลงโทรเข้ามาเป็นตัวแทนของถ่านจำนวนเต็มของคุณเช่นถ่านInteger.toString(size) '5'การแทนค่า ASCIIของอักขระนั้นคือค่า 65

คุณต้องแยกวิเคราะห์สตริงกลับเป็นค่าจำนวนเต็มก่อนเช่นโดยใช้Integer.parseIntเพื่อดึงกลับค่า int เดิม

สิ่งที่สำคัญที่สุดสำหรับการแปลงที่ลงชื่อ / ไม่ได้ลงชื่อคุณควรละทิ้งStringรูปภาพและใช้การจัดการบิตตามที่ @JB แนะนำ


อย่างนี้เหรอ? (ยังคงได้รับ 65 ที่นี่)String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
darksky

@Nayefc ฉันอัปเดตคำตอบของฉันเช่นเดียวกับที่คุณเขียนความคิดเห็นของคุณ :-)
PéterTörök

ได้เลยขอบคุณ! ด้วยเหตุผลบางอย่างมันยังคงพิมพ์ออกมา 65 แม้ว่า สิ่งนี้หมายความว่า? ถ้ามันแสดงแค่ 65 สิ่งที่ถูกเก็บไว้ในไบต์จริงๆฉันจะไม่เข้าใจ โปรดตรวจสอบคำถามของฉันฉันแก้ไขและเพิ่มหัวข้อเกี่ยวกับการแปลงสตริง :)
darksky

@Nayefc อีกครั้งString.getBytes()ให้ค่า ASCII ของอักขระที่มีอยู่ในข้อความไม่ใช่ค่าตัวเลขของตัวเลข คุณต้องแยกวิเคราะห์สตริงเป็นค่าตัวเลขตามที่ฉันได้บอกไว้ข้างต้น
PéterTörök

1
@Nayefc นั่นเป็นคำถามที่แตกต่างไปจากเดิมอย่างสิ้นเชิง และ AFAIS ควรใช้งานได้ สร้างคำถามใหม่รวมถึงอินพุตและเอาต์พุตจริงของการแปลงและโค้ดแบบเต็มเพื่อสร้างเคสซ้ำ
PéterTörök

3

โซลูชันนี้ใช้งานได้ดี (ขอบคุณ!) แต่ถ้าคุณต้องการหลีกเลี่ยงการแคสต์และปล่อยให้งานระดับต่ำไปที่ JDK คุณสามารถใช้ DataOutputStream เพื่อเขียน int และ DataInputStream เพื่ออ่านข้อมูลเหล่านั้นกลับมาได้โดยอัตโนมัติจะถือว่าไม่ได้ลงชื่อ ไบต์แล้ว:

สำหรับการแปลง int เป็นไบต์ไบนารี

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

อ่านย้อนกลับไปใน:

// 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. มีประโยชน์สำหรับการจัดการรูปแบบข้อมูลไบนารี (เช่นรูปแบบข้อความแบนเป็นต้น)


3

ยกเว้นcharทุกประเภทข้อมูลตัวเลขอื่น ๆ ใน Java จะถูกเซ็นชื่อ

ตามที่กล่าวในคำตอบที่ก่อนหน้านี้คุณสามารถได้รับค่าที่ได้รับการรับรองโดยการดำเนินการการดำเนินการกับand 0xFFในคำตอบนี้ฉันจะอธิบายว่ามันเกิดขึ้นได้อย่างไร

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

หากเครื่องของคุณเป็นแบบ 32 บิตintประเภทข้อมูลจะต้องมี 32 บิตในการจัดเก็บค่า byteต้องการเพียง 8 บิต

intตัวแปรiมีตัวแทนอยู่ในหน่วยความจำดังต่อไปนี้ (เป็นจำนวนเต็ม 32 บิต)

0{24}11101010

จากนั้นbyteตัวแปรbจะแสดงเป็น:

11101010

ในฐานะที่เป็นbytes -22จะไม่ได้ลงนามค่านี้แทน (ค้นหาส่วนเติมเต็มของ 2 เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการแทนจำนวนเต็มลบในหน่วยความจำ)

แล้วถ้าคุณหล่อintจะยังคงเป็น-22เพราะการหล่อรักษาสัญลักษณ์ของตัวเลข

1{24}11101010

32-bitมูลค่าcasted ของbการดำเนินandการกับ0xFF.

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

แล้วคุณจะได้234รับคำตอบ


1

แม้ว่ามันจะสายเกินไป แต่ฉันก็อยากจะให้ข้อมูลของฉันเกี่ยวกับเรื่องนี้เพราะอาจทำให้กระจ่างได้ว่าทำไมวิธีการแก้ปัญหาที่ JB Nizet ให้ไว้จึงใช้ได้ผล ฉันสะดุดกับปัญหาเล็กน้อยนี้ในการทำงานกับตัวแยกวิเคราะห์ไบต์และการแปลงสตริงด้วยตัวเอง เมื่อคุณคัดลอกจากประเภทอินทิกรัลที่มีขนาดใหญ่กว่าไปยังประเภทอินทิกรัลที่มีขนาดเล็กกว่าดังที่เอกสารจาวากล่าวว่าสิ่งนี้จะเกิดขึ้น:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 การแปลงจำนวนเต็มที่ลงนามให้แคบลงเป็นประเภทอินทิกรัล T เพียงแค่ละทิ้งทั้งหมด แต่ n ต่ำสุด ลำดับบิตโดยที่ n คือจำนวนบิตที่ใช้แทนชนิด T นอกเหนือจากการสูญเสียข้อมูลที่เป็นไปได้เกี่ยวกับขนาดของค่าตัวเลขแล้วสิ่งนี้อาจทำให้เครื่องหมายของค่าผลลัพธ์แตกต่างจากสัญลักษณ์ของค่าอินพุต .

คุณสามารถมั่นใจได้ว่าไบต์เป็นประเภทอินทิกรัลตามที่เอกสารจาวานี้ระบุว่า https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html ไบต์: ประเภทข้อมูลไบต์คือ 8 บิตที่เซ็นชื่อสอง เติมเต็มจำนวนเต็ม

ดังนั้นในกรณีของการส่งจำนวนเต็ม (32 บิต) เป็นไบต์ (8 บิต) คุณเพียงแค่คัดลอกส่วนสุดท้าย (8 บิตที่มีนัยสำคัญน้อยที่สุด) ของจำนวนเต็มนั้นไปยังตัวแปรไบต์ที่กำหนด

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

ส่วนที่สองของเรื่องราวเกี่ยวข้องกับวิธีการที่ตัวดำเนินการยูนารีและไบนารีของ Java ส่งเสริมตัวถูกดำเนินการ https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 การ ขยายการแปลงแบบดั้งเดิม (§5.1.2) จะใช้กับการแปลงตัวถูกดำเนินการอย่างใดอย่างหนึ่งหรือทั้งสองอย่างตามที่ระบุ ตามกฎต่อไปนี้:

ถ้าตัวถูกดำเนินการตัวใดตัวหนึ่งเป็นประเภท double อีกตัวจะถูกแปลงเป็น double

มิฉะนั้นถ้าตัวถูกดำเนินการอย่างใดอย่างหนึ่งเป็นประเภท float อีกตัวหนึ่งจะถูกแปลงเป็น float

มิฉะนั้นถ้าตัวถูกดำเนินการตัวใดตัวหนึ่งเป็นประเภท long อีกตัวถูกแปลงเป็น long

มิฉะนั้นตัวถูกดำเนินการทั้งสองจะถูกแปลงเป็นประเภท int

มั่นใจได้หากคุณกำลังทำงานกับอินทิกรัลประเภท int และ / หรือต่ำกว่าจะได้รับการเลื่อนระดับเป็น 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

ฉันเกาหัวของฉันด้วย :) มีคำตอบที่ดีสำหรับเรื่องนี้โดย rgettman ตัวดำเนินการ Bitwise ใน java สำหรับจำนวนเต็มและ long เท่านั้น?


0

หากคุณต้องการใช้คลาส Wrapper แบบดั้งเดิมสิ่งนี้จะใช้งานได้ แต่ java ทุกประเภทจะถูกเซ็นชื่อโดยค่าเริ่มต้น

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));
}

0

การจัดการไบต์และจำนวนเต็มที่ไม่ได้ลงนามด้วย 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

สิ่งนี้จะแปลงเป็นจำนวนเต็ม SIGNED ไม่ใช่ไม่ได้ลงชื่อ ...
Pedro

-1

ใน 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);
    }
}

ผลลัพธ์: 254

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.