Die beste Möglichkeit, zwei oder mehr Byte-Arrays in C # zu kombinieren

Ich habe 3 Byte-Arrays in C #, die ich zu einem kombinieren muss. Was wäre die effizienteste Methode, um diese Aufgabe zu erledigen?

Solutions Collecting From Web of "Die beste Möglichkeit, zwei oder mehr Byte-Arrays in C # zu kombinieren"

Verwenden System.Buffer.BlockCopy für primitive Typen (einschließlich Byte) System.Buffer.BlockCopy anstelle von System.Array.Copy . Es ist schneller.

Ich habe jede der vorgeschlagenen Methoden in einer Schleife, die 1 Million mal ausgeführt wurde, unter Verwendung von 3 Feldern von jeweils 10 Bytes zeitlich abgestimmt. Hier sind die Ergebnisse:

  1. Neues Byte-Array mit System.Array.Copy – 0,2187556 Sekunden
  2. Neues Byte-Array mit System.Buffer.BlockCopy – 0.1406286 Sekunden
  3. IEnumerable mit C # yield operator – 0,0781270 Sekunden
  4. IEnumerable mit LINQs Concat <> – 0,0781270 Sekunden

Ich habe die Größe jedes Arrays auf 100 Elemente erhöht und den Test erneut durchgeführt:

  1. Neues Byte-Array mit System.Array.Copy – 0,2812554 Sekunden
  2. Neues Byte-Array mit System.Buffer.BlockCopy – 0,2500048 Sekunden
  3. IEnumerable mit C # yield operator – 0,0625012 Sekunden
  4. IEnumerable mit LINQs Concat <> – 0,0781265 Sekunden

Ich habe die Größe jedes Arrays auf 1000 Elemente erhöht und den Test erneut durchgeführt:

  1. Neues Byte-Array mit System.Array.Copy – 1.0781457 Sekunden
  2. Neues Byte-Array mit System.Buffer.BlockCopy – 1.0156445 Sekunden
  3. IEnumerable mit C # yield operator – 0,0625012 Sekunden
  4. IEnumerable mit LINQs Concat <> – 0,0781265 Sekunden

Schließlich habe ich die Größe jedes Arrays auf 1 Million Elemente erhöht und den Test erneut ausgeführt, wobei jede Schleife nur 4000 Mal ausgeführt wurde:

  1. Neues Byte-Array mit System.Array.Copy – 13.4533833 Sekunden
  2. Neues Byte-Array mit System.Buffer.BlockCopy – 13.1096267 Sekunden
  3. IEnumerable mit C # yield operator – 0 Sekunden
  4. IEnumerable mit LINQ’s Concat <> – 0 Sekunden

Wenn Sie also ein neues Byte-Array benötigen, verwenden Sie

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

Wenn Sie jedoch ein IEnumerable , bevorzugen Sie definitiv die LINQ-Methode “Concat <>“. Es ist nur geringfügig langsamer als der C # Yield-Operator, ist aber prägnanter und eleganter.

 IEnumerable rv = a1.Concat(a2).Concat(a3); 

Wenn Sie über eine beliebige Anzahl von Arrays verfügen und .NET 3.5 verwenden, können Sie die System.Buffer.BlockCopy Lösung wie System.Buffer.BlockCopy generischer gestalten:

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

* Hinweis: Für den obigen Block müssen Sie den folgenden Namespace oben hinzufügen, damit er funktioniert.

 using System.Linq; 

Zu Jon Skeets Frage bezüglich der Iteration der nachfolgenden Datenstrukturen (Byte-Array vs. IEnumerable ) habe ich den letzten Timing-Test (1 Million Elemente, 4000 Iterationen) wiederholt und eine Schleife hinzugefügt, die mit jedem über das gesamte Array iteriert bestehen:

  1. Neues Byte-Array mit System.Array.Copy – 78.20550510 Sekunden
  2. Neues Byte-Array mit System.Buffer.BlockCopy – 77.89261900 Sekunden
  3. IEnumerable mit C # yield operator – 551.7150161 Sekunden
  4. IEnumerable mit LINQs Concat <> – 448.1804799 Sekunden

