Thomas123: Ingeter/float aus Byte Array

Hallo,
ich versuche gerade mit meinem geringen php Kenntnissen aus einem Byte Array einen Integer bzw. float zusammenzubauen. Jedoch bekomme ich das nicht richtig hin.

Beispieldaten:

  
$data = Array(0xff, 0xff, 0x30, 0x39);  

Das sollen mal meine Rohdaten sein. Aus $data[0] und $data[1] würde ich ganz gerne -1 erhalten, aus $data[2] und $data[3] gerne 12345.

Jetzt habe ich eine Funktion (ganz C-like):

  
function toSInt16($lo, $hi)  
{  
	return (($lo << 8) | $hi);  
}  

welche aber nicht das Vorzeichen beachtet.
Ich habe es schon mit diversen pack/unpack versucht, komme aber nicht so recht weiter.
So in dem Prinzip brauche ich auch eine Konvertierung in einen 4-Byte float.

Vielleicht kann mir einer einen Tip geben.

Danke und Gruß
Thomas

  1. Hello,

    ich versuche gerade mit meinem geringen php Kenntnissen aus einem Byte Array einen Integer bzw. float zusammenzubauen. Jedoch bekomme ich das nicht richtig hin.

    Beispieldaten:

    $data = Array(0xff, 0xff, 0x30, 0x39);

    
    > Das sollen mal meine Rohdaten sein. Aus $data[0] und $data[1] würde ich ganz gerne -1 erhalten, aus $data[2] und $data[3] gerne 12345.  
    >   
    > Jetzt habe ich eine Funktion (ganz C-like):  
    >   
    > ~~~php
      
    
    > function toSInt16($lo, $hi)  
    > {  
    > 	return (($lo << 8) | $hi);  
    > }  
    > 
    
    

    welche aber nicht das Vorzeichen beachtet.
    Ich habe es schon mit diversen pack/unpack versucht, komme aber nicht so recht weiter.
    So in dem Prinzip brauche ich auch eine Konvertierung in einen 4-Byte float.

    zeig doch bitte Deinen ganzen bisherigen Code, bitte mit guten Kommentaren dazu, was Du Dir dabei gedacht hast und was dabei wirklich herausgekommen ist.

    Wenn Du es nicht nachher tatsächlich ber Bitoperatoren erledigen willst, dann ist die Unpack()-Funktion aber schon der richtige Weg dafür.

    Dazu müssen die Byte-Werte in einem String angeordnet werden. Dieser kann dann mittels unpack() behandelt werden.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. zeig doch bitte Deinen ganzen bisherigen Code, bitte mit guten Kommentaren dazu, was Du Dir dabei gedacht hast und was dabei wirklich herausgekommen ist.

      Wenn Du es nicht nachher tatsächlich ber Bitoperatoren erledigen willst, dann ist die Unpack()-Funktion aber schon der richtige Weg dafür.

      Dazu müssen die Byte-Werte in einem String angeordnet werden. Dieser kann dann mittels unpack() behandelt werden.

      Hallo Tom,
      so viel kompletten Code habe ich noch nicht.
      Aber ich bin deinem Tip mit unpack() nochmal gefolgt, und für einen signed int funktionert es jetzt zumindest

        
      /* Rohdaten */  
      $data = Array(0xff, 0xff, 0x30, 0x39);  
        
      /* Im Array $data ab Position 0 */  
      $x = toSint16($data, 0);  
      echo "x= ". $x . "\n";  
        
      /* Im Array $data ab Position 2 */  
      $x = toSint16($data, 2);  
      echo "x= ". $x . "\n";  
        
      function toSint16($arr, $pos)  
      {  
      	$val = unpack("s", chr($arr[$pos + 1]) . chr($arr[$pos]));  
      	return $val[1];	  
      }  
      
      

      Muss mal sehen ob ich meine anderen Datentypen mit diesem Prinzip auch erschlagen bekomme, und ob das ganze maschinenunabhänging ist.

      Am liebsten hätte ich natürlich auch nur ein Funktionsparameter wie toSint16($data[2]).

      Gruß
      Thomas

      1. Hello,

        zeig doch bitte Deinen ganzen bisherigen Code, bitte mit guten Kommentaren dazu, was Du Dir dabei gedacht hast und was dabei wirklich herausgekommen ist.

        Wenn Du es nicht nachher tatsächlich ber Bitoperatoren erledigen willst, dann ist die Unpack()-Funktion aber schon der richtige Weg dafür.

        Dazu müssen die Byte-Werte in einem String angeordnet werden. Dieser kann dann mittels unpack() behandelt werden.

        Hallo Tom,
        so viel kompletten Code habe ich noch nicht.
        Aber ich bin deinem Tip mit unpack() nochmal gefolgt, und für einen signed int funktionert es jetzt zumindest

        /* Rohdaten */
        $data = Array(0xff, 0xff, 0x30, 0x39);

        /* Im Array $data ab Position 0 */
        $x = toSint16($data, 0);
        echo "x= ". $x . "\n";

        /* Im Array $data ab Position 2 */
        $x = toSint16($data, 2);
        echo "x= ". $x . "\n";

        function toSint16($arr, $pos)
        {
        $val = unpack("s", chr($arr[$pos + 1]) . chr($arr[$pos]));
        return $val[1];
        }

        
        >   
        > Muss mal sehen ob ich meine anderen Datentypen mit diesem Prinzip auch erschlagen bekomme, und ob das ganze maschinenunabhänging ist.  
          
        Ich verstehe nicht ganz, woher Du die Daten bekommst und warum Du sie dann erst in einem Array parken musst. PHP speichert die numerischen Werte, so wie Du sie ins Array speicherst, als Integer mit üblicherweise 4 Byte.  
          
        Wie das nun bei der 64-Bit-Version ist, kann ich nicht beantworten.  
          
        Du willst aber augenscheinlich Lowbyte und Highbyte separat erfassen. Dann solltest Du diese in eine Byte-(Zeichen-)Kette schreiben, bei PHP auch als Single-Byte-String bezeichnet.  
          
        Solche Dinge, wie Big-Endian, Little-Endian usw. musst Du dann entsprechned beachten.  
          
          
          
          
          
          
          
          
        Liebe Grüße aus dem schönen Oberharz  
          
          
        Tom vom Berg  
        ![](http://selfhtml.bitworks.de/Virencheck.gif)  
          
        
        -- 
         ☻\_  
        /▌  
        / \ Nur selber lernen macht schlau  
        <http://bergpost.annerschbarrich.de>
        
        1. Ich verstehe nicht ganz, woher Du die Daten bekommst und warum Du sie dann erst in einem Array parken musst. PHP speichert die numerischen Werte, so wie Du sie ins Array speicherst, als Integer mit üblicherweise 4 Byte.

          Die Daten lese ich über socket_read() von einem Gerät ein. Gut, da bekomme ich einen String zurück den ich zuerst in ein Array konvertiere und zurückgebe.
          Ich dachte mir, Binärdaten gehören in ein Array und nicht in einen String.

          Thomas

          1. Moin!

            Die Daten lese ich über socket_read() von einem Gerät ein. Gut, da bekomme ich einen String zurück den ich zuerst in ein Array konvertiere und zurückgebe.
            Ich dachte mir, Binärdaten gehören in ein Array und nicht in einen String.

            Du machst aus dem String ein Array mit Byte-Integerwerten.

            Und dann machst du in deiner Funktion toSint() aus dem Arraywert wieder einen String, auf den du dann unpack() anwendest. Klingt irgendwie nach einem Irrweg, oder? Zumal ein Array von Integerwerten deutlich mehr Speicher verbraucht, als ein String. Wenn die gelesene Datenmenge also hinreichend groß ist, kriegst du damit noch ganz andere Probleme.

            Andererseits: unpack zerlegt dir auf Wunsch auch mehr als ein Byte eines Strings in Integerwerte. Effektiv könntest du, sofern sich die eingelesenen Daten dazu eignen, alle Bytes direkt in Integer wandeln.

            - Sven Rautenberg

            1. Du machst aus dem String ein Array mit Byte-Integerwerten.

              Und dann machst du in deiner Funktion toSint() aus dem Arraywert wieder einen String, auf den du dann unpack() anwendest. Klingt irgendwie nach einem Irrweg, oder? Zumal ein Array von Integerwerten deutlich mehr Speicher verbraucht, als ein String. Wenn die gelesene Datenmenge also hinreichend groß ist, kriegst du damit noch ganz andere Probleme.

              Nun, socket_read() gibt unter php warum auch immer einen String zurück. Die rohe recv() Socket Betriebssystemfunktion schreibt meine Daten in einen "char* buf", was in C üblicherweise ein char-Array ist.

              Es kann später auch sein, dass ich aus dem Datenstrom auf ein einzelnes Byte zugreifen möchte (quasi ein int8_t). Das müsste dann auch mittels Ord() in den entsprechenden Wert gewandelt werden.

              Ich denke aber nochmal drüber nach.

              Andererseits: unpack zerlegt dir auf Wunsch auch mehr als ein Byte eines Strings in Integerwerte. Effektiv könntest du, sofern sich die eingelesenen Daten dazu eignen, alle Bytes direkt in Integer wandeln.

              Da muss ich mal schauen wie das geht.

              Gruß
              Thomas

              1. Moin!

                Du machst aus dem String ein Array mit Byte-Integerwerten.

                Und dann machst du in deiner Funktion toSint() aus dem Arraywert wieder einen String, auf den du dann unpack() anwendest. Klingt irgendwie nach einem Irrweg, oder? Zumal ein Array von Integerwerten deutlich mehr Speicher verbraucht, als ein String. Wenn die gelesene Datenmenge also hinreichend groß ist, kriegst du damit noch ganz andere Probleme.

                Nun, socket_read() gibt unter php warum auch immer einen String zurück. Die rohe recv() Socket Betriebssystemfunktion schreibt meine Daten in einen "char* buf", was in C üblicherweise ein char-Array ist.

                Du bist im PHP-Land. Vergiss alles, was für C gilt, denn C hat im PHP-Land keine Bedeutung. Auch nicht, wenn PHP namensgleich auf C-Funktionen zurückgreift.

                Es kann später auch sein, dass ich aus dem Datenstrom auf ein einzelnes Byte zugreifen möchte (quasi ein int8_t). Das müsste dann auch mittels Ord() in den entsprechenden Wert gewandelt werden.

                Ein einzelnes Zeichen eines Strings erreichst du über den vom Array bekannten Indexzugriff:

                $str = "abc";  
                $i = 1;  
                echo $str[$i] // 'b'
                

                Wenn das "blöd" ist (z.B. für mehrere Zeichen), gibts auch noch die Stringfunktion substr():
                echo substr($str,$i,1); // 'b'

                Und die Funktion ord() liefert dir den Bytewert des Zeichens, also uint8_t, wenn du so willst - nicht jedoch int8_t. Oder nur für Zeichen mit ASCII-Werten kleiner 128. ;)

                Ich halte es jedoch aus Logikgründen für ungünstig, im Kontext von "ein Interface liefert mir Bytewerte als String verpackt" etwas anderes als unpack() zum Wandeln in Integer zu verwenden. Diese Sonderbehandlung von uint8_t durch ord() wäre nicht selbsterklärend. Außer du verzichtest komplett auf unpack() und rechnest dir die gewünschten Bytewerte lieber mit ord() und Mathematik selber aus.

                - Sven Rautenberg

          2. Hello,

            Ich verstehe nicht ganz, woher Du die Daten bekommst und warum Du sie dann erst in einem Array parken musst. PHP speichert die numerischen Werte, so wie Du sie ins Array speicherst, als Integer mit üblicherweise 4 Byte.

            Die Daten lese ich über socket_read() von einem Gerät ein. Gut, da bekomme ich einen String zurück den ich zuerst in ein Array konvertiere und zurückgebe.
            Ich dachte mir, Binärdaten gehören in ein Array und nicht in einen String.

            PHP kennt aber gar keine Arrays, außer den Strings :-))
            Die Dinger, die in PHP Array heißen, kann man sich besser als (kreuz-)verkettete Listen vorstellen, also als Baumstrukturen von Elementen, die aus Schlüsseln und beliebigen Nutzdatentypen bestehen.
            Der Index ist ein Name und keine Position, lässt also keinen Direktzugriff auf den Speicher per Berechnung der Speicherposition aus Index und Datentyp zu.

            Das möchte PHP selber erledigen.

            Für Deine Zwecke müsste es dann ja am Besten sogar noch ein Ringspeicher sein. Aber auch den müsstest Du Dir besser aus "Strings" basteln.

            Die PHP-Arrays haben ein mächtigen Overhead von bis zu 20 Bytes pro Element.
            Darauf hat Sven Dich ja schon hingewiesen.

            Nach der Behandlung mit unpack() erhältst Du dann allerdings ein PHP-Array von Werten.

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de