Bir bayt dizisini Java'da tamsayıya veya tam tersine dönüştürme


139

Bazı verileri Java'daki bayt dizilerine depolamak istiyorum. Temelde sadece sayı başına 2 bayt alabilen sayılar.

Nasıl bir tamsayıyı 2 bayt uzunluğunda bir bayt dizisine dönüştürebileceğini bilmek istiyorum. Google'da bir sürü çözüm buldum, ancak çoğu kodda ne olduğunu açıklamıyor. Gerçekten anlamadığım birçok şey var, bu yüzden temel bir açıklamayı takdir ediyorum.


4
Ne kadar do Eğer biraz değiştirme ile ilgili anladın mı? Sorunun, bayt dizilerine dönüştürmekten daha çok "biraz kayması ne yapar" gibi görünüyor, gerçekten - dönüşümün nasıl çalışacağını anlamak istiyorsanız.
Jon Skeet

1
(Açıklığa kavuşturmak için, her iki soruda da iyiyim, ancak hangi sorunun gerçekten cevaplanmasını istediğinizi açıklığa kavuşturmaya değer . Muhtemelen sizin için daha yararlı bir cevap alacaksınız.)
Jon Skeet

Tamam, anladım! Yorum için teşekkürler. Biraz kaymanın ne olduğunu biliyorum, henüz bayt dizilerini dönüştürmede ne için kullanıldığını anlamadım.
Chris

3
@prekageo ve Jeff Mercado İki cevabınız için teşekkürler. prekageo bunun nasıl yapıldığına dair iyi bir açıklama yaptı, güzel link! Bu benim için çok daha net. Ve Jeff Mercados çözümü yaşadığım sorunu çözdü.
Chris

Yanıtlar:


230

java.nioÖzellikle ad alanında bulunan sınıfları kullanın ByteBuffer. Tüm işi sizin için yapabilir.

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
Bayt dizisinin yalnızca 1 veya 2 tamsayı içermesi çok mu pahalı? Maliyet oluşturma konusunda emin değilim a ByteBuffer.
Miyav Kedi 2012

2-4 baytlık yığınlarda ne sıklıkla ikili verilerle çalışıyorsunuz? Gerçekten mi? Aklı başında bir uygulama, BUFSIZ parçalarında (genellikle 4kb) onunla çalışır veya bu ayrıntıyı gizleyen diğer IO kitaplıklarını kullanır. Çerçeve içinde, veri arabellekleri üzerinde çalışmanıza yardımcı olmak için ayrılmış tüm bir kütüphane vardır. Ortak operasyonları iyi bir sebep olmaksızın (mükemmel veya diğer kritik operasyonlar olmadan) uygularken, kendinize ve kodunuzun diğer kullanıcılarına bir kötülük yaparsınız. Bu tamponlar sadece diziler üzerinde çalışan sarıcılar, başka bir şey değil.
Jeff Mercado

Nasıl olur da soyut bir sınıfı başlatabilirsiniz?

1
@JaveneCPPMcGowan Bu cevapta doğrudan bir örnek yok. Fabrika yöntemleri bahsediyorsan wrapve allocatebunlar soyut sınıfının bir örneğini dönmüyor ByteBuffer.
Marko Topolnik

3 bayt adım için bir çözüm değil. Biz alabilirsiniz Char, Short, Int. Ben 4 bayt için pad ve her seferinde 4 atın olabilir varsayalım, ama ben değil.
John

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

İmzalı baytları int olarak paketlerken, her baytın maskelenmesi gerekir, çünkü aritmetik yükseltme kuralı (JLS, Dönüşümler ve Promosyonlar bölümünde açıklanmıştır) nedeniyle 32 bite (sıfır genişletilmiş yerine) işarete genişletilir.

Bununla ilgili ilginç bir yapboz Joshua Bloch ve Neal Gafter tarafından Java Puzzlers'da ("Her Baytta Büyük Bir Zevk") açıklanmıştır. Bir bayt değeri bir int değeriyle karşılaştırılırken, bayt bir int'e genişletilir ve sonra bu değer diğer int ile karşılaştırılır

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

Tüm sayısal türlerin, 16 bit işaretsiz tam sayı türü olan char dışında Java'da imzalandığını unutmayın.


