Version 10 of file size

Updated 2008-06-20 17:08:27 by MB

file size name

Returns a decimal string giving the size of file name in bytes. If the file doesn't exist or its size cannot be queried then an error is generated.


Before Tcl 8.4a4, file size would actually throw an error on a file larger than 2gb. Now it works, and returns the correct value for such 'large files'.

file size must return the complete size of a file including any data which the OS might not yet have written to disk. On Unix this is easy, but on Windows, Tcl actually has to force the OS to flush all buffers before returning the information. (Of course all of this happens behind the scenes).


The following is an algorithm to convert a file size in bytes into a human readable form. For example, if one has a 10 000 bytes file :

  hrfilesize::bytestohr 10000

returns "9.8 Kbytes"

    #
    # Convert a size in byte into a human readable form.
    # http://en.wikipedia.org/wiki/Byte
    # Copyright: Michael Baudin [email protected]
    #

    package provide hrfilesize 1.0

    namespace eval hrfilesize  {
        variable kilobytes 1024
        # This is a map from the power to the unit
        variable powertounitmap [list 0 "bytes" \
                                     1 "Kbytes" \
                                     2 "Mbytes" \
                                     3 "Gbytes" \
                                     4 "Tbytes"]
    }

    #
    # hrfilesize::bytestohr --
    #   Returns a string containing an human-readable form
    #   representing the given size in bytes by computing
    #   the size in the suitable units :
    #   - bytes,
    #   - kilobytes,
    #   - megabytes,
    #   - gigabytes,
    #   - terabytes.
    #   This corresponds to the problem of solving the following
    #   equation :
    #     x = y 1024^power
    #   where
    #     0<= y < 1024
    #     power >= 0
    # Example:
    #   If one has a 10 000 bytes file :
    #     hrfilesize::bytestohr 10000
    #   returns "9.8 Kbytes"
    # Arguments:
    #   size: the size in bytes
    #   fmt : the format used to convert from the full real size
    #     to a sexy short real. Defaults to "%.1f"
    #
    proc hrfilesize::bytestohr {size {fmt "%.1f"}} {
        set power [hrfilesize::kilobytespower $size]
        # Limits the power to 4
        if {$power>4} then {
            set power 4
        }
        array set unitarray $hrfilesize::powertounitmap
        set unit $unitarray($power)
        set factor [expr {pow($hrfilesize::kilobytes,$power)}]
        set reducedsize [expr {$size/$factor}]
        set shortdouble [format $fmt $reducedsize]
        set result "$shortdouble $unit"
        return $result
    }
    #
    # Returns the power of the kilobytes multiple.
    #
    proc hrfilesize::kilobytespower {size} {
        set newsize $size
        set power 0
        while {$newsize >= $hrfilesize::kilobytes } {
            incr power
            set newsize [expr {$newsize / double($hrfilesize::kilobytes)}]
        }
        return $power
    }

And here are the tests.

    package require tcltest
    namespace import tcltest::test
    lappend ::auto_path .
    package require hrfilesize 1.0

    tcltest::test hrfilesize-1 {50 bytes} {
        set res [hrfilesize::bytestohr 50]
    } {50.0 bytes}
    tcltest::test hrfilesize-2 {10000 bytes} {
        set res [hrfilesize::bytestohr 10000]
    } {9.8 Kbytes}
    tcltest::test hrfilesize-3 {10000000 bytes} {
        set res [hrfilesize::bytestohr 10000000]
    } {9.5 Mbytes}
    tcltest::test hrfilesize-4 {10000000000 bytes} {
        set res [hrfilesize::bytestohr 10000000000]
    } {9.3 Gbytes}
    tcltest::test hrfilesize-5 {10000000000000 bytes} {
        set res [hrfilesize::bytestohr 10000000000000]
    } {9.1 Tbytes}
    tcltest::test hrfilesize-6 {1024 bytes} {
        set res [hrfilesize::bytestohr 1024]
    } {1.0 Kbytes}
    tcltest::test hrfilesize-7 {1025 bytes} {
        set res [hrfilesize::bytestohr 1025]
    } {1.0 Kbytes}
    tcltest::test hrfilesize-8 {0 bytes} {
        set res [hrfilesize::bytestohr 0]
    } {0.0 bytes}
    tcltest::test hrfilesize-9 {1 Mbyte bytes} {
        set res [hrfilesize::bytestohr [expr {pow(1024.,2)}]]
    } {1.0 Mbytes}

    ::tcltest::cleanupTests

See also: