Version 34 of base64

Updated 2011-06-28 16:08:21 by JMN

Reference documentation can be found at

and


Background: Base64 (RFC 3548 ) is a way to encode arbitrary binary data in such a way that the result contains only printable characters and are thus able to pass unchanged even through old applications which are not 8bit-ASCII clean.

Base64 is related to UUencodeing in that it uses the same mechanism of distributing the bits in 3 bytes into 4 bytes. But it uses a different table to map the resulting data into printable characters.

The above means that base64 encoded data takes one-third more space than the data before the conversion. Yes.


The Cookbook includes an example which processes GIFs.


Base 64 encode/decode shows an example of the Tcllib module at work.


There are incompatibilities between various base64 packages and versions of Tcl. CL has pursued this most; the root problem is a change in the semantics of binary. Tcl 8.0 with the base64 in tcllib 0.8 and before was definitely bad. This thread [L1 ] details this aspect of the change from 2.1 to 2.2 of the base64.tcl source code.


Please note that there is some base64 decoding support built into Tk which is different code than what is in tcllib. There may be other bits around as well. The reason this is important is when fixing bugs, etc.


Critcl also has a Base64 implementation, see "ascenc" in critlib and a critical mindset about policy


LES on July 24, 2004: Beware of a little pitfall with Windows and binaries. Guess why this experiment fails:

 set  myFile  "test.zip"
 # unpack "test.zip" -> it works; let's go ahead

 package require base64

 # ---- open and read file ----
 set  myFP  [ open  $myFile  r ]
 set  {pure file}  [ read  $myFP ]
 close  $myFP

 # ---- encode ----
 set  {encoded file}  [ ::base64::encode  ${pure file} ]

 # puts  ${encoded file}
 # output looks good

 # ---- decode ----
 set  {decoded file}  [ ::base64::decode  ${encoded file} ]

 # ---- open and write file ----
 set  myFP  [ open  "decoded - $myFile"  w ]
        fconfigure  $myFP  -translation  binary
 puts  $myFP  ${decoded file}
 close  $myFP

 # now hopefully the file has been decoded back to its original state
 # unpack "test.zip" -> ouch! it's corrupt!
 # how does element "fly" get into element "Brundle"?!!

 exit

Answer: Note that I use fconfigure when I write the file back, to make it a binary. But I didn't use fconfigure when I read the original binary file. That tripped me up because in PHP we only have to use "wb" (write binary) when writing the binary file. We don't need "rb" to read it. The simple "r" option is enough. In Tcl, one must configure the channel as binary in every step.

Take a look at open and fconfigure to learn more about configuring a channel and making it binary.

Thanks, schlenk.

AK. Right. See Working with binary data as well.


2006-07-23: RS could not resist to do base64 encoding as a little weekend fun project, like this:

 proc b64en str {
    binary scan $str B* bits
    switch [expr {[string length $bits]%6}] {
        0 {set tail ""}
        2 {append bits 0000; set tail ==}
        4 {append bits 00; set tail =}
    }
    return [string map {
        000000 A 000001 B 000010 C 000011 D 000100 E 000101 F
        000110 G 000111 H 001000 I 001001 J 001010 K 001011 L
        001100 M 001101 N 001110 O 001111 P 010000 Q 010001 R
        010010 S 010011 T 010100 U 010101 V 010110 W 010111 X
        011000 Y 011001 Z 011010 a 011011 b 011100 c 011101 d
        011110 e 011111 f 100000 g 100001 h 100010 i 100011 j
        100100 k 100101 l 100110 m 100111 n 101000 o 101001 p
        101010 q 101011 r 101100 s 101101 t 101110 u 101111 v
        110000 w 110001 x 110010 y 110011 z 110100 0 110101 1
        110110 2 110111 3 111000 4 111001 5 111010 6 111011 7
        111100 8 111101 9 111110 + 111111 /
    } $bits]$tail
 }

On short test strings, it delivers the same results as "the real thing". What's missing is insertion of whitespace for longer strings. In any case, I think this implementation is pretty educational...


2009-12-08: Jannis wrote an base64 decoder in the same manner as above. No warranties, works for me and I learned a lot. Doesn't honor "wrap characters", just as the encoder above. Many thanks to RS for the encoder!

 proc b64de {str} {
   set tail [expr [string length $str] - [string length [string trimright $str =]]]
   set str [string trimright $str =]
   set bits [string map {
     A 000000 B 000001 C 000010 D 000011 E 000100 F 000101
     G 000110 H 000111 I 001000 J 001001 K 001010 L 001011
     M 001100 N 001101 O 001110 P 001111 Q 010000 R 010001
     S 010010 T 010011 U 010100 V 010101 W 010110 X 010111
     Y 011000 Z 011001 a 011010 b 011011 c 011100 d 011101
     e 011110 f 011111 g 100000 h 100001 i 100010 j 100011
     k 100100 l 100101 m 100110 n 100111 o 101000 p 101001
     q 101010 r 101011 s 101100 t 101101 u 101110 v 101111
     w 110000 x 110001 y 110010 z 110011 0 110100 1 110101
     2 110110 3 110111 4 111000 5 111001 6 111010 7 111011
     8 111100 9 111101 + 111110 / 111111
   } $str]
   set bytes [binary format B* $bits]
   return [string range $bytes 0 end-$tail]
 }

base 64 encoding (Garrigues)

see also base58