Sanırım & 0xFFgereksiz.
Ori Popowski

11
@LeifEricson JVM'ye & 0xFFimzalı baytı sadece bu bitler setiyle bir tamsayıya dönüştürmesini söylediği için s'nin gerekli olduğuna inanıyorum . Aksi takdirde, bayt -1 (0xFF) int -1'e (0xFFFFFFFF) dönüşür. Yanlış olabilirdim ve ben olsam bile incinmez ve işleri daha açık hale getirir.
coderforlife

4
& 0xFF gerçekten zorunludur. 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 Aslında değil. Eğer uygulamadan önce ve ilgili işlem byte0xFF ile ( int) JVM döküm olacak bytekadar intolan uzatılmış 1 ya da 0 genişletilmiş birinci bit önde gelen göre yöntem. Java'da imzasız bayt yoktur byte, her zaman imzalanır.
Nier

2
İnt bayt dizisinden ayrıştırılırken, şu belgeye göre 4 bayttan büyükse bayt dizisinin boyutuna dikkat edin ByteBuffer.getInt(): Reads the next four bytes at this buffer's current positionyalnızca ilk 4 bayt ayrıştırılır;
Bin

57

BigInteger'ı değişken uzunluklu baytlar için de kullanabilirsiniz. Gereksinimlerinize uygun olanı uzun, int veya kısa biçime dönüştürebilirsiniz.

new BigInteger(bytes).intValue();

veya polariteyi belirtmek için:

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

Baytları geri almak için:

new BigInteger(bytes).toByteArray()

1
1.8'den beri, öyle intValueExactdeğilintValue
Abhijit Sarkar

5

Temel bir uygulama aşağıdaki gibi olabilir:

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

Bir şeyleri anlamak için şu WP makalesini okuyabilirsiniz: http://en.wikipedia.org/wiki/Endianness

Yukarıdaki kaynak kodu çıkacaktır 34 12 78 56 bc 9a. İlk 2 bayt ( 34 12), ilk tamsayıyı, vb. Temsil eder. Yukarıdaki kaynak kodu, tam sayıları küçük endian biçiminde kodlar.


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

Bitlerden okumak zorunda oldukları bir gereksinimi olan biri, sadece 3 bitten okumak zorunda olduğunuzu ancak işaretli tam sayıya ihtiyacınız olduğunu ve sonra aşağıdakileri kullandığını söyleyelim:

data is of type: java.util.BitSet

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

Sihirli sayı 3, kullandığınız bit sayısı (bayt değil) ile değiştirilebilir .


0

Sıklıkla, guava ihtiyacınız olan şey var.

Bayt dizisinden int: 'e gitmek için Ints.fromBytesArray, buraya doc

İnt'ten bayt dizisine gitmek için: Ints.toByteArraydoc burada


-7

Bence bu int için döküm için en iyi mod

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

Dize ilk comvert bayt

comb=B+"";

sonraki adım bir int

out= Integer.parseInt(comb);

ama bayt bu reasone için -128 ila 127 öfkesinde, bence 0 ila 255 öfke kullanmak daha iyidir ve sadece bunu yapmanız gerekir:

out=out+256;

Bu yanlış. 0x01 baytını düşünün. Metodunuz 129 çıktısını verir ve bu yanlıştır. 0x01 tamsayı 1'in çıktısını almalıdır. Yalnızca parseInt öğesinden aldığınız tamsayı negatifse 128 eklemelisiniz.
disklosr

Yani 128 değil 256 eklemeliydim. Daha sonra düzenleyemedim.
disklosr

başkalarına yararlı olabilir gibi 256 eklemek için yazı değiştirildi!
apmartin1991

Bu, çok fazla döküm yapar ve performansı düşürebilecek yeni nesneler oluşturur (bunu döngüler için yapmayı düşünün), lütfen sayıların ayrıştırılmasıyla ilgili ipuçları için Integer.toString () yöntemini kontrol edin.
Marcos Vasconcelos

Ayrıca, stackoverflow üzerinde kod gönderirken nokta kolayca mantıklı kod sonrası etmektir. Kolay anlaşılır olan kodun anlaşılır tanımlayıcıları olmalıdır . Yığın akışı üzerinde, anlaşılır olması mutlaka İngilizce anlamına gelir .
Mike Nakis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.