Feed Sign in with OpenID OpenID

Simon Willison’s Weblog

width = str(len(str(len(lines))))

The above monstrosity came up today while writing a function to add zero padded line numbers to a chunk of text:

def linenumbers(text):
    "Add zero padded line numbers to text"
    lines = text.split('\n')
    # Find the maximum 'width' of the line count
    width = str(len(str(len(lines))))
    for i, line in enumerate(lines):
        lines[i] = ("%0" + width  + "d. %s") % (i + 1, line)
    return '\n'.join(lines)

I think it has a pleasant kind of symmetry to it.

This is width = str(len(str(len(lines)))) by Simon Willison, posted on 10th February 2004.

View blog reactions

Next: Code generation vs data driven programming

Previous: The dangers of PageRank

13 comments

  1. Why not just do len(`len(lines)`) and/or lines[i] = ("%0%sd. %s") % (i + 1, width, line)?

    The former doesn't entail any surprises (len() still returns an int) and you're already using string interpolation (is that the right term?) in the latter expression.

    Joe Grossberg - 10th February 2004 02:11 - #

  2. It takes a certain kind of person to find beauty in code. :)

    Chris Vincent - 10th February 2004 02:18 - #

  3. I suppose it would be pleasant if you have a Lisp fetish. :)

    David Lindquist - 10th February 2004 04:47 - #

  4. I'm not sure how it affects the poetic balance of the code, but you can save one str() using the * format specifier.

    I'd write the guts of the function as:

    width = len(str(len(lines)))
    lines = [ '%0*d. %s' % (width, i, line) for i, line in enumerate(lines) ]
    

    Mark Russell - 10th February 2004 11:44 - #

  5. Is this the time to point out string.zfill? Although I can't see a way around the len(str(len(lines))) construct. as Mark Russell mentions.

    sil - 10th February 2004 19:44 - #

  6. The need for comments is a code smell. So I'll add another comment. ;)

    How about:

    
    def widestLineNumberLen(lines):
      return len(str(len(lines)))
    
    def linenumbers(text):
        "Add zero padded line numbers to text"
        lines = text.split('\n')
    
        for i, line in enumerate(lines):
            lines[i] = ("%0" + str(widestLineNumberLen(lines))  + "d. %s") % (i + 1, line)
        return '\n'.join(lines)
    

    Jeremy Dunck - 10th February 2004 22:12 - #

  7. def widestLineNumberLen(lines):
      1 + floor(log(len(lines)))
    

    Not sure if those are the right function names, but that does the trick without multiple conversions, no?

    Tom - 11th February 2004 15:13 - #

  8. Tom's is better, as long as the reader is decent in math. ;)

    Jeremy Dunck - 11th February 2004 16:10 - #

  9. And proving once again that Perl is blatently superior to Python:

    sub linenumbers($) {
        my @lines = split /\n/, $_[0], -1;
        my $width = length @lines;
        my $count = 0;
        $_ = sprintf '%0*d %s', $width, ++$count, $_ foreach @lines;
        local $" = "\n";
        return "@lines";
    }

    /me hides

    (And yes, length(@array) in Perl is equivalent to len(str(len(array))) in Python. Isn't it great?)

    Ian Hickson - 14th February 2004 00:39 - #

  10. If we assume *nix, and we assume the lines in question are contained in somefile, and we assume that all we want to do is print the numbered lines to stdout or to some other file, why not:

    cat -n somefile

    I guess that is a lot of assuming though. :)

    David Lindquist - 14th February 2004 22:48 - #

  11. Ian, you don't want to use the prototype: it forces the argument(s) to scalar context.

    Anyway, yet another way to do it that works with any type of line ending:

    sub linenumbers {
        my @lines = split /^/, shift, -1;
        my $width = length @lines;
        my $count = 0;
        return join '',  map { sprintf '%0*d %s', $width, ++$count, $_ } @lines;
    }

    Or:

    sub linenumbers {
        my @lines = split /^/, shift, -1;
        my $width = length @lines;
        for (1 .. @lines) {
          substr($lines[$_ - 1], 0, 0) = sprintf '%0*d ', $width, $_;
        }
        return join '',  @lines;
    }

    Or even:

    sub linenumbers {
        local $_ = shift;
        my $width = length (() = /^/gm);  #  Context is everything...
        my $count = 0;
        s/^/sprintf '%0*d ', $width, ++$count/egm;
        return $_;
    }

    Hmmm, maybe I got a bit carried away at the end there, but somehow it seemed appropriate...

    Arien - 16th February 2004 10:12 - #

  12. Is there an equivalent to enumerate lines in PHP?

    Mark - 25th March 2004 21:28 - #

  13. php $arr = explode("\n", $str);

    Per - 16th December 2005 17:47 - #

Comments are closed.

Previously hosted at http://simon.incutio.com/archive/2004/02/10/strlenstrlen

A django site