在c#中对字节数组表示的Int32求和

Posted

技术标签:

【中文标题】在c#中对字节数组表示的Int32求和【英文标题】:summing Int32 represented by byte array in c# 【发布时间】:2016-12-18 19:56:16 【问题描述】:

我有一个音频数据数组,它是由字节数组表示的大量 Int32 数字(每个 4 字节元素代表一个 Int32),我想对数据进行一些操作(例如,将 10 添加到每个Int32)。

我将字节转换为 Int32,进行操作并将其转换回字节,如下例所示:

//byte[] buffer;
for (int i=0; i<buffer.Length; i+=4)

    Int32 temp0 = BitConverter.ToInt32(buffer, i);
    temp0 += 10;
    byte[] temp1 = BitConverter.GetBytes(temp0);
    for (int j=0;j<4;j++)
    
        buffer[i + j] = temp1[j];
    

但我想知道是否有更好的方法来进行这种操作。

【问题讨论】:

如果你知道字节的顺序,你可以直接在字节上自己添加。 定义“更好”。你觉得这不可读吗?是不是太慢了? 我觉得这既丑陋又缓慢,任何可以改进的建议都将不胜感激。 @MillieSmith 如果这就是你的意思,它是大端,但我不确定在每次处理进位位的同时添加字节会更快。你有更好的主意吗? @pio 如果是大端,不要使用BitConverter 类。 BitConverter 类假定您系统的本机字节序。 【参考方案1】:

您可以查看.NET Reference Source 以获取有关如何从/到大端转换的指针(咧嘴笑)。

class intFromBigEndianByteArray 
    public byte[] b;
    public int this[int i] 
        get 
            i <<= 2; // i *= 4; // optional
            return (int)b[i] << 24 | (int)b[i + 1] << 16 | (int)b[i + 2] << 8 | b[i + 3];
        
        set 
            i <<= 2; // i *= 4; // optional
            b[i    ] = (byte)(value >> 24);
            b[i + 1] = (byte)(value >> 16);
            b[i + 2] = (byte)(value >>  8);
            b[i + 3] = (byte)value;
        
    

和样品使用:

byte[] buffer =  127, 255, 255, 255, 255, 255, 255, 255 ;//big endian  int.MaxValue, -1 

//bool check = BitConverter.IsLittleEndian;     // true
//int test = BitConverter.ToInt32(buffer, 0);   // -129 (incorrect because little endian)

var fakeIntBuffer = new intFromBigEndianByteArray()  b = buffer ;

fakeIntBuffer[0] += 2;    //  128, 0, 0, 1  = big endian int.MinValue - 1
fakeIntBuffer[1] += 2;    //    0, 0, 0, 1  = big endian 1

Debug.Print(string.Join(", ", buffer)); // "128, 0, 0, 0, 1, 0, 0, 1" 

为了获得更好的性能,您可以研究并行处理和 SIMD 指令 - Using SSE in C# 为了获得更好的性能,您可以查看Utilizing the GPU with c#

【讨论】:

谢谢!如果我想对 little endian(以 little endian 表示 int32 的字节数组)做同样的事情,我应该改变什么? >>/>24 代表 i+3 而不是 i 等等)。 @pio 只改变位移的顺序或索引的顺序就足够了,但是对于小端系统上的小端,你可以得到一个指向内存位置的 int 引用指针带有不安全代码的字节数组。 你的意思是:int [] converted = buffer; @pio 我的意思是根本不需要转换,因为字节数组将包含正确字节顺序的整数,并且只获取字节数组中的内存地址就足够了。有点像.Net 参考源fixed( byte * pbyte = &amp;buffer[startIndex]) return *((int *) pbyte); @pio 抱歉,这是一个糟糕的建议,因为不安全的代码和指针更适合理解它的高级用户(即使我不使用它),BitConverter.ToInt32 已经这样做了你。对于小端架构上的小端字节,我建议您使用 BinaryReader.ReadInt32BinaryWriter.Write(Int32),您可以在 FileStreamMemoryStream 上使用它们【参考方案2】:

下面的方法怎么样:

struct My

    public int Int;

var bytes = Enumerable.Range(0, 20).Select(n => (byte)(n + 240)).ToArray();
foreach (var b in bytes) Console.Write("0,-4", b);

// Pin the managed memory
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);

for (int i = 0; i < bytes.Length; i += 4)

    // Copy the data
    My my = (My)Marshal.PtrToStructure<My>(handle.AddrOfPinnedObject() + i);

    my.Int += 10;

    // Copy back
    Marshal.StructureToPtr(my, handle.AddrOfPinnedObject() + i, true);

// Unpin
handle.Free();

foreach (var b in bytes) Console.Write("0,-4", b);

我只是为了好玩。

不确定那不那么难看。

我不知道,会更快吗?测试一下。

【讨论】:

@Phil1970 - 我专门使用这些值来表明代码正确地进行了进位传播。 好吧,你没有为My 选择有意义的名称,所以当我意识到(它包含一个整数)时,我的评论已发布,但我一意识到就删除了它. 我认为在循环中包含 AddrOfPinnedObject 会产生很多开销。也许,如果你想要“安全”的代码,你必须这样做。否则,How to: Use Pointers to Copy an Array of Bytes 可能有用。

以上是关于在c#中对字节数组表示的Int32求和的主要内容,如果未能解决你的问题,请参考以下文章

Modbus系列随笔

优化求和 2 个字节数组

C# 是不是有办法将具有 int 字段(字节、ushort、ulong 等)的对象编写为字节数组?

(61)C#里怎么样转换字节数组与int类型

(61)C#里怎么样转换字节数组与int类型

在 C 中将偏移字节数组转换为 int32 数组