C # int tote []


172

Bir intkullanmak için byte[]bir yol dönüştürmek gerekir BitConverter.GetBytes(). Ancak, aşağıdaki spesifikasyona uyup uymadığından emin değilim:

XDR imzalı bir tam sayı, [-2147483648,2147483647] aralığındaki bir tamsayıyı kodlayan 32 bitlik bir veridir. Tam sayı ikinin tamamlayıcı gösteriminde gösterilir. En çok ve en az anlamlı bayt sırasıyla 0 ve 3'tür. Tamsayılar aşağıdaki gibi beyan edilir:

Kaynak: RFC1014 3.2

Yukarıdaki spesifikasyonu karşılayacak bir bayt-içi dönüşümü nasıl yapabilirim?


Bu iyi bir soru.
ChaosPandion

Yanıtlar:


217

RFC, işaretli bir tam sayının, büyük bir endian yolunda sıralanan baytlarla normal 4 baytlık bir tam sayı olduğunu söylemeye çalışıyor.

Şimdi, büyük olasılıkla küçük bir endian makinesinde çalışıyorsunuz ve BitConverter.GetBytes()size byte[]tersini vereceksiniz . Böylece deneyebilirsiniz:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
Array.Reverse(intBytes);
byte[] result = intBytes;

Ancak kodun en taşınabilir olması için bunu şu şekilde yapabilirsiniz:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
    Array.Reverse(intBytes);
byte[] result = intBytes;

7
Veya ToArray kullanın. byte [] sonuç = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Lars Truijens

neden son satır byte[] result = intBytes;? intByteszaten istediğiniz dizi değil mi?
derHugo

2
@derHugo Bu doğru, resultbu kod parçasında gereksiz. Bununla birlikte, sonucun adının bazı değişkenlerde bulunduğunu anlayabildiklerini varsaymaktan ziyade okuyucunun sonucun ne olduğunu açıkça göstermenin daha pedagojik olduğunu hissettim intBytes. Ayrıca, hafızayı kopyalamadığı veya yeni hafıza ayırmadığı için atama yapmak ucuzdur, sadece önceden atanmış diziye yeni bir ad ekler. Öyleyse neden yapmıyorsunuz?
1919'da

41

Bunu yapmanın başka bir yolu: hepimizin bildiği gibi 1x bayt = 8x bit ve ayrıca "normal" bir tamsayı (int32) 32 bit (4 bayt) içerir. Bitleri sağa kaydırmak için >> işlecini kullanabiliriz (>> işleci değer değiştirmez.)

int intValue = 566;

byte[] bytes = new byte[4];

bytes[0] = (byte)(intValue >> 24);
bytes[1] = (byte)(intValue >> 16);
bytes[2] = (byte)(intValue >> 8);
bytes[3] = (byte)intValue;

Console.WriteLine("{0} breaks down to : {1} {2} {3} {4}",
    intValue, bytes[0], bytes[1], bytes[2], bytes[3]);

1
Dizi başlatıcısı ve xor (^) ve & 0xFFbitleri gereksizdir.
dtb

6
Big-endian, bu yüzden önce MSB saklanır, bu nedenle endekslerinizi tersine çevirmelisiniz.
Marcin Deptuła

7
Bunun tersine bir örnek ekleyebilir miyiz? (bayt tamsayıya geri döndü)
13:13

1
Bunu performans nedeniyle kullanıyorum. Bu küçük farkı umursamıyorsam , kabul edilen cevaba gideceğim.
Zamansız

2
Bu kodu bir uncheckedbloğa sarmalısınız. Şu anda yalnızca derleyici ayarlarında tamsayı taşması denetimi devre dışı bırakılmışsa çalışır.
CodesInChaos

25

BitConverter.GetBytes(int) neredeyse istediklerini yapar, ancak endianlık yanlıştır.

Jon Skeet'in EndianBitConverter sınıfını kullanmadan önce tam sayı değeri içindeki baytları değiştirmek için IPAddress.HostToNetwork yöntemini BitConverter.GetByteskullanabilirsiniz . Her iki yöntem de taşınabilirlik konusunda doğru olanı yapar.

int value;
byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value));

3

Bu açıklamaya baktığımda, bu xdr tamsayısının sadece büyük-endian "standart" bir tamsayı olduğu hissine sahibim, ancak en gizli şekilde ifade ediliyor. Two'nun tamamlayıcı notasyonu U2 olarak daha iyi bilinir ve bugünün işlemcilerinde kullandığımız şey budur. Bayt sırası bunun büyük endian gösterimidir.
Bu nedenle, sorunuzu cevaplarken, dizinizdeki öğeleri (0 <--> 3, 1 <--> 2) ters çevirmelisiniz, çünkü bunlar küçük endian olarak kodlanmıştır. Sadece emin olmak için, ilk önce BitConverter.IsLittleEndianhangi makinede çalıştığınızı kontrol etmelisiniz .


3

Neden yukarıdaki örneklerde bu kod ...

