Chuyển đổi một mảng byte thành số nguyên trong Java và ngược lại


139

Tôi muốn lưu trữ một số dữ liệu vào các mảng byte trong Java. Về cơ bản chỉ là những con số có thể mất tới 2 byte mỗi số.

Tôi muốn biết làm thế nào tôi có thể chuyển đổi một số nguyên thành một mảng byte dài 2 byte và ngược lại. Tôi đã tìm thấy rất nhiều giải pháp googling nhưng hầu hết trong số họ không giải thích những gì xảy ra trong mã. Có rất nhiều thứ thay đổi mà tôi không thực sự hiểu nên tôi sẽ đánh giá cao một lời giải thích cơ bản.


4
Bao nhiêu làm bạn hiểu về chút chuyển dịch? Có vẻ như câu hỏi thực sự là "việc dịch chuyển bit làm gì" hơn là về việc chuyển đổi sang mảng byte, thực sự - nếu bạn thực sự muốn hiểu cách chuyển đổi sẽ hoạt động.
Jon Skeet

1
(Chỉ cần làm rõ, tôi là tốt với một trong hai câu hỏi, nhưng nó đáng để làm cho nó rõ ràng câu hỏi mà bạn thực sự muốn được trả lời Bạn đang có khả năng để có được một câu trả lời đó là hữu ích hơn đối với bạn như vậy..)
Jon Skeet

Được rồi tôi đã nhận được điểm của bạn! Cảm ơn đã nhận xét. Tôi biết những gì dịch chuyển bit tôi chỉ không hiểu những gì nó được sử dụng để chuyển đổi các mảng byte.
Chris

3
@prekageo và Jeff Mercado Cảm ơn hai câu trả lời của bạn. prekageo đã đưa ra một lời giải thích tốt về cách thực hiện, liên kết tốt đẹp! Điều đó làm cho nó rõ ràng hơn rất nhiều với tôi. Và giải pháp Jeff Mercados đã giải quyết vấn đề tôi gặp phải.
Chris

Câu trả lời:


230

Sử dụng các lớp được tìm thấy trong java.niokhông gian tên, đặc biệt là ByteBuffer. Nó có thể làm tất cả các công việc cho bạn.

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
Có quá đắt không nếu mảng byte chỉ chứa 1 hoặc 2 số nguyên? Không chắc chắn về chi phí xây dựng a ByteBuffer.
Mèo Meow 2012

Bạn có thường xuyên làm việc với dữ liệu nhị phân trong các đoạn 2-4 byte không? Có thật không? Việc triển khai lành mạnh sẽ hoạt động với nó trong các khối BUFSIZ (thường là 4kb) hoặc sử dụng các thư viện IO khác ẩn chi tiết này. Có toàn bộ thư viện trong khuôn khổ dành riêng để giúp bạn làm việc trên bộ đệm dữ liệu. Bạn thực hiện một dịch vụ cho chính mình và những người duy trì mã khác của bạn khi bạn triển khai các hoạt động chung mà không có lý do chính đáng (có thể là hoàn hảo hoặc hoạt động quan trọng khác). Các bộ đệm này chỉ là các hàm bao hoạt động trên các mảng, không có gì hơn.
Jeff Mercado

Làm thế nào bạn có thể khởi tạo một lớp trừu tượng?

1
@JaveneCPPMcGowan Không có câu trả lời trực tiếp nào trong câu trả lời này. Nếu bạn có nghĩa là các phương thức nhà máy wrapallocate, chúng không trả về một thể hiện của lớp trừu tượng ByteBuffer.
Marko Topolnik

Không phải là một giải pháp cho sải chân 3 byte. Chúng tôi có thể nhận được Char, Short, Int. Tôi cho rằng tôi có thể đệm tới 4 byte và loại bỏ lần thứ 4 mỗi lần, nhưng tôi không muốn.
Giăng

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

Khi đóng gói các byte đã ký vào một int, mỗi byte cần được che đi vì nó được mở rộng ký hiệu thành 32 bit (chứ không phải mở rộng bằng 0) do quy tắc quảng cáo số học (được mô tả trong JLS, Chuyển đổi và Khuyến mãi).

Có một câu đố thú vị liên quan đến điều này được mô tả trong Java Puzzlers ("A Big Delight in Every Byte") của Joshua Bloch và Neal Gafter. Khi so sánh giá trị byte với giá trị int, byte được mở rộng đăng nhập thành int và sau đó giá trị này được so sánh với giá trị int khác

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

