แปลงอาร์เรย์ไบต์เป็นจำนวนเต็มใน Java และในทางกลับกัน


139

ฉันต้องการเก็บข้อมูลลงในอาร์เรย์ไบต์ใน Java โดยทั่วไปเพียงแค่ตัวเลขซึ่งอาจใช้เวลาสูงสุด 2 ไบต์ต่อตัวเลข

ฉันต้องการทราบว่าฉันสามารถแปลงจำนวนเต็มเป็นอาร์เรย์ไบต์ยาว 2 ไบต์และในทางกลับกัน ฉันพบวิธีแก้ปัญหาจำนวนมาก googling แต่ส่วนใหญ่ไม่ได้อธิบายว่าเกิดอะไรขึ้นในโค้ด มีสิ่งที่เปลี่ยนแปลงอย่างมากที่ฉันไม่เข้าใจจริงๆดังนั้นฉันขอขอบคุณคำอธิบายพื้นฐาน


4
เท่าไหร่ไม่ว่าคุณเข้าใจเกี่ยวกับการขยับ bit? ดูเหมือนว่าคำถามคือ "การเปลี่ยนบิตทำอะไร" มากกว่าเกี่ยวกับการแปลงเป็นอาร์เรย์ไบต์จริง ๆ - ถ้าคุณต้องการเข้าใจว่าการแปลงทำงานอย่างไร
— Jon Skeet

1
(เพียงเพื่อชี้แจงฉันดีกับคำถามทั้งสอง แต่มันก็คุ้มค่าที่จะทำให้ชัดเจนว่าคำถามที่คุณต้องการคำตอบจริง ๆ แล้วคุณน่าจะได้รับคำตอบที่เป็นประโยชน์กับคุณมากกว่าวิธีนั้น)
— Jon Skeet

โอเคฉันเข้าใจแล้ว! ขอบคุณสำหรับคำพูด ฉันรู้ว่าการขยับบิตจะไม่เข้าใจว่ามันใช้สำหรับการแปลงอาร์เรย์ไบต์หรือไม่
— Chris

3
@prekageo และ Jeff Mercado ขอบคุณสำหรับคำตอบสองข้อ prekageo ให้คำอธิบายที่ดีเกี่ยวกับวิธีการเชื่อมโยงที่ดี! นั่นทำให้ฉันชัดเจนยิ่งขึ้น และทางออกของ Jeff Mercados ก็แก้ปัญหาที่ฉันมี
— Chris

คำตอบ:


230

ใช้การเรียนที่พบในjava.nionamespace ByteBufferโดยเฉพาะอย่างยิ่งที่ มันสามารถทำงานทั้งหมดให้คุณได้

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
มันแพงเกินไปหรือไม่ถ้าอาร์เรย์ไบต์มีจำนวนเต็มเพียง 1 หรือ 2 ตัว? ByteBufferไม่แน่ใจเกี่ยวกับค่าใช้จ่ายในการก่อสร้าง
— Meow Cat 2012

คุณทำงานกับข้อมูลไบนารีใน 2-4 ไบต์บ่อยแค่ไหน? จริงๆ? การใช้งานที่มีสติจะทำงานร่วมกับมันใน BUFSIZ ชิ้น (ปกติ 4kb) หรือใช้ห้องสมุด IO อื่น ๆ ที่ซ่อนรายละเอียดนี้ มีไลบรารีทั้งหมดภายในกรอบงานที่อุทิศเพื่อช่วยคุณทำงานกับบัฟเฟอร์ของข้อมูล คุณสร้างความเสียหายให้กับตัวคุณเองและผู้ดูแลรหัสอื่น ๆ ของคุณเมื่อคุณใช้งานทั่วไปโดยไม่มีเหตุผลที่ดี บัฟเฟอร์เหล่านี้เป็นเพียงตัวห่อหุ้มที่ทำงานกับอาร์เรย์ไม่มีอะไรเพิ่มเติม
— Jeff Mercado

ทำไมคุณถึงสร้างคลาสนามธรรมได้?

1
@JaveneCPPMcGowan ไม่มีการสร้างอินสแตนซ์โดยตรงในคำตอบนี้ ถ้าคุณหมายถึงวิธีการโรงงานwrapและพวกเขาไม่ได้กลับไปอินสแตนซ์ของคลาสนามธรรมallocate ByteBuffer
— Marko Topolnik

ไม่ใช่วิธีแก้ปัญหาสำหรับ 3 ไบต์ เราจะได้รับChar, ,Short Intฉันคิดว่าฉันสามารถปัดข้อมูลได้ถึง 4 ไบต์และละทิ้งอันดับที่ 4 ในแต่ละครั้ง
— จอห์น

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

เมื่อทำการบรรจุไบต์ที่เซ็นชื่อลงใน int แต่ละไบต์จะต้องถูกปิดบังเนื่องจากมีการขยายสัญญาณเป็น 32 บิต (แทนที่จะเป็นศูนย์ขยาย) เนื่องจากกฎการเลื่อนเลขคณิต (อธิบายไว้ใน JLS, การแปลงและการส่งเสริมการขาย)

มีปริศนาที่น่าสนใจเกี่ยวกับเรื่องนี้ที่อธิบายไว้ใน Java Puzzlers ("ความยินดียิ่งใหญ่ในทุกๆทาง") โดย Joshua Bloch และ Neal Gafter เมื่อเปรียบเทียบค่าไบต์กับค่า int ไบต์จะถูกขยายสัญญาณไปยัง int จากนั้นค่านี้จะถูกเปรียบเทียบกับค่าอื่น ๆ

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

