C # int เป็นไบต์ []


172

ฉันจำเป็นต้องแปลงintไปเป็นวิธีหนึ่งในการทำก็คือการใช้งานbyte[] BitConverter.GetBytes()แต่ฉันไม่แน่ใจว่าสิ่งที่ตรงกับข้อกำหนดต่อไปนี้:

จำนวนเต็ม XDR ที่ลงนามคือ datum 32 บิตที่เข้ารหัสจำนวนเต็มในช่วง [-2147483648,2147483647] จำนวนเต็มถูกแทนด้วยเครื่องหมายเสริมสองอัน ไบต์ที่สำคัญที่สุดและน้อยที่สุดคือ 0 และ 3 ตามลำดับ จำนวนเต็มถูกประกาศดังนี้:

ที่มา: RFC1014 3.2

ฉันจะแปลง int เป็น byte ที่จะตอบสนองข้อกำหนดข้างต้นได้อย่างไร


นั่นเป็นคำถามที่ดี
ChaosPandion

คำตอบ:


217

RFC กำลังพยายามที่จะบอกว่าจำนวนเต็มที่ลงนามเป็นจำนวนเต็ม 4 ไบต์ปกติกับไบต์สั่งในทางใหญ่

ตอนนี้คุณน่าจะทำงานกับเครื่องจักรเล็ก ๆ น้อย ๆ และBitConverter.GetBytes()จะให้สิ่งที่byte[]ตรงกันข้ามกับคุณ ดังนั้นคุณสามารถลอง:

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

อย่างไรก็ตามเพื่อให้โค้ดพกพาได้มากที่สุดคุณสามารถทำสิ่งนี้ได้:

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

7
หรือใช้ ToArray ไบต์ [] ผล = BitConverter.GetBytes (intValue). ย้อนกลับ (). ToArray ();
Lars Truijens

ทำไมบรรทัดสุดท้ายbyte[] result = intBytes;? ยังไม่intBytesได้อาร์เรย์ที่คุณต้องการใช่ไหม
derHugo

2
@derHugo ถูกต้องแล้วresultซ้ำซ้อนในส่วนรหัสนี้ intBytesแต่ผมรู้สึกว่ามันเป็นน้ำท่วมทุ่งมากขึ้นอย่างชัดเจนแสดงให้ผู้อ่านสิ่งที่เป็นผลที่ถูกกว่าสมมติว่าพวกเขาสามารถคิดออกว่าผลที่มีอยู่ในตัวแปรบางชื่อ นอกจากนี้การทำการมอบหมายนั้นถูกเพราะมันไม่ได้คัดลอกหน่วยความจำหรือจัดสรรหน่วยความจำใหม่มันเพิ่งเพิ่มชื่อใหม่ไปยังอาร์เรย์ที่จัดสรรไว้แล้ว ดังนั้นทำไมไม่ทำมัน?
paracycle

41

นี่เป็นอีกวิธีในการทำ: เมื่อเรารู้ 1x ไบต์ = 8x บิตและจำนวนเต็ม "ปกติ" (int32) มี 32 บิต (4 ไบต์) เราสามารถใช้ตัวดำเนินการ >> เพื่อเลื่อนบิตไปทางขวา (ตัวดำเนินการ >> จะไม่เปลี่ยนแปลงค่า)

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
array initializer และ xor (^) และ& 0xFFbits ไม่จำเป็น
dtb

6
มันสุดยอดมากดังนั้น MSB จะถูกเก็บไว้ก่อนดังนั้นคุณควรกลับดัชนี
Marcin Deptuła

7
เราสามารถเพิ่มตัวอย่างของการไปในทางตรงกันข้ามได้หรือไม่? (ไบต์กลับไปจำนวนเต็ม)
jocull

1
ฉันใช้สิ่งนี้เพราะประสิทธิภาพ ถ้าฉันไม่สนใจความแตกต่างเล็ก ๆนี้ฉันจะไปหาคำตอบที่ยอมรับได้
Timeless

2
คุณควรห่อโค้ดนั้นในuncheckedบล็อก ปัจจุบันนี้ใช้งานได้เฉพาะเมื่อปิดการตรวจสอบจำนวนเต็มล้นในการตั้งค่าคอมไพเลอร์
CodesInChaos

25

BitConverter.GetBytes(int) เกือบจะทำในสิ่งที่คุณต้องการยกเว้น endianness นั้นผิด

คุณสามารถใช้IPAddress.HostToNetworkวิธีการสลับไบต์ภายในค่าจำนวนเต็มก่อนที่จะใช้BitConverter.GetBytesหรือการใช้งานจอน Skeet ของระดับ EndianBitConverter ทั้งสองวิธีทำสิ่งที่ถูกต้องเกี่ยวกับการพกพา

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