Lưu ý rằng tất cả các loại số được ký trong Java ngoại trừ char là loại số nguyên không dấu 16 bit.


Tôi nghĩ rằng & 0xFFs là không cần thiết.
Ori Popowski

11
@LeifEricson Tôi tin rằng & 0xFFs là cần thiết vì nó bảo JVM chuyển đổi byte đã ký thành một số nguyên chỉ với các bit được đặt. Nếu không, byte -1 (0xFF) sẽ biến thành int -1 (0xFFFFFFFF). Tôi có thể sai và ngay cả khi tôi không đau và làm cho mọi thứ rõ ràng hơn.
coderforlife

4
& 0xFF là bắt buộc thực sự. 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 Không thực sự. Trước khi bạn thực hiện vận hành bytevới 0xFF ( int), JVM sẽ bytechuyển sang 1int với phần mở rộng hoặc 0 được mở rộng theo bit đầu tiên. Không có byte không dấu trong Java, bytes luôn được ký.
Nier

2
Khi phân tích int từ mảng byte, hãy chú ý đến kích thước của mảng byte, nếu nó lớn hơn 4 byte, theo tài liệu của ByteBuffer.getInt(): Reads the next four bytes at this buffer's current position, chỉ 4 byte đầu tiên sẽ được phân tích cú pháp, đó không phải là điều bạn muốn.
Bin

56

Bạn cũng có thể sử dụng BigInteger cho các byte có độ dài thay đổi. Bạn có thể chuyển đổi nó thành dài, int hoặc ngắn, tùy theo nhu cầu của bạn.

new BigInteger(bytes).intValue();

hoặc để biểu thị sự phân cực:

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

Để lấy lại byte chỉ:

new BigInteger(bytes).toByteArray()

1
Lưu ý rằng kể từ ngày 1.8 intValueExact, không phảiintValue
Abhijit Sarkar

5

Một triển khai cơ bản sẽ giống như thế này:

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

Để hiểu những điều bạn có thể đọc bài viết WP này: http://en.wikipedia.org/wiki/Endianness

Mã nguồn trên sẽ xuất ra 34 12 78 56 bc 9a. 2 byte đầu tiên ( 34 12) đại diện cho số nguyên đầu tiên, v.v ... Mã nguồn ở trên mã hóa các số nguyên theo định dạng endian nhỏ.


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

Một người có yêu cầu phải đọc từ bit, giả sử bạn chỉ đọc từ 3 bit nhưng bạn cần số nguyên đã ký thì sử dụng như sau:

data is of type: java.util.BitSet

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

Số ma thuật 3có thể được thay thế bằng số bit (không phải byte) bạn đang sử dụng.


0

Như thường lệ, ổi có những gì bạn cần.

Để đi từ mảng byte đến int : Ints.fromBytesArray, doc ở đây

Để chuyển từ mảng int sang byte byte : Ints.toByteArray, doc ở đây


-7

tôi nghĩ rằng đây là một chế độ tốt nhất để chuyển sang int

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

comvert byte đầu tiên thành String

comb=B+"";

bước tiếp theo là chuyển sang int

out= Integer.parseInt(comb);

nhưng byte đang trong cơn thịnh nộ từ -128 đến 127 cho reasone này, tôi nghĩ tốt hơn là sử dụng cơn thịnh nộ từ 0 đến 255 và bạn chỉ cần làm điều này:

out=out+256;

Cái này sai. Hãy xem xét byte 0x01. Phương pháp của bạn sẽ xuất ra 129 sai. 0x01 sẽ xuất số nguyên 1. Bạn chỉ nên thêm 128 nếu số nguyên mà bạn nhận được từ parseInt là âm.
đĩalosr

Ý tôi là bạn nên thêm 256 chứ không phải 128. Không thể chỉnh sửa nó sau đó.
đĩalosr

thay đổi bài đăng để thêm 256 vì nó có thể hữu ích cho người khác!
apmartin1991

Điều này thực hiện rất nhiều việc truyền và tạo các đối tượng mới (nghĩ rằng thực hiện điều này trên các vòng lặp) có thể làm giảm hiệu suất, vui lòng kiểm tra phương thức Integer.toString () để biết gợi ý về cách phân tích số.
Marcos Vasconcelos

Ngoài ra, khi đăng mã trên stackoverflow, điểm quan trọng là đăng mã dễ dàng có ý nghĩa. Mã dễ dàng có ý nghĩa phải có định danh dễ hiểu . Và trên stackoverflow, có thể hiểu được có nghĩa là tiếng Anh .
Mike Nakis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.