Page 1 of 1

Trying to understand txt2bin code from forum

Posted: 23 Oct 2015, 04:10
by dmg
For fun I decided to write some functions to convert text to binary and back. To do this I first learned how to read/write in binary. That was oddly the easy part. Now I want to understand how I can do it using code. I looked at existing code on the forum and selected these functions to learn from:

Code: Select all

text := "I have no idea why this works, but it obviously does."
bin := txt2bin(text)
msgbox % "Txt:`n" text "`n`nBin:`n" bin "`n`nTxt:`n" bin2txt(bin)


txt2bin(txt)
 {
   loop parse, txt
    {
      loop 8
       {
         bin := bin (asc(a_loopfield) >> (8 - a_index) & 1)
       }
    }
   return bin
 }

bin2txt(bin)
 {
   loop parse, bin
    {
      x += x + (a_loopfield = "1")
      if !mod(a_index, 8)
       {
         txt := txt chr(x)
         x = 0
       }
    }
   return txt
 }
The problem is this code has elements I just don't understand. I can figure out in very general terms what the parts do, meaning the action they accomplish, but I have no idea exactly what they are doing or how they are doing it.

In the first function it is >> (8 - a_index) & 1) I am stumped on (I am not math literate). I know that >> is bitshift right, and & is bitwise-and, but knowing their names does not help me understand them.

In the second function the hardest part is x += x + (a_loopfield = "1") That is incrementing x by x +... Whatever that last bit is. That looks like it is assigning to a_loopfield, but a_loopfield is a built in variable and you can't assign to them, right? And what is = "1"? If it were := "1" I would say it was assigning a literal 1 character, but if I add : to it it breaks the code, so that's out. I have no reference point for that bit of code.

Anybody care to pick this code apart and explain it? I can't guarantee I will understand, but I promise to try

Re: Trying to understand txt2bin code from forum

Posted: 23 Oct 2015, 04:22
by jNizM

Re: Trying to understand txt2bin code from forum

Posted: 23 Oct 2015, 04:40
by dmg
It certainly can't hurt. I will look through those and see. Thanks jNizM. :)

Re: Trying to understand txt2bin code from forum

Posted: 23 Oct 2015, 04:47
by jNizM
or you let the dll's calc this for you :P

Re: Trying to understand txt2bin code from forum

Posted: 23 Oct 2015, 05:44
by just me
Well, my attempt for txt2bin().

As a start, the parse-loop will only work properly for strings containing only characters which are represented by exactly one byte.

Each byte is composed of 8 bits. The bit positions are numbered from 0 to 7, where 0 is the position of the least-significant / rightmost bit, and 7 is the position of the most-significant / leftmost bit. Each bit can contain 0 or 1. Its value is the result of BitValue * 2**BitPosition, e.g 00000001 = 1 * 2**0 = 1, 00000010 = 0 * 2**0 + 1 * 2**1 = 2, 00000011 = 1 * 2** + 1 * 2**1 = 3, etc.

txt2bin():

bin := bin (asc(a_loopfield) >> (8 - a_index) & 1)

The expression is performing a & (bitwise and) operation upon two bytes. One of the bytes is retrieved by the parse-loop and contains various values, the other byte is always 1. The & operation compares the two bytes and sets all bits in the result which are set in both bytes. The result of Asc("A") & 1) would be:

Code: Select all

01000001 ; Asc("A") = 65 = 0x41
00000001 ; 1 = 0x01
--------
00000001
As you can see, the value 1 will always check only the least-significant / rightmost bit. To check the other bits this way they have to be shifted / moved right to position 0, and that's exactly what >> (8 - a_index) does, shifting 7 positions to the right in the first iteration, 6 in the second, and 0 in the last. So the bits are compared in the order 7 to 0. The result of the & operation is either 1 (if the rightmost bit is set) or 0 (if not) and appended to bin.

A bit clearer now?

Edit: As a hint for bin2txt(): x += x =x * 2 = x << 1 (shifting one position to the left eqals multiplying by 2 eqals adding a value to itself).

