Byte Array an anderes Byte Array anhängen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Byte Array an anderes Byte Array anhängen

    Hi Leute :)

    ich bin grad dabei bisschen mich in Arrays einzuarbeiten, habe bisher fast ausschliesslich mit Listen gearbeitet... (besser spät als nie :) )

    Zwei Fragen:

    (1) Macht es bei der Deklaration einen Unterschied ob man

    Dim MeinArray As Byte() macht oder Dim MeinArray() As Byte

    Das hab ich irgendwo so gelesen, kann mir aber schwer vorstellen, dass das so stimmt.

    Der Compiler scheint jedenfalls nicht zu meckern bei beiden...

    (2) Wie hänge ich ein Byte Array komplett an ein anderes an? Ich habe das hier:

    VB.NET-Quellcode

    1. Private ByteTestArray As Byte() = New Byte() {} 'deklarieren der Arrays
    2. Private Property RecordedData As Byte()
    3. Private Sub ArrayAnhängen(length As Integer)
    4. RecordedData = New Byte(length) {}
    5. '... befüllen von RecordedData...
    6. Array.Resize(ByteTestArray, ByteTestArray.Length + RecordedData.Length) 'Array vergrössern
    7. System.Buffer.BlockCopy(RecordedData, 0, ByteTestArray, ByteTestArray.Length, RecordedData.Length) 'RecordedData an ByteTestArray anhängen
    8. End Sub


    Da bekomme ich diesen Fehler:

    System.ArgumentException: "Offset und Länge für das Array liegen außerhalb des gültigen Bereichs, oder die Anzahl ist größer als die Anzahl der Elemente vom Index bis zum Ende der Quellsammlung."

    Ich verstehe zwar, was dieser Fehler mir sagen will, aber meiner bescheidenen Meinung nach sind Offset und Länge im gültigen Bereich??

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    kafffee schrieb:

    Macht es bei der Deklaration einen Unterschied


    Nein es besteht kein Unterschied. Es ist nur eine Gewöhnungssache beim Lesen.


    kafffee schrieb:

    Wie hänge ich ein Byte Array


    Heute ist es möglich mit Concat zu arbeiten. Das erfüllt genau den Zweck den du möchtest.
    learn.microsoft.com/en-us/dotn…rable.concat?view=net-8.0

    Freundliche Grüsse

    exc-jdbi
    (1)

    kafffee schrieb:

    Macht es bei der Deklaration einen Unterschied
    Nein.
    Ich habe mir aber z.b. angewöhnt uninitialisierte Arrays so zu schreiben Dim a as Integer() und die andere Schreibweise entsprechend nur beim Initialisieren zu nutzen Dim a(5) as Integer, wobei beim Initialisieren das auch notwendig ist (Dim a as Integer(5) meckert der Compiler dagegen an) (Beim ersten ist a Nothing, beim zweiten nicht, deswegen unterscheide ich die Schreibweise)
    Dim a = New Integer() {} lässt sich z.b. auch so schreiben Dim a(-1) as Integer
    In dem Sinne kannst du dir die geschwungenen Klammern sparen wenn du nix reinschreibst und schon die Arraygröße definiert hast.

    (2)
    Das klappt nicht, weil Dim arr(5) as Byte eine Länge von 6 hat. Was du also als Länge angibst, ist gar nicht die Länge, sondern der letzte Index.
    Das ist in c# z.b. anders. Da gibt man tatsächlich die Arraylänge in ihrer Initialisierung an. int[] a = new int[5]; hat Länge 5.
    Bei BlockCopy benutzt du auch den count Parameter falsch, die Methode ist da glaub ich eher ungeeignet, und das Zusammenhängen von Arrays macht auch wenig Sinn zu testen, wenn ein Array leer ist.

    Mit LINQ kriegt man das auch so: a.Concat(b).ToArray
    Ohne LINQ: besser mit Array.Copy()

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Super danke Leute :)

    Haudruferzappeltnoch schrieb:

    Das klappt nicht, weil Dim arr(5) as Byte eine Länge von 6 hat

    Also unterscheidet sich das von .Count bei den Listen....

    Haudruferzappeltnoch schrieb:

    Mit LINQ kriegt man das auch so: a.Concat(b).ToArray
    Ohne LINQ: besser mit Array.Copy()


    Und man kann dabei dann auf das .Resize verzichten?
    Je nachdem, was man vor hat sind auch Memory(Of Byte) (Heap) und Span(Of Byte) (Stack) für solche Vorhaben interessant. Da laufen so Operationen super performant direkt auf dem Speicher und man kann auch z.B. CopyTo benutzen, um Daten rumzubewegen oder direkt mit Indexern zum Setzen arbeiten. Mit Slice kriegt man dann z.B. nur bestimmte Teile raus, die dann direkt an den gegebenen Speicheradressen operieren. Das zugrundeliegende Array kann man dann z.B. auch nicht-verwaltet allokieren + pinnen (in C# sogar mit Pointern im unsafe-Kontext) oder mit einem ArrayPool performant(er) holen.
    Kann aber auch hier overkill sein. Nur FYI.

    Viele Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Trade“ ()

    kafffee schrieb:

    Haudruferzappeltnoch schrieb:

    Das klappt nicht, weil Dim arr(5) as Byte eine Länge von 6 hat

    Also unterscheidet sich das von .Count bei den Listen....
    .Count gibts bei Array nicht - es heisst .Length.
    Und Dim arr(5) as Byte gibts bei Listen nicht. Wenn du eine Liste mit 6 Elementen willst, musste sie hinzufügen.
    Hingegen bei Array kannste dieses wie gezeigt, von vornherein mit 6 Elementen deklarieren. Dabei beachte: Der Platz für die Elemente ist damit deklariert, die Elemente selbst sind aber Nothing.

    Ansonsten sind Listen- wie auch Array-Indicees Nullbasiert, also dings(9) wirft eine IndexOutofRangeException, wenn das Array/die Liste weniger als 10 Elemente hat.
    Und eine Zählschleife muss immer bis .Count/.Length -1 gehen.


    kafffee schrieb:


    Haudruferzappeltnoch schrieb:

    Mit
    LINQ kriegt man das auch so: a.Concat(b).ToArray
    Ohne LINQ: besser mit Array.Copy()


    Und man kann dabei dann auf das .Resize verzichten?

    Jo - mehrere Wege führen nach Rom.
    Bleibt aber immer dasselbe: Wenn du ein Array vergrössern oder verkleinern willst, musste es umkopieren in ein anderes Array (was die gewünschte Größe hat)
    Auch Array.Resize tut intern nix anderes, wenn man Lust hat, kann man mal Performance-Tests schreiben, welche von den verschiedenen Möglichkeiten für welchen Bedarf günstiger ist.

    Übrigens befindet sich in List(Of T) intern auch nix anneres als ein Array, was bei Bedarf vergrössert (also in ein grösseres umkopiert) wird.

    Trade schrieb:

    Das zugrundeliegende Array kann man dann z.B. auch nicht-verwaltet allokieren + pinnen (in C# sogar mit Pointern im unsafe-Kontext) oder mit einem ArrayPool performant(er) holen.
    Kann aber auch hier overkill sein

    Jo Performance ist bei mir glaube ich wichtig. Ich nehme mit der bass.dll raw PCM-Audiodaten in den Arbeitsspeicher auf (die Aufnahmen können mitunter sehr lang werden, dassis dafür gedacht, z.B Vinyl-Schallplatten aufzunehmen), und dann in einzelne Tracks zu zerhacken, und erst dann die einzelnen Tracks auf Platte zu schreiben, um die Festplatte/SSD zu schonen. Was kannst du mir empfehlen? Ich habe folgendes in einer RECORDPROC-Callback-Funktion, die wird während der Aufnahme immer wieder in kurzen Abständen aufgerufen wird und Byte-Chunks (buffer) liefert:

    Würdest du das dann also so lassen, ich konvertiere ja mit Marshal.Copy den verwalteten in unverwalteteten Speicher um?

    VB.NET-Quellcode

    1. Private Function MyRecording(handle As Integer, buffer As IntPtr, length As Integer, user As IntPtr) As Boolean
    2. Dim cont As Boolean = True
    3. If length > 0 AndAlso buffer <> IntPtr.Zero Then
    4. If RecordedData Is Nothing OrElse RecordedData.Length < length Then
    5. RecordedData = New Byte(length) {}
    6. End If
    7. ' copy from managed to unmanaged memory
    8. Marshal.Copy(buffer, RecordedData, 0, length)
    9. BytesWritten += length
    10. RecordedDataComplete = RecordedDataComplete.Concat(RecordedData).ToArray 'anängen der Byte-Chunks (RecordedData) an das Byte-Array mit der kompletten Aufnahme (RecordedDataComplete)
    11. [...]
    12. End Function


    Wenn ich dann RecordedDataComplete auf Platte speichere und anhöre, dann bekomm ich leider bisher bloss Stille, abwechselnd mit Rauschen, das aber in einem festen Muster. Könnt ihr ausschliessen, dass das an obigem Code liegt?


    ErfinderDesRades schrieb:

    Der Platz für die Elemente ist damit deklariert, die Elemente selbst sind aber Nothing

    Okay dassis gut zu wissen.
    @kafffee Du kannst das ganze auch elegant über einen MemoryStream lösen, da kannst Du reinschfreiben was immer Du willst und rausholen, was immer Du willst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    kafffee schrieb:

    Ressourcen/Performance
    habe ich nicht explizit untersucht, aber ich arbeite sehr viel mit MemoryStreams und bin damit sehr zufrieden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    kafffee schrieb:

    ich konvertiere ja mit Marshal.Copy den verwalteten in unverwalteteten Speicher um?

    Nein, hier eher andersrum. Dein IntPtr zeigt auf nicht-verwalteten Speicher und du kopierst ihn woanders hin. Weiß nicht, was RecordedData ist.

    Es kommt halt drauf an, was du machen willst. Bytes im unverwalteten Speicher zu halten bringt dir halt nur dann was, wenn du darauf dann Operationen ausführst, die schneller laufen sollen. Da du dann die Mechanismen der CLR zum Aufräumen und Rumschieben von Speicher umgehst und Overhead beim Abrufen einsparst. Nur reines Hin- und Herkopieren ist redundant und kannst du dann auch gleich komplett verwaltet machen.

    Viele Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    kafffee schrieb:

    Wenn ich dann RecordedDataComplete auf Platte speichere und anhöre, dann bekomm ich leider bisher bloss Stille, abwechselnd mit Rauschen, das aber in einem festen Muster. Könnt ihr ausschliessen, dass das an obigem Code liegt?

    Du speicherst RecordedDataComplete halt nur plain. Was solls denn sein/werden? WAV, MP3? Wenn es eine WAV werden soll, dann musst Du vor den eigentlichen Daten (RecordedDataComplete) einen entsprechenden WaveHeader schreiben.
    Mfg -Franky-
    @-Franky-

    Ja ich weiss. Ich hab das mit meiner DAW angehört, der kannst du sagen: Okay behandle das wie eine .wav aber die Audiodaten fangen bei Index 0 an.

    Ich hab das aber auch mit Hilfe der bass.dll auch schon in mp3 konvertiert, das hat sich dann genauso angehört.

    Der Ian von Un4Seen hat das Problem aber gerade gelöst:

    VB.NET-Quellcode

    1. ReDim Preserve RecordedData(BytesWritten + length - 1)
    2. Marshal.Copy(buffer, RecordedData, BytesWritten, length)
    3. BytesWritten += length


    Und RecordedDataComplete hab ich rausgemschmissen und anstattdessen den mp3 Encoder mit RecordedData gefüttert.

    Keine Ahnung warum das geht und das in Post #7 nicht...
    Hi

    Ich würde das Array nicht ständig neu Redimensionieren wenn Daten ankommen. Redimensioniere das Array nur dann wenn BytesWritten + lenght > als das Array ist und dann gleich um zb 10000 Bytes (oder mehr/weniger) vergrößern. Am Ende kürzt Du das Array auf die tatsächliche Größe von BytesWritten.

    Da müsstest mal einen Testlauf machen was da schneller ist. Zum einem mit Redim Preserve DeinArray und Array.Resize(DeinArray, NeueGröße) und ständig neu Redimensionieren wenn Daten ankommen oder nur dann Redimensionieren wenn die Daten nicht mehr in das Array passen würden.
    Mfg -Franky-
    @kafffee Ich würde erst mal dafür sorgen, dass es funktioniert.
    Wenn es funktioniert, kannst Du es optimieren.
    Teste alle diese Vorschläge durch und nimm dann den, der Dir am besten gefällt.
    Pderformance und SSD sind zunächst zweitrangig.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!