【问题标题】:summing Int32 represented by byte array in c#在c#中对字节数组表示的Int32求和
【发布时间】: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 类假定您系统的本机字节序。

标签: c# .net arrays


【解决方案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 可能有用。
猜你喜欢
  • 2017-05-04
  • 2015-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
相关资源
最近更新 更多