Re: Trying to understand txt2bin code from forum

Posted: 23 Oct 2015, 08:38
by noname
Very intruiging !But it is also flawed it seems when the first digit is 1.
Txt:
èèè

Bin:
111010001110100011101000

Txt:
hèè

The (A_LoopField="1") seems to convert the "string" 1 or 0 to "number" 1 or 0 so it can be used to count up.The position of the 1 in the binary will determine how many times it will loop.In pos second from the left it will loop 7 times and gives 128 so all positions with 1 will add up to give the total.

Code: Select all

x:=1
loop 7
x +=x
msgbox %x%
Ithink the function should be corrected like this by initialising x:=0

Code: Select all

bin2txt(bin)
 {
 x:=0
   loop parse, bin
    {
      x += x + (a_loopfield = "1")
      if !mod(a_index, 8)
       {
         txt := txt chr(x)
         x = 0
       }
    }
   return txt
 }

Re: Trying to understand txt2bin code from forum

Posted: 24 Oct 2015, 02:33
by dmg
jNizM wrote:or you let the dll's calc this for you :P
yes, yes I could have a system DLL do the conversion. But as I said, I am doing this for fun. Using a DLL would be a little like strapping a camera to your butler and tossing him out of a plane, then watching the video. Not as much fun. ;)
just me wrote:As a start, the parse-loop will only work properly for strings containing only characters which are represented by exactly one byte.
Yes, using only 8 bits limits usable characters to the first 255 in the ascii/unicode table, but that covers most English characters.
just me wrote:The expression is performing a & (bitwise and) operation upon two bytes. One of the bytes is retrieved by the parse-loop and contains various values, the other byte is always 1. The & operation compares the two bytes and sets all bits in the result which are set in both bytes. The result of Asc("A") & 1) would be:

Code: [Select all] [Download] GeSHi © Codebox Plus

01000001 ; Asc("A") = 65 = 0x41
00000001 ; 1 = 0x01
--------
00000001

As you can see, the value 1 will always check only the least-significant / rightmost bit. To check the other bits this way they have to be shifted / moved right to position 0, and that's exactly what >> (8 - a_index) does, shifting 7 positions to the right in the first iteration, 6 in the second, and 0 in the last. So the bits are compared in the order 7 to 0. The result of the & operation is either 1 (if the rightmost bit is set) or 0 (if not) and appended to bin.
Yes, I think I start to get it. That bit of code is taking the decimal number for a character, and using >> and & to generate that number's binary code by appending 1s and 0s one at a time to the bin var. The math still confuses me but at least there is hope.
just me wrote:A bit clearer now?
Yes, and nice pun. :bravo:
just me wrote:Edit: As a hint for bin2txt(): x += x =x * 2 = x << 1 (shifting one position to the left eqals multiplying by 2 eqals adding a value to itself).
Yeah, I can see the x += x but it is the (a_loopfield = "1")part that I don't understand. noname says it is converting the literal 1 and 0 characters in the input string back into integers, but how is it doing that? I have often appended an empty string to force treating numbers as strings, but I have no idea why this syntax would do the opposite.
noname wrote:The (A_LoopField="1") seems to convert the "string" 1 or 0 to "number" 1 or 0 so it can be used to count up.
OK, but how is it doing that? I know appending non integers or an empty string to an integer forces a script to treat the integer as a string, but I don't see anything in the docs about doing the opposite. I just have no reference point for the syntax of that bit of code.
noname wrote:The position of the 1 in the binary will determine how many times it will loop.In pos second from the left it will loop 7 times and gives 128 so all positions with 1 will add up to give the total.
... I don't understand that at all. :?
noname wrote:Ithink the function should be corrected like this by initialising x:=0
Thank you! I have made the change to my code. I am looking at this and I can't see what difference that makes to how this works. Somehow having x start as 0 instead of blank changes the math, but I can't see how just from looking at it. Again, math is not a skill I have.

