Wie einen Wert an beliebiger Stelle in ein Byte-Array schreiben?

  • VB.NET

Es gibt 41 Antworten in diesem Thema. Der letzte Beitrag () ist von sonne75.

    Wie einen Wert an beliebiger Stelle in ein Byte-Array schreiben?

    Hallo,

    ich brauche mal einen Gedankenanstoss:

    Ich habe ein Byte-Array aus 13 Bytes. Insgesamt also 104 Bits. Jetzt muss ich aber an eine beliebige Stelle, z.B. Bit 19, einen Wert, der z.B. 7 Bit lang ist, schreiben.

    Mein erster Gedanke war, ByteArray in eine UInteger-Variable, mit Maske verunden, Value an die richtige Stelle schieben und dann wieder verodern. Bei 4 Bytes (am Anfang war das Array nur 4 Bytes lang) funktioniert es auch. Aber bei 13 kann ich so nicht vorgehen, weil es keine Zahl gibt, die so lang ist.

    Mit BitConverter habe ich es nicht hingekriegt, hat jemand eine Idee? Ich wollte jetzt ungerne in vier 4-Byte-Arrays splitten und da kompliziert den Wert auch noch zwischen 2 Array aufteilen...

    Also gegeben: ein ByteArray(12), offset, size, value.

    Danke.

    sonne75 schrieb:

    z.B. Bit 19, einen Wert, der z.B. 7 Bit lang ist, schreiben.
    Du meinst wahrscheinlich:
    an Bit-Position 19 das 1. von 7 Bits
    an Bit-Position 20 das 2. von 7 Bits
    an Bit-Position 21 das 3. von 7 Bits
    an Bit-Position 22 das 4. von 7 Bits
    an Bit-Position 23 das 5. von 7 Bits
    an Bit-Position 24 das 6. von 7 Bits
    an Bit-Position 25 das 7. von 7 Bits
    schreiben.
    Das sollte doch nun absolut kein Problem sein.
    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!
    Als erstes solltest du den Bit-Index in einen Byte-Index und den Bit-Index innerhalb dieses Bytes umrechnen, dann weißt du, wo du anfangen musst. Die Bits im ersten Byte kannst du gleich setzen, wenn du jetzt schon fertig bist, lässt du alles nachfolgende weg. Dann musst du dir merken, wie viele Bits du noch setzen musst. Du lässt ne Schleife laufen, die solange läuft, bis dieser Wert 0 ist. In der Schleife berechnest du jeweils, wie viele Bits du ins nächste Byte schreiben musst (das sind immer 8 bis auf den letzten Schleifendurchlauf) und setzt dementsprechend viele Bits. Gleichzeitig musst du dir natürlich noch merken, an welcher Position du in den Quell-Bits bist, damit du die richtigen Bits kopieren kannst.
    Ne alternative und ganz andere Ansatzweise wäre die BitArray-Klasse, da brauchst du dann gar keine Bitshifts oder Bitoperatoren, das ist aber um einiges langsamer.

    VB.NET-Quellcode

    1. Private Function PutValueIntoByteArray(ByVal value As UInteger, ByRef Data() As Byte, ByVal offset As UInteger, ByVal size As Integer) As Boolean


    Data() ist 13 Byte lang. Function gibt False, wenn die Werte nicht schlüssig sind (darum kümmere ich mich selbst).
    Mit BitArray gehts sogar gar nicht wirklich einfacher, man kannst auch direkt mit Bitshifts und Bitwiseoperatoren umsetzen. :)

    VB.NET-Quellcode

    1. Private Function PutValueIntoByteArray(value As UInteger, ByRef data As Byte(), offset As Integer, size As Integer) As Boolean
    2. For i = 0 To size - 1
    3. Dim byteIndex = (i + offset) \ 8
    4. Dim bitOffset = (i + offset) Mod 8
    5. Dim bitSet = (value And (1 << i)) <> 0
    6. Dim bit = CByte(1 << bitOffset)
    7. If bitSet Then
    8. data(byteIndex) = data(byteIndex) Or bit
    9. Else
    10. data(byteIndex) = data(byteIndex) And Not bit
    11. End If
    12. Next
    13. End Function
    Allerdings ist dieser Algo bei weitem nicht optimal, weil jedes Bit einzeln gesetzt wird, man könnte jedoch gleich alle in einem Byte zu setzenden Bits gleichzeitig setzen.
    Ach ja und du wirst nen Fehler bekommen, wenn offset und/oder size außerhalb der in data oder value vorhandenen Bits sind, das ist dir aber vermutlich klar.

    sonne75 schrieb:

    wenn die Werte nicht schlüssig sind
    Dann übergib ein Boolean-Feld mit einem Element für jedes Bit, True -> Set, False -> Reset.
    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!

    Artentus schrieb:

    du wirst nen Fehler bekommen, wenn offset und/oder size außerhalb der in data oder value vorhandenen Bits sind

    Wie meinst du das? Was hat "offset" und "size" mit "value" zu tun? Offset und size beziehen sich doch nur auf "Data".

    @RodFromGermany
    Es ist nur eine Sicherheitsmaßnahme, dass keine Exception geworfen wird, da reicht mir ein einzelner False.
    In nem UInt32 sind nur 32 Bit drin, da kann dein Byte-Array noch so groß sein, wenn du für size etwas > 32 eingibst, wird auf jeden Fall ein Fehler geschmissen.
    Wenn offset kleiner 0 ist, dann gibst sowieso nen Fehler, und wenn offset + size größer ist als die Anzahl der Bits in Data, dann gibts auch nen Fehler.
    Ach so, klar, danke, dafür habe ich den Boolean-Rückgabewert.
    Den Algorhithmus habe ich noch nicht genau angeschaut, bin gerade nach Hause gekommen und kann es sowieso nicht testen bis morgen. Aber danke schon mal. :thumbup:

    EDIT: habe durchgeschaut, sieht sehr plausibel aus. Erst Byte-Nummer bestimmen, dann Bit-Nummer in dem Byte, dann Bit extrahieren, dann setzen und wieder von vorne.
    So, habe es getestet, es würde auch funktionieren, wenn ich die Spezifikationen richtig gelesen hätte. Bei uns ist MSB first, d.h. zuerst Datenbyte 3, aber Offset geht von DB3.7 los. Nur Value muss richtig rum sein, d.h. am Offset muss das höchstwertige Bit sein. Deswegen habe ich jetzt statt einem Array mit 13 Byte eine List (Of Byte), weil die Länge variabel ist und ich bei dieser Funktion wissen muss, bei welchem Byte ich anfange. Ich erstelle jedes Mal, wenn ich weiß, wie viele Bytes dieser Kanal hat, die List erneut (beim Initialisieren des Kanals), mit der Anzahl der Elemente, die ich brauche. Kann 1 bis 13 sein.

    VB.NET-Quellcode

    1. For i = 0 To size - 1
    2. Dim byteIndex As Integer = CInt(Data.Count - 1 - (i + offset) \ 8)
    3. Dim bitOffset = CInt(7 - (i + offset) Mod 8)
    4. Dim bitSet = (value And (1 << size - 1 - i)) <> 0
    5. Dim bit = CByte(1 << bitOffset)
    6. If bitSet Then
    7. Data(byteIndex) = Data(byteIndex) Or bit
    8. Else
    9. Data(byteIndex) = Data(byteIndex) And Not bit
    10. End If
    11. Next


    So geht es. Jetzt muss ich den Spieß umdrehen und die Value aus den Datenbytes holen... Bin gerade dran.

    Ah, und Compiler hat gemeckert, dass ich es nach CInt konvertieren muss, da nach \ und Mod ein Long rauskommt.

    sonne75 schrieb:

    da nach \ und Mod ein Long rauskommt.
    Nö.
    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!

    sonne75 schrieb:

    Data.Count ist Integer.
    wohl eher Long, sieh mal nach.
    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!

    RodFromGermany schrieb:

    sieh mal nach.

    Wenn ich was schreibe, dann habe ich vorher nachgeschaut ;)



    EDIT: Und wenn ich sage, dass "\" Long zurückgibt, dann habe ich es auch nachgeschaut ;)


    EDIT2: habe gerade nachgeschaut, ich habe 32-Bit Betriebssystem, daran wird es nicht liegen.
    Aber du hast 64 Bit, wenn ich mich richtig erinnere... Aber kann es daran liegen? Das wäre ja überkreuz und nicht besonders logisch...

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „sonne75“ ()

    sonne75 schrieb:

    Aber du hast 64 Bit
    Nö, es liegt an der breitesten verwendeten Variable.
    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!
    Ok, ich habe aber in meinem Ausdruck nur Integer und UInteger. Habe aber gerade ausprobiert, was passiert, wenn ich UInteger gegen Integer tausche - dann kommt bei "\" Integer statt Long raus. Verstehe einer die Welt... Denn UInteger ist auch 32 Bit.
    erstell dir auf jeden fall (falls du nicht schon hast) eine wasserdichte Testing-Function, und mach viele Tests mit verschiedenen Längen, und falls du mit signed Zahlen arbeitest auch mit Negativa.

    VB-Bitshift ist da tückisch, das schiebt beim rechtsshift das vorzeichen-Bit nach, also aus (SByte) -127 wird nicht 64, sondern -64.