在C#中将int[]转换为byte[]

51

我知道如何用冗长的方式来处理这个问题:创建一个必要大小的字节数组,然后使用for循环将每个元素从int数组强制转换。

我想知道是否有更快的方法,因为上述方法似乎会在int大于sbyte时出错。


字节数组和“大于short”不匹配。 - H H
我的意思是,就尺寸而言,int不是与字节一对一的。 - soandos
而 short/ushort 不是与 byte 一一对应的。 - H H
1
@scandos:你想把4个字节转换成1个整数吗?请注意您的问题下方有“编辑”链接。 - H H
@henk:抱歉,我没有考虑到这一点,但是假设我可以只加载4个字节,然后将它们读取为int。似乎需要更多的“格式化”,因为并不是每4个字节都意味着是一个int(较小的数字需要更少的空间,并且如上所述,直接转换会导致数据丢失)。 - soandos
3个回答

114

如果你想进行位拷贝,即从一个 int 类型中获取 4 个字节,则使用 Buffer.BlockCopy

byte[] result = new byte[intArray.Length * sizeof(int)];
Buffer.BlockCopy(intArray, 0, result, 0, result.Length);

不要使用 Array.Copy,因为它会尝试转换而不仅仅是复制。请查看MSDN页面上的备注以获取更多信息。


2
它是如何知道每个int获取4个字节的呢? - soandos
1
它只是复制内存。它不知道intbyte的存在。result的内存表示将与intArray完全相同。 - Daniel Hilgarth
3
@Moop int[] result = new int[byteArray.Length / sizeof(int)]; Buffer.BlockCopy(byteArray, 0, result, 0, result.Length);这段代码创建了一个整型数组result,它的长度是字节数组byteArray长度除以一个整型所占用的字节数。然后使用Buffer.BlockCopy方法将byteArray中的数据复制到result中,从位置0开始,复制长度为result.Length - Daniel Hilgarth
4
如果您无法在编译时知道值类型数组的元素类型,可以使用以下代码: byte[] result = new byte[Buffer.ByteLength((Array)valueTypeArray)]; 翻译后文意不变,并更加通俗易懂。 - Zenima
5
对于丹尼尔提供的反向解决方案(从byte[]到int[]),只需要进行一个更正:应该是Buffer.BlockCopy(byteArray, 0, result, 0, byteArray.Length); => 基本上,最后一个参数不应该是result.Length,而应该是byteArray.Length。 - Jonathan Vukadinovic
显示剩余7条评论

8
除了已经被接受的答案(我现在正在使用),对于喜欢 Linq 的人来说,另一个一行代码的替代方案是:
byte[] bytes = ints.SelectMany(BitConverter.GetBytes).ToArray(); 

我认为,它可能会慢一些...

没错,但对于快速短期的单元测试来说还是可以的。建议作出修改,因为你不需要 v=> ... yptes(v) - Meirion Hughes

8

这是一个有点老的线程,现在是2022年...
我有一堆short(抱歉,没有int;-)),想让它们成为一个字节数组。在阅读了所有不同方法后,我非常困惑,于是开始对我的一些最喜欢的方法进行基准测试。
(该代码应该易于用于任何基本类型。)
它使用BenchmarkDotNet进行实际测试和统计分析。

using System;
using System.Linq;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace ArrayCastingBenchmark;

public class Benchy {
    private const int number_of_shorts = 100000;
    private readonly short[] shorts;

    public Benchy() {
        Random r = new(43);
        shorts = new short[number_of_shorts];
        for (int i = 0; i < number_of_shorts; i++)
            shorts[i] = (short) r.Next(short.MaxValue);
    }

    [Benchmark]
    public ReadOnlySpan<byte> SPANSTYLE() {
        ReadOnlySpan<short> shortSpan = new ReadOnlySpan<short>(shorts);
        return MemoryMarshal.Cast<short, byte>(shortSpan);
    }

    [Benchmark]
    public byte[] BLOCKCOPY() {
        byte[] bytes = new byte[shorts.Length * sizeof(short)];
        Buffer.BlockCopy(shorts, 0, bytes, 0, bytes.Length);
        return bytes;
    }

    [Benchmark]
    public byte[] LINQY() {
        return shorts.Select(i => (byte) i).ToArray();
    }

    [Benchmark]
    public byte[] BITCONVERTER() {
        byte[] bytes = shorts.SelectMany(BitConverter.GetBytes).ToArray();
        return bytes;
    }

    //[Benchmark]
    //public void BINARYWRITER() {
    //    var fhandle = File.OpenHandle("_shorts_binarywriter.bin", FileMode.Create, FileAccess.Write);
    //    var binaryWriter = new BinaryWriter(new FileStream(fhandle, FileAccess.Write));
    //    foreach (var shorty in shorts)
    //        binaryWriter.Write(shorty);
    //    binaryWriter.Flush();
    //    binaryWriter.Close();
    //    fhandle.Close();
    //}
}

internal class Program {
    static void Main(string[] args) {
        var summary = BenchmarkRunner.Run<Benchy>();
    }
}

我将最后一行保留了下来,因为如果你只是在所有方法的结尾添加File.WriteAllBytes函数并使它们实际产生一些输出,那么在我的机器上BLOCKCOPYSPANSTYLE稍微快一点。如果其他人也有这样的体验或者有任何想法,请告诉我。

编辑: 抱歉,我忘记包括实际结果了(请注意:在我的机器上运行需要相当长时间才能热身并达到稳定状态)。

 |       Method |              Mean |           Error |          StdDev |
 |------------- |------------------:|----------------:|----------------:|
 |    SPANSTYLE |         0.4592 ns |       0.0333 ns |       0.0666 ns |
 |    BLOCKCOPY |    15,384.8031 ns |     304.6014 ns |     775.3079 ns |
 |        LINQY |   175,187.7816 ns |   1,119.2713 ns |   1,046.9671 ns |
 | BITCONVERTER | 9,053,750.0355 ns | 330,414.7870 ns | 910,058.2814 ns |

4
Span 速度快的原因是它实际上不会创建数据的副本。Span 只是一个经过美化的指针。所以,你所有的代码所做的就是创建一个指向相同数据的新指针。此外,你的 LINQ 测试是不正确的。仅将 short 强制转换为 byte 将会丢失信息,因为 short 包含 16 位数据而 byte 只有 8 位。 - Daniel Hilgarth
我找到了另一种方法:MemoryMarshal.AsBytes,也许比强制转换快一点。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接