Thank you all for responding. I will keep looking at what you posted and trying to figure it out. if anyone wants to add to this or clarify some points please bring it on! :salute:

Re: Trying to understand txt2bin code from forum

Posted: 24 Oct 2015, 04:27
by just me
(a_loopfield = "1") is an expression which will resolve to either 1 (True) or 0 (False) depending of the current value in A_LoopField, which is also either 1 or 0 in this case (BinToTxt()). So 1 or 0 will be added to x.

Re: Trying to understand txt2bin code from forum

Posted: 24 Oct 2015, 04:37
by noname

Code: Select all

; why initialising x:=0 (x starts empty not 0)

x +=x +1
msgbox Did you gamble it to be 1 ? It is %x%

Just me explained the positional value of "1" in the 8bit binary string so here is a breakdown what happens during the loop.

Code: Select all

x:=0

bin=10000001  ;this calculates the decimal value of the byte

Loop, parse,bin
x +=x +(A_LoopField="1")

msgbox after 8 iterations value is %x%

x:=0

;taking bin apart and calculating the "1" position values
bin=10000000

Loop, parse,bin
{
x +=x +(A_LoopField="1")
msgbox iteration = %A_Index% result %x%
}

msgbox after 8 iterations value is 128

x:=0

bin=00000001

Loop, parse,bin
{
x +=x +(A_LoopField="1")
msgbox iteration = %A_Index% result %x%
}

msgbox after 8 iterations value is 1

The total sum is 129

Re: Trying to understand txt2bin code from forum

Posted: 24 Oct 2015, 18:40
by dmg
just me wrote:(a_loopfield = "1") is an expression which will resolve to either 1 (True) or 0 (False) depending of the current value in A_LoopField, which is also either 1 or 0 in this case (BinToTxt()). So 1 or 0 will be added to x
OK. That it is a comparison and not an assignment makes that syntax a little more reasonable, but still doesn't make sense in context. I have never seen where a comparison can be used outside of something that compares, ie a If or ternary. So I took it out completely and the code still works:

Code: Select all

bin2txt(bin)
 {
   x:=0
   loop parse, bin
    {
      x += x + a_loopfield
      if !mod(a_index, 8)
       {
         txt := txt chr(x)
         x = 0
       }
    }
   return txt
 }
Does that mean (a_loopfield = "1") wasn't actually doing anything for the script?
noname wrote:

Code: Select all

x +=x +1
msgbox Did you gamble it to be 1 ? It is %x%
So this is a time when the script wasn't seeing blank as equal to 0, and it was basically failing to do anything on the first loop iteration. That makes sense, in an abstract way. Thank you.
noname wrote:breakdown what happens during the loop.
That does help some, but the math is still over my head for the moment. Thank you though. :)

Re: Trying to understand txt2bin code from forum

Posted: 25 Oct 2015, 02:42
by just me
dmg wrote:Does that mean (a_loopfield = "1") wasn't actually doing anything for the script?
I'd say it actually does something but isn't needed in this case. The expression resolves to 1, if A_LoopField is "1", and 0, if A_LoopField is "0". Since AHK is treating pure numerical string as numbers you can safely use A_LoopField directly. BTW: Both methods don't check the string for other 'characters' than "0" or "1", so both methods will yield wrong results, then. + (a_loopfield = "1") converts invalid characters to 0 whereas + A_LoopField tries to add the invalid value.
dmg wrote:That does help some, but the math is still over my head for the moment.
In txt2bin() A_LoopField (hopefully) contains a byte value. So you have to Loop 8 times for each byte to get the bits. In bin2txt() A_LoopField represents a single bit, so you have to collect 8 bits to get a byte value. That's what if !mod(a_index, 8) is for. The condition is true whenever mod(a_index, 8) returns 0 (false) and this will happen each time A_Index is a multiple of 8. If so, the character represented by the byte value is appended to the result and the byte value x is set to 0.
You can replace x += x + a_loopfield by x := (x << 1) + A_LoopField to make the functions look more similar:

