แปลงอาร์เรย์ไบต์เป็นจำนวนเต็มใน 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.