Der Punkt ist, es ist SEHR wichtig, die Effizienz sowohl der Erstellung als auch der Verwendung der resultierenden Datenstruktur zu verstehen. Sich einfach auf die Effizienz der Erstellung zu konzentrieren, kann die mit der Verwendung verbundene Ineffizienz übersehen. Kudos, Jon.

Viele der Antworten scheinen mir die genannten Anforderungen zu ignorieren:

  • Das Ergebnis sollte ein Byte-Array sein
  • Es sollte so effizient wie möglich sein

Diese beiden schließen zusammen eine LINQ-Sequenz von Bytes aus – alles mit einer yield wird es unmöglich machen, die endgültige Größe zu erhalten, ohne die gesamte Sequenz durchlaufen zu müssen.

Wenn das natürlich nicht die wirklichen Anforderungen sind, könnte LINQ eine perfekte Lösung sein (oder die IList Implementierung). Ich nehme jedoch an, dass Superdumbell weiß, was er will.

(EDIT: Ich habe gerade einen anderen Gedanken gehabt. Es gibt einen großen semantischen Unterschied zwischen dem Erstellen einer Kopie der Arrays und dem langsamen Lesen. Überlegen Sie, was passiert, wenn Sie die Daten in einem der “Source” -Arrays ändern, nachdem Sie das Combine was auch immer) Methode, aber vor der Verwendung des Ergebnisses – mit Lazy Evaluation, wird diese Änderung sichtbar sein. Mit einer sofortigen Kopie wird es nicht. Unterschiedliche Situationen erfordern ein anderes Verhalten – nur etwas zu beachten.)

Hier sind meine vorgeschlagenen Methoden – die denen, die in einigen der anderen Antworten enthalten sind, sicherlich sehr ähnlich sind 🙂

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

Natürlich erfordert die “Params” -Version das Erstellen eines Arrays der Byte-Arrays zuerst, was eine zusätzliche Ineffizienz einführt.

Ich habe Matts LINQ-Beispiel einen Schritt weiter für Code-Sauberkeit genommen:

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

In meinem Fall sind die Arrays klein, daher mache ich mir keine Gedanken über die performance.

Wenn Sie einfach ein neues Byte-Array benötigen, verwenden Sie Folgendes:

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

Wenn Sie nur einen einzelnen IEnumerable benötigen, sollten Sie alternativ den C # 2.0-Yield-Operator verwenden:

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

Ich habe tatsächlich einige Probleme mit der Verwendung von Concat … (mit Arrays in der 10-Millionen, es ist tatsächlich abgestürzt).

Ich fand das Folgende einfach, einfach und funktioniert gut genug, ohne auf mich zu stürzen, und es funktioniert für jede Anzahl von Arrays (nicht nur drei) (Es verwendet LINQ):

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

Die MemoryStream-class erledigt diese Aufgabe ziemlich gut für mich. Ich konnte die Pufferklasse nicht so schnell wie MemoryStream ausführen.

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

Hier ist eine Verallgemeinerung der Antwort von @ Jon Skeet. Es ist im Grunde das Gleiche, nur dass es für jede Art von Array verwendbar ist, nicht nur für Bytes:

 public static T[] Combine(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[] 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(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; } 

Kann Generics verwenden, um Arrays zu kombinieren. Der folgende Code kann leicht auf drei Arrays erweitert werden. Auf diese Weise müssen Sie niemals Code für verschiedene Arten von Arrays duplizieren. Einige der obigen Antworten scheinen mir zu komplex zu sein.

 private static T[] CombineTwoArrays(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; } 

Alles, was Sie brauchen, um die Liste der Byte-Arrays zu übergeben, und diese function wird Ihnen das Array der Bytes (zusammengeführt) zurückgeben. Dies ist die beste Lösung, die ich denke :).

 public static byte[] CombineMultipleByteArrays(List 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 ist die richtige Antwort, aber aus irgendeinem Grund bekommt ein handgerolltes Ding die meisten Stimmen. Wenn Ihnen diese Antwort gefällt, möchten Sie vielleicht diese allgemeinere Lösung noch mehr:

  IEnumerable Combine(params byte[][] arrays) { foreach (byte[] a in arrays) foreach (byte b in a) yield return b; } 

was dir Dinge wie:

  byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();