Code: Select all

Bin := "01000001" ; 0x41 = "A"
; Initialize X
X := 0                           ;  '       0'
; 1st iteration
B := Substr(Bin, 1, 1)
X := (X << 1) + B                ;  '      00'
; 2nd iteration
B := Substr(Bin, 2, 1)
X := (X << 1) + B                ;  '     001'
; 3rd iteration
B := Substr(Bin, 3, 1)
X := (X << 1) + B                ;  '    0010'
; 4th iteration
B := Substr(Bin, 4, 1)
X := (X << 1) + B                ;  '   00100'
; 5th iteration
B := Substr(Bin, 5, 1)
X := (X << 1) + B                ;  '  001000'
; 6th iteration
B := Substr(Bin, 6, 1)
X := (X << 1) + B                ;  ' 0010000'
; 7th iteration
B := Substr(Bin, 7, 1)
X := (X << 1) + B                ;  '00100000'
; 8th iteration
B := Substr(Bin, 8, 1)
X := (X << 1) + B                ; 0'01000001'
MsgBox, % "Bin '" . Bin . "'' = " . "Chr '" . Chr(X) . "'"

Re: Trying to understand txt2bin code from forum

Posted: 25 Oct 2015, 10:14
by noname
Thank you a thousand times dmg for verifying if turning a string into a number when using parsing loop was needed !I would have made some terrible code from now on thinking this.

Maybe the old version of ahk had a need to convert the string ? Where did you find the original code?

Greetings

Re: Trying to understand txt2bin code from forum

Posted: 25 Oct 2015, 16:58
by dmg
just me wrote:I'd say it actually does something but isn't needed in this case.
You are certainly right that it does something. I confirmed with:

Code: Select all

x := 2
msgbox, % (x = 1)
I have never encountered a comparison used outside of an If or ternary, or similar. I had no idea it would work this way.
just me wrote: BTW: Both methods don't check the string for other 'characters' than "0" or "1", so both methods will yield wrong results, then.
Yes, these functions have no error checking as yet, but for my purposes they don't need any.
just me wrote: so you have to collect 8 bits to get a byte value. That's what if !mod(a_index, 8) is for.
That part of the code I actually understand. Using mod() to create a sort of rotating number 'loop' is something I have used before.
noname wrote:Thank you a thousand times dmg for verifying if turning a string into a number when using parsing loop was needed !I would have made some terrible code from now on thinking this...Where did you find the original code?
Glad to be of service. :D

I found the original code here: autohotkey.com/board/topic/7835-ascii-binary-converter/#entry48740

I think I understand all this well enough in theory to recreate these functions using different syntax. Thank you all for your patient help. When/if I make my own version I will post it back here. :morebeard:

Re: Trying to understand txt2bin code from forum

Posted: 27 Oct 2015, 02:37
by dmg
They are longer, less efficient and unnecessarily use entirely different methodology, but they work!:

Code: Select all

text := "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"
bin := txt2bin(text)
msgbox % "Txt:`n" text "`n`nBin:`n" bin "`n`nTxt:`n" bin2txt(bin)


txt2bin(txt)
 {
   loop parse, txt
    {
      chr := asc(a_loopfield)
      place := 128
      loop 8
       {
         if(chr >= place)
          {
            chr -= place
            bits .= 1
          }
         else
          {
            bits .= 0
          }
         place /= 2
       }
      bin .= bits
      bits := ""
    }
   return bin
 }

bin2txt(bin)
 {
   count := bits := 0
   place := 128
   loop parse, bin
    {
      if(a_loopfield)
       {
         bits += place
       }
      place /= 2
      count ++
      if(count = 8)
       {
         text .= chr(bits)
         count := 0
         bits := 0
         place := 128
       }
    }
   return text
 }

Re: Trying to understand txt2bin code from forum

Posted: 27 Oct 2015, 12:32
by noname
but they work!
That is the most important thing !
Image