โปรดทราบว่าประเภทตัวเลขทั้งหมดได้รับการลงนามใน Java โดยมีข้อยกเว้นในการถ่านเป็นประเภทจำนวนเต็ม 16 บิตที่ไม่ได้ลงชื่อ


ฉันคิดว่า& 0xFFมันไม่จำเป็น
— Ori Popowski

11
@LeifEricson ฉันเชื่อว่า& 0xFFs จำเป็นเพราะบอก JVM ให้แปลง byte ที่เซ็นชื่อเป็นจำนวนเต็มโดยตั้งค่าบิตเหล่านั้น มิฉะนั้นไบต์ -1 (0xFF) จะเปลี่ยนเป็น int -1 (0xFFFFFFFF) ฉันอาจผิดและแม้ว่าฉันจะไม่เจ็บและทำให้สิ่งต่าง ๆ ชัดเจนขึ้น
— coderforlife

4
& 0xFF จำเป็นต้องมีแน่นอน 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 ไม่จริง ก่อนที่คุณจะดำเนินการ&ดำเนินการbyteกับ 0xFF ( int) JVM จะส่งbyteไปที่intโดยมี1 ส่วนขยายหรือ0 ขยายตามบิตนำหน้าก่อน ไม่มีจาวาในไบต์ที่ไม่ได้ลงชื่อbyte s จะถูกลงชื่อเสมอ
— Nier

2
เมื่อแยกวิเคราะห์ int จากอาร์เรย์ไบต์ให้ใส่ใจกับขนาดของอาร์เรย์ไบต์ถ้ามากกว่า 4 ไบต์ตามเอกสารของByteBuffer.getInt(): Reads the next four bytes at this buffer's current positionเฉพาะ 4 ไบต์แรกเท่านั้นที่จะถูกแยกวิเคราะห์ซึ่งไม่ควรเป็นสิ่งที่คุณต้องการ
— Bin

57

คุณยังสามารถใช้ BigInteger เป็นไบต์ความยาวผันแปรได้ คุณสามารถแปลงเป็น long, int หรือ short ใดก็ได้ตามที่คุณต้องการ

new BigInteger(bytes).intValue();

หรือเพื่อแสดงขั้ว:

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

ในการรับจำนวนไบต์กลับมาเพียง:

new BigInteger(bytes).toByteArray()

1
โปรดทราบว่าตั้งแต่ 1.8 มันintValueExactไม่ใช่intValue
— Abhijit Sarkar

5

การใช้งานพื้นฐานจะเป็นดังนี้:

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

เพื่อที่จะเข้าใจสิ่งต่าง ๆ คุณสามารถอ่านบทความ WP นี้: http://en.wikipedia.org/wiki/Endianness

34 12 78 56 bc 9aแหล่งที่มารหัสข้างต้นออกจะ 2 ไบต์แรก ( 34 12) แทนจำนวนเต็มแรกเป็นต้นซอร์สโค้ดด้านบนจะเข้ารหัสจำนวนเต็มในรูปแบบ 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

บางคนที่มีข้อกำหนดที่ต้องอ่านจากบิตให้บอกว่าคุณต้องอ่านจาก 3 บิตเท่านั้น แต่คุณต้องมีจำนวนเต็มเซ็นชื่อแล้วใช้ต่อไปนี้:

data is of type: java.util.BitSet

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

3สามารถเปลี่ยนหมายเลขเวทย์มนตร์ด้วยจำนวนบิต (ไม่ใช่ไบต์) ที่คุณใช้


0

ฝรั่งมักมีสิ่งที่คุณต้องการ

หากต้องการไปจากอาร์เรย์ไบต์เป็น int: Ints.fromBytesArrayให้ทำที่นี่

หากต้องการไปจากอาร์เรย์ int ถึงไบต์: Ints.toByteArrayให้เปิดที่นี่


-7

ฉันคิดว่านี่เป็นโหมดที่ดีที่สุดในการส่งถึง int

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

แรก comvert ไบต์เพื่อ String

comb=B+"";

ขั้นตอนต่อไปคือกลับไปที่ int

out= Integer.parseInt(comb);

แต่ไบต์อยู่ในความโกรธ -128 ถึง 127 สำหรับ reasone นี้ฉันคิดว่าดีกว่าใช้ rage 0 ถึง 255 และคุณต้องทำสิ่งนี้:

out=out+256;

นี่เป็นสิ่งที่ผิด พิจารณาไบต์ 0x01 วิธีการของคุณจะแสดงผล 129 ซึ่งเป็นสิ่งที่ผิด 0x01 ควรส่งออกจำนวนเต็ม 1 คุณควรเพิ่ม 128 เฉพาะเมื่อจำนวนเต็มที่คุณได้รับจาก parseInt เป็นลบ
— disklosr

ฉันหมายถึงคุณควรเพิ่ม 256 ไม่ใช่ 128 ไม่สามารถแก้ไขได้ในภายหลัง
— disklosr

โพสต์ที่เปลี่ยนไปเพื่อเพิ่ม 256 เนื่องจากอาจเป็นประโยชน์กับผู้อื่น!
— apmartin1991

สิ่งนี้ทำการคัดเลือกและสร้างวัตถุใหม่ (คิดว่าทำเพื่อห่วง) ซึ่งอาจทำให้ประสิทธิภาพลดลงโปรดตรวจสอบวิธี Integer.toString () สำหรับคำแนะนำเกี่ยวกับวิธีการแยกตัวเลข
— Marcos Vasconcelos

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