Açık düzen içeren bir yapı her iki şekilde de hareket eder ve performans isabeti yoktur.

Güncelleme: Endianness ile nasıl başa çıkılacağına dair bir soru olduğundan, bunu nasıl soyutlayacağınızı gösteren bir arayüz ekledim. Başka bir uygulayıcı yapı ise karşı dava ile baş edebilir

public interface IIntToByte
{
    Int32 Int { get; set;}

    byte B0 { get; }
    byte B1 { get; }
    byte B2 { get; }
    byte B3 { get; }
}

[StructLayout(LayoutKind.Explicit)]
public struct IntToByteLE : UserQuery.IIntToByte
{
    [FieldOffset(0)]
    public Int32 IntVal;

    [FieldOffset(0)]
    public byte b0;
    [FieldOffset(1)]
    public byte b1;
    [FieldOffset(2)]
    public byte b2;
    [FieldOffset(3)]
    public byte b3;

    public Int32 Int {
        get{ return IntVal; }
        set{ IntVal = value;}
    }

    public byte B0 => b0;
    public byte B1 => b1;
    public byte B2 => b2;
    public byte B3 => b3; 
}

2
Bunu nasıl kullanırdınız? ve ikincisi aynı sonucu alır mısın, bunu 'big-endian' ve 'little-endian' üzerinde de çalıştırıyorsun.
Peter

@Peter işte bir güncelleme. Yine de vardiyalardan daha iyi performans göstermeli
Sten Petrov

Evet. Temelde c # structs olarak uygulanan bir c ++ birliği. Bu süper hızlıdır ve bit dönüştürücü / endian problem çözümlerine iyi uymayan her türlü şeyi paketlemek ve paketinden çıkarmak için iyidir. IIRC, NAudio bu yaklaşımı çok iyi bir etki için kullanır.
Craig

Bu en iyi cevap ve bunun en üstte olmaması biraz üzücü ama 2019'da programlama durumu hakkında bir şeyler söylüyor
John Evans


1

Diğer yol BinaryPrimitives'ı böyle kullanmaktır.

byte[] intBytes = BitConverter.GetBytes(123); int actual = BinaryPrimitives.ReadInt32LittleEndian(intBytes);


0
using static System.Console;

namespace IntToBits
{
    class Program
    {
        static void Main()
        {
            while (true)
            {
                string s = Console.ReadLine();
                Clear();
                uint i;
                bool b = UInt32.TryParse(s, out i);
                if (b) IntPrinter(i);
            }
        }

        static void IntPrinter(uint i)
        {
            int[] iarr = new int [32];
            Write("[");
            for (int j = 0; j < 32; j++)
            {
                uint tmp = i & (uint)Math.Pow(2, j);

                iarr[j] = (int)(tmp >> j);
            }
            for (int j = 32; j > 0; j--)
            {
                if(j%8==0 && j != 32)Write("|");
                if(j%4==0 && j%8 !=0) Write("'");
                Write(iarr[j-1]);
            }
            WriteLine("]");
        }
    }
}```

-1
byte[] Take_Byte_Arr_From_Int(Int64 Source_Num)
{
   Int64 Int64_Num = Source_Num;
   byte Byte_Num;
   byte[] Byte_Arr = new byte[8];
   for (int i = 0; i < 8; i++)
   {
      if (Source_Num > 255)
      {
         Int64_Num = Source_Num / 256;
         Byte_Num = (byte)(Source_Num - Int64_Num * 256);
      }
      else
      {
         Byte_Num = (byte)Int64_Num;
         Int64_Num = 0;
      }
      Byte_Arr[i] = Byte_Num;
      Source_Num = Int64_Num;
   }
   return (Byte_Arr);
}

lütfen daha fazla açıklama ekleyin
Gregor Doroschenko

Cevabınız için teşekkürler, neden stackoverflow.com/a/1318948/58553 kullanmak yerine yeni bir uygulama yazmayı seçtiniz ? (çözümünüzle ilgili herhangi bir profesyonel veya eksileri var mı)
Peter

Projenin sadece bir dil ile geliştirilmemesi gerektiğinde, aynı fikirlerin uygulanması gerektiği durumlarda diller arasındaki kod benzerliğinin fark edilmesi yararlı olabilir. Bu yol hataları yakalamada yardımcı olabilir. 'Endian' ile 'c #' işlevi 'BitConverter.GetBytes' kullandığınızdan emin olun. Ve dahası, bazı durumlarda sadece Int32 (4 bayt) veya Int64 (8 bayt) değil, diğer sayıda bayta dönüştürme kullanın. Teşekkürler.
Valery Rode

Bu gerçekten evrensel olarak kullanılabilir değil ... her zaman 8 baytlık bir dizi döndürür, bu da 64 bitlik giriş değerleri ile çok fazla ek iş getirir. Mantıksal olarak bir 2 baytlık dizi dönüştürmek istiyorum Int16.
Nyerguds
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.