3

เมื่อฉันดูคำอธิบายนี้ฉันมีความรู้สึกว่าจำนวนเต็ม xdr นี้เป็นเพียงจำนวนเต็ม "มาตรฐาน" จำนวนเต็มขนาดใหญ่ แต่มันแสดงออกในลักษณะที่สับสนที่สุด สัญกรณ์เสริมสองอย่างนั้นรู้กันดีกว่าในชื่อ U2 และเป็นสิ่งที่เราใช้กับโปรเซสเซอร์ในปัจจุบัน เพื่อ byte แสดงให้เห็นว่ามันเป็นbig-สัญกรณ์
ดังนั้นในการตอบคำถามของคุณคุณควรสลับองค์ประกอบในอาร์เรย์ของคุณ (0 <--> 3, 1 <--> 2) เนื่องจากมีการเข้ารหัสในแบบ endian น้อย เพียงเพื่อให้แน่ใจว่าคุณควรตรวจสอบก่อนBitConverter.IsLittleEndianเพื่อดูว่าคุณกำลังใช้เครื่องใดอยู่


3

ทำไมรหัสทั้งหมดในตัวอย่างด้านบน ...

โครงสร้างที่มีเค้าโครงชัดเจนจะทำหน้าที่ทั้งสองวิธีและไม่มีการเข้าชมประสิทธิภาพ

อัปเดต: เนื่องจากมีคำถามเกี่ยวกับวิธีจัดการกับ endianness ฉันจึงเพิ่มอินเทอร์เฟซที่แสดงให้เห็นถึงวิธีนามธรรม โครงสร้างการใช้งานอื่นสามารถจัดการกับกรณีตรงข้าม

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
คุณจะใช้สิ่งนี้อย่างไร และอย่างที่สองคุณจะได้รับผลลัพธ์เดียวกันแม้ว่าคุณจะเรียกใช้ใน 'big-endian' และ 'little-endian'
ปีเตอร์

@Peter นี่คือการปรับปรุง มันควรจะทำงานได้ดีกว่ากะ
Sten Petrov

ได้. โดยพื้นฐานแล้วจะมีการรวมสหภาพ c ++ เป็น c # structs นี่เป็นความเร็วที่รวดเร็วและดีสำหรับการบรรจุและแกะสิ่งต่าง ๆ ที่ไม่พอดีกับโซลูชันตัวแก้ไขปัญหาบิตคอนเวอร์เตอร์ / endian IIRC, NAudio ใช้วิธีนี้เพื่อให้ได้ผลดีมาก
Craig.Feied

นี่เป็นคำตอบที่ดีที่สุดและเป็นเรื่องน่าเศร้าที่นี่ไม่ใช่จุดสูงสุด แต่เพียงบอกคุณบางอย่างเกี่ยวกับสถานะของการเขียนโปรแกรมในปี 2019
John Evans



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

โปรดเพิ่มคำอธิบายเพิ่มเติม
Gregor Doroschenko

ขอบคุณสำหรับคำตอบที่ว่าทำไมคุณไม่เลือกที่จะเขียนการดำเนินงานใหม่แทนการใช้stackoverflow.com/a/1318948/58553 ? (มีข้อดีหรือข้อเสียใด ๆ กับทางแก้ปัญหาของคุณ)
Peter

ในกรณีที่จำเป็นต้องพัฒนาโครงการด้วยภาษาเดียวมันจะมีประโยชน์ในการตระหนักถึงความคล้ายคลึงกันของรหัสระหว่างภาษาในสถานการณ์ที่ต้องนำแนวคิดที่เหมือนกันมาใช้ วิธีนี้อาจช่วยในการจับข้อผิดพลาด ให้แน่ใจว่าใช้ 'c #' ฟังก์ชั่น 'BitConverter.GetBytes' ด้วย 'Endian' ตรวจสอบว่าทุกสิ่งที่จำเป็นในกรณีส่วนใหญ่ และอื่น ๆ ในบางสถานการณ์อาจเกิดการใช้งานการแปลงไม่เพียง แต่เป็น Int32 (4 ไบต์) หรือ Int64 (8 ไบต์) แต่ไปยังจำนวนไบต์อื่น ๆ ขอบคุณ
Valery Rode

นี่ไม่ใช่การใช้งานในระดับสากลเลย ... มันจะส่งคืนอาร์เรย์ 8 ไบต์เสมอซึ่งจะนำงานพิเศษมากมายมาใช้กับค่าอินพุตที่ไม่ใช่ int 64- บิต ฉันมีเหตุผลที่ต้องการอาร์เรย์ 2 Int16ไบต์เมื่อแปลง
Nyerguds
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.