Wednesday, April 13, 2016

A quick console ruler in Python

By Vasudev Ram

I've done this ruler program a few times before, in various languages.

Here is an earlier version: Rule the command-line with ruler.py!

This one is a simplified and also slightly enhanced version of the one above.

It generates a simple text-based ruler on the console.

Can be useful for data processing tasks related to fixed-length or variable-length records, CSV files, etc.

With REPS set to 8, it works just right for a console of 80 columns.

Here is the code:
# ruler.py
"""
Program to display a ruler on the console.
Author: Vasudev Ram
Copyright 2016 Vasudev Ram - http://jugad2.blogspot.com
0123456789, concatenated.
Purpose: By running this program, you can use its output as a ruler,
to find the position of your own program's output on the line, or to 
find the positions and lengths of fields in fixed- or variable-length 
records in a text file, fields in CSV files, etc.
"""

REPS = 8

def ruler(sep=' ', reps=REPS):
    for i in range(reps):
        print str(i) + ' ' * 4 + sep + ' ' * 3,
    print '0123456789' * reps

def main():

    # Without divider.
    ruler()

    # With various dividers.
    for sep in '|+!':
        ruler(sep)

if __name__ == '__main__':
    main()
And the output:
$ python ruler.py
0         1         2         3         4         5         6         7         
01234567890123456789012345678901234567890123456789012345678901234567890123456789
0    |    1    |    2    |    3    |    4    |    5    |    6    |    7    |    
01234567890123456789012345678901234567890123456789012345678901234567890123456789
0    +    1    +    2    +    3    +    4    +    5    +    6    +    7    +    
01234567890123456789012345678901234567890123456789012345678901234567890123456789
0    !    1    !    2    !    3    !    4    !    5    !    6    !    7    !    
01234567890123456789012345678901234567890123456789012345678901234567890123456789
You can also import it as a module in your own program:
# test_ruler.py
from ruler import ruler
ruler()
# Code that outputs the data you want to measure 
# lengths or positions of, goes here ...
print 'NAME      AGE  CITY'
ruler()
# ... or here.
print 'SOME ONE   20  LON '
print 'ANOTHER    30  NYC '
$ python test_ruler.py
Output:
0         1         2         3         4         5         6         7         
01234567890123456789012345678901234567890123456789012345678901234567890123456789
NAME      AGE  CITY
0         1         2         3         4         5         6         7         
01234567890123456789012345678901234567890123456789012345678901234567890123456789
SOME ONE   20  LON 
ANOTHER    30  NYC 

- Enjoy.

- Vasudev Ram - Online Python training and consulting

Signup to hear about my new products and services.

My Python posts     Subscribe to my blog by email

My ActiveState recipes


5 comments:

Sascha said...

Nice idea :)

imho there is a '\n' missing for working correctly here:

   def ruler(sep=' ', reps=REPS):
      for i in range(reps):
         print str(i) + ' ' * 4 + sep + ' ' * 3,
      print '\n', '0123456789' * reps

Regards
Sascha

Vasudev Ram said...

>Nice idea :)

Thanks :)

>imho there is a '\n' missing for working correctly here:

Actually, there is a bit of an oddity involved there:

I did know about the issue you mention, before publishing the post:

I did the calculations of how many characters of what kind to print.

The comma at the end of this line:

print str(i) + ' ' * 4 + sep + ' ' * 3,

causes a space to print instead of a newline (at the end, as I'm sure you know). The sum of the explicitly printed chars plus the space caused by the comma, is exactly 10, which, multiplied by REPS (8) gives exactly 80. And when 80 chars are printed on an 80-column console, even if you do not print a newline, the cursor position scrolls to the next line - it has to.

That's why I had that comma at the end, and no explicit newline.

For what the ruler is meant for, i.e., measuring lengths and positions of data items vertically adjacent to it, it works fine (as long as your display is exactly 80 cols).

Also, with your correction, there is an extra blank line between the two lines of digits that comprise the ruler.

Regards
Vasudev

Vasudev Ram said...

My earlier solution (also linked above in the post), also worked, though with somewhat different logic.

Sascha said...

Ok, now I understand your intention :)
And it works fine with an 80 columns console now :)

Another enhancement could be to define whether to start counting at 0 or 1

Vasudev Ram said...

>Ok, now I understand your intention :)
>And it works fine with an 80 columns console now :)

Cool :)

>Another enhancement could be to define whether to start counting at 0 or 1

Yes, that's a good idea. In fact, just a little while ago, when reviewing the code and the output, as part of replying to your previous comment, I began to think that the ruler starting from 0 looked a bit strange [1]. Obviously many languages and other tools have 0-based counting, like Python, C, etc., and for good reasons. I may subconsciously have had business data processing uses in mind, could be why it felt strange; see the mention of records and CSV in the post, though of course scientific and other non-business apps can use those formats too. In the case of CSV it would seem more natural to think in these terms:

field 1: start pos: 1, length: 10
field 2: start pos: 11, length: 25
etc.

[1] Though many real-world rulers do start from 0.