17

I'm parsing a binary file in javascript that is storing two pieces of information per byte, one per nibble. The values are, of course, 0-16 and 0-16.

In all other parts of the file format, each byte represents one piece of information, so I have been using the following to successfully get the number values I need:

var num = str.charCodeAt(0) & 0xFF;

But I'm stuck at trying to figure out how to get the 0-16 value of the first nibble, and the same for the 2nd nibble from my single byte character "str".

Appreciate any help on this.

Geuis
  • 41,122
  • 56
  • 157
  • 219
  • favorited. I haven't had the need yet, but I'll probably need to work with binary at some point. I'm curious how you're using this though. – vol7ron Sep 21 '10 at 02:48
  • 2
    I'm coming back to this... I would think the values would be 0-15, not 0-16 – vol7ron Aug 07 '11 at 15:57

4 Answers4

18
var num = str.charCodeAt(0) & 0xFF;
var nibble1 = num & 0xF;
var nibble2 = num >> 4;
sje397
  • 41,293
  • 8
  • 87
  • 103
  • Awesome, thank you for such a quick response. I get what num & 0xF is doing (getting the first nibble). Can you explain what >> 4 is doing? – Geuis Sep 21 '10 at 02:28
  • @Geuis: `>>` is the 'shift right' operator - it takes the byte as input and shifts it over one bit (discarding the least significant) to the right as many times as you specify on the right hand side, and returns the modified value. – sje397 Sep 21 '10 at 02:30
  • For some reason this wasn't working for me. For example, when converting the byte using str.charCodeAt(0) & 0xFF === 229, I am getting "5" for the lower and "3966" for the higher. – Geuis Sep 21 '10 at 02:43
  • Ah, I see why now. When I adapted your implementation I was doing nibble1 and nibble2 directly from the byte without passing it through & 0xFF first (which returns the integer value). Now both your solution and codaddict's are working. Thank you both. – Geuis Sep 21 '10 at 02:51
17

You can do:

var num = str.charCodeAt(0);
var lower_nibble = (num & 0xF0) >> 4;
var higher_nibble = num & 0x0F;

How does it work?

Lets suppose the bit representation of num is abcdwxyz and we want to extract abcd as higher nibble and wxyz as lower nibble.

To extract the lower nibble we just mask the higher nibble by bitwise anding the number with 0x0F:

a b c d w x y z
              &
0 0 0 0 1 1 1 1
---------------
0 0 0 0 w x y z  = lower nibble.

To extract the higher nibble we first mask the lower nibble by bitwise anding with 0xF0 as:

a b c d w x y z
              &
1 1 1 1 0 0 0 0
---------------
a b c d 0 0 0 0

and then we bitwise right- shift the result right 4 times to get rid of the trailing zeros.

Bitwise right shifting a variable 1 time will make it loose the rightmost bit and makes the left most bit zero:

a b c d w x y z 
           >> 1
----------------
0 a b c d w x y

Similarly bitwise right shifting 2 times will introduce result in :

a b c d w x y z 
           >> 2
----------------
0 0 a b c d w x

and bitwise right shift 4 times gives:

a b c d w x y z 
           >> 4
----------------
0 0 0 0 a b c d 

as clearly seen the result is the higher nibble of the byte (abcd).

codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 2
    your lower and higher should be reversed. binary reads from right to left. `ASCII:A = DEC:65 = HEX:41`, not `HEX:14` – vol7ron Sep 21 '10 at 02:37
  • noprob. nice diagram, I think that will really help neophytes. – vol7ron Sep 21 '10 at 02:46
  • @codaddicy: yes, nice ascii art. maybe one more table, showing the effect of the right shift, would complete the picture? – sje397 Sep 21 '10 at 03:16
1

Since I'm favoriting this, I wanted to add some things I just wrote that might be useful. Perhaps others will find it useful as well.

Below's jsFiddle

Prototypes:


   Number.prototype.fromCharCode  = function ()   {return String.fromCharCode(this);        };

   String.prototype.byte          = function (val){  var a = new Array();                                                         
                                                     for(var i=(val||0),n=val===0?0:this.length-1; i<=n; i++){
                                                        a.push(this.charCodeAt(i) & 0xFF);
                                                     }
                                                     return a;
                                                  };
   
   String.prototype.HiNibble      = function (val){
                                                     var b = this.byte(val);
                                                     var a = new Array();
                                                     for(var i=0,n=b.length-1; i<=n; i++){a.push(b[i] >> 4);}
                                                     return a;
                                                  };

   String.prototype.LoNibble      = function (val){
                                                     var b = this.byte(val);
                                                     var a = new Array();
                                                     for(var i=0,n=b.length-1; i<=n; i++){a.push(b[i] & 0xF);}
                                                     return a;
                                                  };



Example Calls:


   var str   = new String("aB");
   console.log(str.byte());             // [ 97, 66 ]
   console.log(str.HiNibble());         // [ 6, 4 ]
   console.log(str.LoNibble());         // [ 1, 2 ]
   
   
   console.log(str.byte(0));            // [ 97 ]
   console.log(str.HiNibble(0));        // [ 6 ]
   console.log(str.LoNibble(0));        // [ 1 ]
   
   var bar = "c";
   console.log(bar.byte());             // [ 99 ]
   console.log(bar.HiNibble());         // [ 6 ]
   console.log(bar.LoNibble());         // [ 3 ]

   var foobar = (65).fromCharCode();    // from an integer (foobar=="A")
   console.log(foobar.byte());          // [ 65 ]
   console.log(foobar.HiNibble());      // [ 4 ]
   console.log(foobar.LoNibble());      // [ 1 ]
   
   



Just added for possible help, but is not used in the above:


/* Useful function that I modified
   Originally from: http://www.navioo.com/javascript/dhtml/Ascii_to_Hex_and_Hex_to_Ascii_in_JavaScript_1158.html
*/
   function AscHex(x,alg){
      hex         = "0123456789ABCDEF";
   
      someAscii   = '  !"#$%&\''
                  + '()*+,-./0123456789:;=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\'
                  + ']^_`abcdefghijklmnopqrstuvwxyz{|}';
      r           = "";
      if(alg=="A2H"){
         for(var i=0,n=x.length;i<n;i++){
            let=x.charAt(i);
            pos=someAscii.indexOf(let)+32;
            h16=Math.floor(pos/16);
            h1=pos%16;
            r+=hex.charAt(h16)+hex.charAt(h1);
         }
      }
      if(alg=="H2A"){
         for(var i=0,n=x.length;i<n;i++){
            let1=x.charAt(2*i);
            let2=x.charAt(2*i+1);
            val=hex.indexOf(let1)*16+hex.indexOf(let2);
            r+=someAscii.charAt(val-32);
         }
      }
      return r;
   }
   
   console.log(AscHex('65','A2H'));                // A
Community
  • 1
  • 1
vol7ron
  • 40,809
  • 21
  • 119
  • 172
0

Convert the string to buffer and then nipples:

function bufferToNibbles(key) {
  // this will convert ascii string to hex values 
  // buffer will get sequence of bytes
  const buffer = Buffer.from(key);
  const nibbles = [];
  for (let i = 0; i < buffer.length; i++) {
    let q = i * 2;
    nibbles[q] = buffer[i] >> 4;
    ++q;
    nibbles[q] = buffer[i] % 16;
  }
  return nibbles;
}
Yilmaz
  • 35,338
  • 10
  • 157
  • 202