C #에는 3 바이트 배열이 있는데 하나로 결합해야합니다. 이 작업을 완료하는 가장 효율적인 방법은 무엇입니까?
C #에는 3 바이트 배열이 있는데 하나로 결합해야합니다. 이 작업을 완료하는 가장 효율적인 방법은 무엇입니까?
답변:
기본 유형 (바이트 포함)의 경우을 System.Buffer.BlockCopy
대신 사용하십시오 System.Array.Copy
. 더 빠릅니다.
루프에서 제안 된 각 메소드의 시간을 각각 10 바이트의 3 개의 배열을 사용하여 백만 번 실행했습니다. 결과는 다음과 같습니다.
System.Array.Copy
-0.2187556 초를 사용하는 새로운 바이트 배열System.Buffer.BlockCopy
-0.1406286 초를 사용하는 새로운 바이트 배열각 배열의 크기를 100 개 요소로 늘리고 테스트를 다시 실행했습니다.
System.Array.Copy
-0.2812554 초를 사용하는 새로운 바이트 배열System.Buffer.BlockCopy
-0.2500048 초를 사용하는 새로운 바이트 배열각 배열의 크기를 1000 요소로 늘리고 테스트를 다시 실행했습니다.
System.Array.Copy
-1.0781457 초를 사용하는 새로운 바이트 배열System.Buffer.BlockCopy
-1.0156445 초를 사용하는 새로운 바이트 배열마지막으로, 1 개 백만 요소 각 어레이의 크기를 증가시키고 각각의 루프를 실행하는 시험을 다시 실행 단지 4000 시간 :
System.Array.Copy
-13.4533833 초를 사용하는 새로운 바이트 배열System.Buffer.BlockCopy
-13.1096267 초를 사용하는 새로운 바이트 배열따라서 새로운 바이트 배열이 필요하면
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);
당신은을 사용할 수 있다면, IEnumerable<byte>
, 확실히했던 LINQ의 CONCAT <> 방법을 선호합니다. C # yield 연산자보다 약간 느리지 만 더 간결하고 우아합니다.
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
임의의 수의 배열이 있고 .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;
}
* 참고 : 위의 블록을 사용하려면 맨 위에 다음 네임 스페이스를 추가해야합니다.
using System.Linq;
후속 데이터 구조 (바이트 배열 대 IEnumerable <byte>)의 반복에 관한 Jon Skeet의 요점에 대해, 마지막 타이밍 테스트 (1 백만 요소, 4000 반복)를 다시 실행하여 각 배열마다 전체 배열을 반복하는 루프를 추가합니다 통과하다:
System.Array.Copy
-78.20550510 초를 사용하는 새로운 바이트 배열System.Buffer.BlockCopy
-77.89261900 초를 사용하는 새로운 바이트 배열요점은 결과 데이터 구조 의 생성 및 사용 의 효율성을 이해하는 것이 매우 중요하다는 것입니다 . 창작의 효율성에 집중하는 것만으로도 사용과 관련된 비 효율성을 간과 할 수 있습니다. 쿠도스, 존
많은 답변이 명시된 요구 사항을 무시하는 것 같습니다.
이 두 가지가 함께 LINQ 바이트 시퀀스를 배제합니다-모든 yield
것은 전체 시퀀스를 반복하지 않고 최종 크기를 얻는 것을 불가능하게 만듭니다.
이것이 실제 요구 사항 이 아닌 경우 LINQ는 완벽한 솔루션 (또는 IList<T>
구현) 일 수 있습니다. 그러나 Superdumbell이 자신이 원하는 것을 알고 있다고 가정합니다.
(편집 :.. 난 그냥 당신이를 호출 한 후 "소스"배열 중 하나의 데이터를 변경하면 어떻게되는지 생각해 배열의 복사본을 만들고 유유히을 읽는 사이에 큰 의미 차이가있어 다른 생각을 했어 Combine
(또는 무엇이든 ) 방법이지만 결과를 사용하기 전에-게으른 평가로 변경 사항을 볼 수 있습니다. 즉시 사본을 사용하면 변경되지 않습니다. 상황에 따라 다른 행동이 필요합니다.
여기에 내가 제안한 방법이 있습니다-다른 답변 중 일부에 포함 된 것과 매우 유사합니다. :)
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;
}
물론 "params"버전은 바이트 배열을 먼저 생성해야하므로 추가적인 비효율이 발생합니다.
단순히 새로운 바이트 배열이 필요한 경우 다음을 사용하십시오.
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;
}
또는 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;
}
메모리 스트림 클래스는이 작업을 꽤 잘 수행합니다. 버퍼 스트림을 메모리 스트림만큼 빠르게 실행할 수 없었습니다.
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
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;
}
where T : struct
) 시도 할 수는 있지만 CLR 내부 전문가가 아닌 경우-특정 구조체에서 예외가 발생할 수 있는지 여부를 알 수 없습니다 (예 : 참조 유형 필드를 포함하는 경우)
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();
}
}
제네릭을 사용하여 배열을 결합 할 수 있습니다. 다음 코드는 3 개의 배열로 쉽게 확장 할 수 있습니다. 이렇게하면 다른 유형의 배열에 대해 코드를 복제 할 필요가 없습니다. 위의 답변 중 일부는 나에게 너무 복잡해 보입니다.
private static T[] CombineTwoArrays<T>(T[] a1, T[] a2)
{
T[] arrayCombined = new T[a1.Length + a2.Length];
Array.Copy(a1, 0, arrayCombined, 0, a1.Length);
Array.Copy(a2, 0, arrayCombined, a1.Length, a2.Length);
return arrayCombined;
}
@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;
}
sizeof(...)
복사하려는 요소 수로 곱하지만 sizeof는 일반 유형과 함께 사용할 수 없습니다. 일부 유형의 경우을 사용할 수 Marshal.SizeOf(typeof(T))
있지만 특정 유형 (예 : 문자열)의 런타임 오류가 발생합니다. CLR 유형의 내부 작동에 대해 더 철저한 지식을 가진 사람은 여기에서 가능한 모든 함정을 지적 할 수 있습니다. [BlockCopy를 사용하여] 일반 배열 연결 방법을 작성하는 것은 쉬운 일이 아닙니다.
/// <summary>
/// Combine two Arrays with offset and count
/// </summary>
/// <param name="src1"></param>
/// <param name="offset1"></param>
/// <param name="count1"></param>
/// <param name="src2"></param>
/// <param name="offset2"></param>
/// <param name="count2"></param>
/// <returns></returns>
public static T[] Combine<T>(this T[] src1, int offset1, int count1, T[] src2, int offset2, int count2)
=> Enumerable.Range(0, count1 + count2).Select(a => (a < count1) ? src1[offset1 + a] : src2[offset2 + a - count1]).ToArray();
바이트 배열 목록을 전달하면이 함수는 바이트 배열 (병합)을 반환합니다. 이것은 내가 생각하는 최고의 솔루션입니다 :).
public static byte[] CombineMultipleByteArrays(List<byte[]> lstByteArray)
{
using (var ms = new MemoryStream())
{
using (var doc = new iTextSharp.text.Document())
{
using (var copy = new PdfSmartCopy(doc, ms))
{
doc.Open();
foreach (var p in lstByteArray)
{
using (var reader = new PdfReader(p))
{
copy.AddDocument(reader);
}
}
doc.Close();
}
}
return ms.ToArray();
}
}
Concat이 정답이지만 어떤 이유로 든 수수께끼가 많은 사람들이 가장 많은 표를 얻고 있습니다. 당신이 그 대답을 좋아한다면, 당신은 아마도 이보다 일반적인 해결책을 원할 것입니다 :
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
다음과 같은 작업을 수행 할 수 있습니다.
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();