programing

C#에서 두 개 이상의 바이트 배열을 결합하는 가장 좋은 방법

padding 2023. 5. 23. 21:38
반응형

C#에서 두 개 이상의 바이트 배열을 결합하는 가장 좋은 방법

C#에 있는 3바이트 배열을 하나로 결합해야 합니다.이 작업을 완료하는 가장 효율적인 방법은 무엇입니까?

기본 유형(바이트 포함)의 경우 를 대신 사용합니다. 더 빠릅니다.

저는 각각 10바이트씩 3개의 배열을 사용하여 100만 번 실행된 루프에서 각각의 제안된 방법의 시간을 측정했습니다.결과는 다음과 같습니다.

  1. 다음을 사용하여 새 바이트 배열 만들기System.Array.Copy.2187556ppm
  2. 다음을 사용하여 새 바이트 배열 만들기System.Buffer.BlockCopy.1406286ppm
  3. C# 항복 연산자를 사용한 IEnumberable <byte> - 0.0781270초
  4. LINQ의 Concat을 사용한 IEnumberable<byte> - 0.0781270초

각 어레이의 크기를 100개 요소로 늘리고 테스트를 다시 실행했습니다.

  1. 다음을 사용하여 새 바이트 배열 만들기System.Array.Copy.2812554초
  2. 다음을 사용하여 새 바이트 배열 만들기System.Buffer.BlockCopy.2500048초
  3. C# 항복 연산자를 사용한 IEnumberable <byte> - 0.0625012초
  4. LINQ의 Concat을 사용한 IEnumberable<byte> - 0.0781265초

각 어레이의 크기를 1000개 요소로 늘리고 테스트를 다시 실행했습니다.

  1. 다음을 사용하여 새 바이트 배열 만들기System.Array.Copy.0781457㎜
  2. 다음을 사용하여 새 바이트 배열 만들기System.Buffer.BlockCopy.0156445ppm
  3. C# 항복 연산자를 사용한 IEnumberable <byte> - 0.0625012초
  4. LINQ의 Concat을 사용한 IEnumberable<byte> - 0.0781265초

마지막으로 각 어레이의 크기를 1백만 개의 요소로 늘리고 테스트를 다시 실행하여 각 루프를 4,000번만 실행했습니다.

  1. 다음을 사용하여 새 바이트 배열 만들기System.Array.Copy.45333mx
  2. 다음을 사용하여 새 바이트 배열 만들기System.Buffer.BlockCopy.1096267초
  3. C# 양보 연산자를 사용한 IEnumberable <byte> - 0초
  4. LINQ의 Concat을 사용한 IEnumberable<byte> - 0초

새 바이트 배열이 필요한 경우

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# 수율 연산자보다 약간 느리지만 보다 간결하고 우아합니다.

IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);

. 3사용하는 에는 .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;

후속 데이터 구조(바이트 배열 대IEnumberable<byte>), 마지막 타이밍 테스트(1백만 개 요소, 4,000번 반복)를 다시 실행하여 각 패스로 전체 어레이에서 반복되는 루프를 추가했습니다.

  1. 다음을 사용하여 새 바이트 배열 만들기System.Array.Copy.20550510초
  2. 다음을 사용하여 새 바이트 배열 만들기System.Buffer.BlockCopy.89261900초
  3. C# 항복 연산자를 사용한 IEnumberable <byte> - 551.7150161초
  4. LINQ의 Concat을 사용한 IEnumberable<byte> - 448.1804799초

중요한 것은 데이터 구조의 생성과 사용의 효율성을 이해하는 것이 매우 중요하다는 것입니다.단순히 창작의 효율성에 초점을 맞추는 것만으로도 사용과 관련된 비효율성을 간과할 수 있습니다.칭찬해, 존.

제가 보기에 많은 답변들이 명시된 요구 사항을 무시하고 있는 것 같습니다.

  • 결과는 바이트 배열이어야 합니다.
  • 가능한 한 효율적이어야 합니다.

두 합니다. 즉, "LINQ"가 있는 모든 는 제외됩니다.yield전체 시퀀스를 반복하지 않고는 최종 크기를 얻는 것이 불가능합니다.

물론 이러한 요구사항이 실제 요구사항이 아니라면 LINQ는 완벽하게 좋은 솔루션이 될 수 있습니다.IList<T>을 원하는지 있다고 하지만 슈퍼덤벨은 그가 무엇을 원하는지 알고 있다고 생각합니다.

(편집: 저는 방금 다른 생각을 했습니다.배열의 복사본을 만드는 것과 게으르게 읽는 것 사이에는 큰 의미적 차이가 있습니다." 배열 중 에서 "" 후 생각해 .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" 버전은 바이트 배열을 먼저 생성해야 하므로 비효율적입니다.

코드 청결을 위해 Matt의 LINQ 예제를 한 단계 더 살펴봤습니다.

byte[] rv = a1.Concat(a2).Concat(a3).ToArray();

저의 경우 어레이가 작기 때문에 성능에 대해 걱정하지 않습니다.

새 바이트 배열이 필요한 경우 다음을 사용합니다.

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;
}

또는 단일 IE 번호가 필요한 경우 C# 2.0 수율 연산자를 사용하는 것이 좋습니다.

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;
}

사실 Concat을 사용할 때 몇 가지 문제가 발생했습니다.(1,000만 개의 어레이를 보유한 상태에서 실제로 충돌했습니다.)

다음은 간단하고 간편하며 충돌 없이 충분히 작동하며, 3개가 아닌 모든 수의 어레이에서 작동합니다(LINQ 사용).

public static byte[] ConcatByteArrays(params byte[][]  arrays)
{
    return arrays.SelectMany(x => x).ToArray();
}

메모리 스트림 수업은 저에게 이 일을 꽤 잘 해줍니다.버퍼 클래스를 메모리 스트림만큼 빠르게 실행할 수 없습니다.

using (MemoryStream ms = new MemoryStream())
{
  ms.Write(BitConverter.GetBytes(22),0,4);
  ms.Write(BitConverter.GetBytes(44),0,4);
  ms.ToArray();
}
    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();
        }
    }
    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;
    }

제네릭을 사용하여 배열을 결합할 수 있습니다.다음 코드는 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;
    }
    /// <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();

다음은 @John 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;
}

바이트 배열 목록을 전달하기만 하면 이 함수는 바이트 배열(병합)을 반환합니다.이것이 제가 생각하는 최고의 해결책입니다 :).

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();
            }
        }

콩캣이 정답이지만, 어떤 이유에서인지 가장 많은 표를 얻는 것은 조작된 것입니다.이러한 답변이 마음에 든다면 더욱 일반적인 솔루션을 원할 것입니다.

    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();

언급URL : https://stackoverflow.com/questions/415291/best-way-to-combine-two-or-more-byte-arrays-in-c-sharp

반응형