在 C# 中组合两个或多个字节数组的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/415291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to combine two or more byte arrays in C#
提问by Superdumbell
I have 3 byte arrays in C# that I need to combine into one. What would be the most efficient method to complete this task?
我在 C# 中有 3 个字节数组,我需要将它们合并为一个。完成此任务的最有效方法是什么?
采纳答案by Matt Davis
For primitive types (including bytes), use System.Buffer.BlockCopy
instead of System.Array.Copy
. It's faster.
对于原始类型(包括字节),使用System.Buffer.BlockCopy
代替System.Array.Copy
。它更快。
I timed each of the suggested methods in a loop executed 1 million times using 3 arrays of 10 bytes each. Here are the results:
我使用 3 个每个 10 字节的数组对循环中的每个建议方法进行计时 100 万次。结果如下:
- New Byte Array using
System.Array.Copy
- 0.2187556 seconds - New Byte Array using
System.Buffer.BlockCopy
- 0.1406286 seconds - IEnumerable<byte> using C# yield operator - 0.0781270 seconds
- IEnumerable<byte> using LINQ's Concat<> - 0.0781270 seconds
- 新字节数组使用
System.Array.Copy
- 0.2187556 秒 - 新字节数组使用
System.Buffer.BlockCopy
- 0.1406286 秒 - IEnumerable<byte> 使用 C# yield 运算符 - 0.0781270 秒
- IEnumerable<byte> 使用 LINQ 的 Concat<> - 0.0781270 秒
I increased the size of each array to 100 elements and re-ran the test:
我将每个数组的大小增加到 100 个元素并重新运行测试:
- New Byte Array using
System.Array.Copy
- 0.2812554 seconds - New Byte Array using
System.Buffer.BlockCopy
- 0.2500048 seconds - IEnumerable<byte> using C# yield operator - 0.0625012 seconds
- IEnumerable<byte> using LINQ's Concat<> - 0.0781265 seconds
- 新字节数组使用
System.Array.Copy
- 0.2812554 秒 - 新字节数组使用
System.Buffer.BlockCopy
- 0.2500048 秒 - IEnumerable<byte> 使用 C# yield 运算符 - 0.0625012 秒
- IEnumerable<byte> 使用 LINQ 的 Concat<> - 0.0781265 秒
I increased the size of each array to 1000 elements and re-ran the test:
我将每个数组的大小增加到 1000 个元素并重新运行测试:
- New Byte Array using
System.Array.Copy
- 1.0781457 seconds - New Byte Array using
System.Buffer.BlockCopy
- 1.0156445 seconds - IEnumerable<byte> using C# yield operator - 0.0625012 seconds
- IEnumerable<byte> using LINQ's Concat<> - 0.0781265 seconds
- 新字节数组使用
System.Array.Copy
- 1.0781457 秒 - 新字节数组使用
System.Buffer.BlockCopy
- 1.0156445 秒 - IEnumerable<byte> 使用 C# yield 运算符 - 0.0625012 秒
- IEnumerable<byte> 使用 LINQ 的 Concat<> - 0.0781265 秒
Finally, I increased the size of each array to 1 million elements and re-ran the test, executing each loop only4000 times:
最后,我将每个数组的大小增加到 100 万个元素并重新运行测试,每个循环仅执行4000 次:
- New Byte Array using
System.Array.Copy
- 13.4533833 seconds - New Byte Array using
System.Buffer.BlockCopy
- 13.1096267 seconds - IEnumerable<byte> using C# yield operator - 0 seconds
- IEnumerable<byte> using LINQ's Concat<> - 0 seconds
- 新字节数组使用
System.Array.Copy
- 13.4533833 秒 - 新字节数组使用
System.Buffer.BlockCopy
- 13.1096267 秒 - IEnumerable<byte> 使用 C# yield 运算符 - 0 秒
- IEnumerable<byte> 使用 LINQ 的 Concat<> - 0 秒
So, if you need a new byte array, use
因此,如果您需要一个新的字节数组,请使用
byte[] rv = new byte[a1.Length + a2.Length + a3.Length];
System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length);
System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length);
System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length);
But, if you can use an IEnumerable<byte>
, DEFINITELYprefer LINQ's Concat<> method. It's only slightly slower than the C# yield operator, but is more concise and more elegant.
但是,如果您可以使用IEnumerable<byte>
,绝对更喜欢 LINQ 的 Concat<> 方法。它只比 C# yield 运算符慢一点,但更简洁、更优雅。
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
If you have an arbitrary number of arrays and are using .NET 3.5, you can make the System.Buffer.BlockCopy
solution more generic like this:
如果您有任意数量的数组并使用 .NET 3.5,您可以使System.Buffer.BlockCopy
解决方案更通用,如下所示:
private byte[] Combine(params byte[][] arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays) {
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
*Note: The above block requires you adding the following namespace at the the top for it to work.
*注意:上面的块需要您在顶部添加以下命名空间才能工作。
using System.Linq;
To Jon Skeet's point regarding iteration of the subsequent data structures (byte array vs. IEnumerable<byte>), I re-ran the last timing test (1 million elements, 4000 iterations), adding a loop that iterates over the full array with each pass:
对于 Jon Skeet 关于后续数据结构迭代(字节数组与 IEnumerable<byte>)的观点,我重新运行了最后一次计时测试(100 万个元素,4000 次迭代),添加了一个循环,该循环对每个数组进行迭代经过:
- New Byte Array using
System.Array.Copy
- 78.20550510 seconds - New Byte Array using
System.Buffer.BlockCopy
- 77.89261900 seconds - IEnumerable<byte> using C# yield operator - 551.7150161 seconds
- IEnumerable<byte> using LINQ's Concat<> - 448.1804799 seconds
- 新字节数组使用
System.Array.Copy
- 78.20550510 秒 - 使用新字节数组
System.Buffer.BlockCopy
- 77.89261900 秒 - IEnumerable<byte> 使用 C# yield 运算符 - 551.7150161 秒
- IEnumerable<byte> 使用 LINQ 的 Concat<> - 448.1804799 秒
The point is, it is VERYimportant to understand the efficiency of both the creation and the usageof the resulting data structure. Simply focusing on the efficiency of the creation may overlook the inefficiency associated with the usage. Kudos, Jon.
关键是,了解结果数据结构的创建和使用的效率非常重要。仅仅关注创建的效率可能会忽略与使用相关的低效率。荣誉,乔恩。
回答by FryGuy
If you simply need a new byte array, then use the following:
如果您只需要一个新的字节数组,请使用以下命令:
byte[] Combine(byte[] a1, byte[] a2, byte[] a3)
{
byte[] ret = new byte[a1.Length + a2.Length + a3.Length];
Array.Copy(a1, 0, ret, 0, a1.Length);
Array.Copy(a2, 0, ret, a1.Length, a2.Length);
Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length);
return ret;
}
Alternatively, if you just need a single IEnumerable, consider using the C# 2.0 yield operator:
或者,如果您只需要一个 IEnumerable,请考虑使用 C# 2.0 yield 运算符:
IEnumerable<byte> Combine(byte[] a1, byte[] a2, byte[] a3)
{
foreach (byte b in a1)
yield return b;
foreach (byte b in a2)
yield return b;
foreach (byte b in a3)
yield return b;
}
回答by Mark Maxham
Concat is the right answer, but for some reason a handrolled thing is getting the most votes. If you like that answer, perhaps you'd like this more general solution even more:
Concat 是正确的答案,但出于某种原因,手工制作的东西获得了最多的选票。如果你喜欢这个答案,也许你更喜欢这个更通用的解决方案:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
which would let you do things like:
这会让你做这样的事情:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();
回答by Jon Skeet
Many of the answers seem to me to be ignoring the stated requirements:
在我看来,许多答案都忽略了规定的要求:
- The result should be a byte array
- It should be as efficient as possible
- 结果应该是一个字节数组
- 它应该尽可能高效
These two together rule out a LINQ sequence of bytes - anything with yield
is going to make it impossible to get the final size without iterating through the whole sequence.
这两个一起排除了 LINQ 字节序列 - 任何有yield
都会使得在不遍历整个序列的情况下无法获得最终大小。
If those aren't the realrequirements of course, LINQ could be a perfectly good solution (or the IList<T>
implementation). However, I'll assume that Superdumbell knows what he wants.
如果这些当然不是真正的要求,LINQ 可能是一个非常好的解决方案(或IList<T>
实现)。但是,我假设 Superdumbell 知道他想要什么。
(EDIT: I've just had another thought. There's a big semantic difference between making a copy of the arrays and reading them lazily. Consider what happens if you change the data in one of the "source" arrays after calling the Combine
(or whatever) method but before using the result - with lazy evaluation, that change will be visible. With an immediate copy, it won't. Different situations will call for different behaviour - just something to be aware of.)
(编辑:我刚刚有了另一个想法。复制数组和懒惰地读取它们之间存在很大的语义差异。考虑一下如果在调用Combine
(或其他任何) 方法,但在使用结果之前 - 使用延迟评估,该更改将是可见的。使用立即复制,它不会。不同的情况会要求不同的行为 - 只是需要注意的事情。)
Here are my proposed methods - which are very similar to those contained in some of the other answers, certainly :)
这是我提出的方法 - 当然与其他一些答案中包含的方法非常相似:)
public static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] Combine(byte[] first, byte[] second, byte[] third)
{
byte[] ret = new byte[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
Of course the "params" version requires creating an array of the byte arrays first, which introduces extra inefficiency.
当然,“params”版本需要先创建一个字节数组,这会带来额外的低效率。
回答by Andrew
The memorystream class does this job pretty nicely for me. I couldn't get the buffer class to run as fast as memorystream.
memorystream 类对我来说很好地完成了这项工作。我无法让缓冲区类像内存流一样快速运行。
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
回答by edd
public static bool MyConcat<T>(ref T[] base_arr, ref T[] add_arr)
{
try
{
int base_size = base_arr.Length;
int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]);
Array.Resize(ref base_arr, base_size + add_arr.Length);
Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T);
}
catch (IndexOutOfRangeException ioor)
{
MessageBox.Show(ioor.Message);
return false;
}
return true;
}
回答by o_c
Here's a generalization of the answer provided by @Jon Skeet. It is basically the same, only it is usable for any type of array, not only bytes:
这是@Jon Skeet 提供的答案的概括。它基本相同,只是它可用于任何类型的数组,而不仅仅是字节:
public static T[] Combine<T>(T[] first, T[] second)
{
T[] ret = new T[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static T[] Combine<T>(T[] first, T[] second, T[] third)
{
T[] ret = new T[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static T[] Combine<T>(params T[][] arrays)
{
T[] ret = new T[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (T[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
回答by Nate Barbettini
I took Matt's LINQ example one step further for code cleanliness:
为了代码的整洁,我将 Matt 的 LINQ 示例更进一步:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
In my case, the arrays are small, so I'm not concerned about performance.
就我而言,数组很小,所以我不关心性能。
回答by Peter Ertl
public static byte[] Concat(params byte[][] arrays) {
using (var mem = new MemoryStream(arrays.Sum(a => a.Length))) {
foreach (var array in arrays) {
mem.Write(array, 0, array.Length);
}
return mem.ToArray();
}
}
回答by 00jt
I actually ran into some issues with using Concat... (with arrays in the 10-million, it actually crashed).
实际上,我在使用 Concat 时遇到了一些问题...(当数组达到 1000 万时,它实际上崩溃了)。
I found the following to be simple, easy and works well enough without crashing on me, and it works for ANY number of arrays (not just three) (It uses LINQ):
我发现以下内容简单、容易并且运行良好而不会在我身上崩溃,并且它适用于任何数量的数组(不仅仅是三个)(它使用 LINQ):
public static byte[] ConcatByteArrays(params byte[][] arrays)
{
return arrays.SelectMany(x => x).ToArray();
}