1

I'm trying to create a table in the console from a csv file. To make the output look more like a table, I want to add a row of dashes on the second line to split the headings and the main table content.

This is my code so far:

import csv

with open('filename.csv', mode='r') as csv_file:
    csv_reader = csv.reader(csv_file)
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            print("|",row[0]," "*(20-len(row[0])), "|",
              row[1], " " * (15 - len(row[1])), "|",
              row[2], " " * (20 - len(row[2])), "|",
              row[3], " " * (5 - len(row[3])), "|")

This is the output:

| Track           | Artist           | Album                 | Time   |
| Songname 1      | Artist 1         | Album 1               | 7:15   |
| Songname 1      | Artist 2         | Album 2               | 6:27   |
| Songname 1      | Artist 3         | Album 3               | 5:24   |
| Songname 1      | Artist 4         | Album 4               | 2:50   |
| Songname 1      | Artist 5         | Album 5               | 4:39   |
| Songname 1      | Artist 6         | Album 6               | 5:11   |
| Songname 1      | Artist 7         | Album 7               | 2:56   |
| Songname 1      | Artist 8         | Album 8               | 4:29   |
| Songname 1      | Artist 9         | Album 9               | 3:17   |
| Songname 1      | Artist 10        | Album 10              | 3:04   |

Process finished with exit code 0

And this is the output I'd like to achieve:

-----------------------------------------------------------------------
| Track           | Artist           | Album                 | Time   |
-----------------------------------------------------------------------
| Songname 1      | Artist 1         | Album 1               | 7:15   |
| Songname 1      | Artist 2         | Album 2               | 6:27   |
| Songname 1      | Artist 3         | Album 3               | 5:24   |
| Songname 1      | Artist 4         | Album 4               | 2:50   |
| Songname 1      | Artist 5         | Album 5               | 4:39   |
| Songname 1      | Artist 6         | Album 6               | 5:11   |
| Songname 1      | Artist 7         | Album 7               | 2:56   |
| Songname 1      | Artist 8         | Album 8               | 4:29   |
| Songname 1      | Artist 9         | Album 9               | 3:17   |
| Songname 1      | Artist 10        | Album 10              | 3:04   |
-----------------------------------------------------------------------
GregG
  • 35
  • 1
  • 1
  • 7
  • have you tried adding the lines yourself? – depperm Nov 28 '18 at 18:54
  • You may be interested in some of the "pretty print" type libraries [mentioned here](https://stackoverflow.com/questions/9535954/printing-lists-as-tabular-data) such as [`tabulate`](https://pypi.python.org/pypi/tabulate), [`PrettyTable`](https://pypi.python.org/pypi/PrettyTable), [`texttable`](https://pypi.python.org/pypi/texttable), etc – Cory Kramer Nov 28 '18 at 18:54
  • @depperm do you mean adding the lines directly into the csv file? – GregG Nov 28 '18 at 18:55
  • I mean what have you tried to add the lines to the output yourself? – depperm Nov 28 '18 at 18:56
  • @CoryKramer thanks for your feedback, I should have mentioned in the original post that I'd like to avoid using type libraries if possible – GregG Nov 28 '18 at 18:56
  • @depperm I have tried using dash = '-' * 40 and then adding 'dash' into my code however that just creates a line of dashes after every line and not just the heading line – GregG Nov 28 '18 at 18:58
  • `print('-' * 40); print(next(csv_reader)); print('-' * 40); for row in csv_reader: ...` ? – G_M Nov 28 '18 at 19:00
  • @PatrickArtner sorry about that, I have fixed the indentation so it looks like my document – GregG Nov 28 '18 at 19:00
  • @G_M That works great, however how do I now line up the headings line with the rest of the table content – GregG Nov 28 '18 at 19:03

2 Answers2

3

You can make your code far easier using the string format mini language:

t = """Track,Artist,Album,Time
Songname 1,Artist 1,Album 1,7:15
Songname 1,Artist 2,Album 2,6:27
"""
with open("t.txt","w") as w:
    w.write(t)


import csv

with open('t.txt', mode='r') as csv_file:
    csv_reader = csv.reader(csv_file)
    for idx, row in enumerate(csv_reader):
        if idx == 0:
            print("-"*65)

        # string format mini language:
        #  {:<20} means take the n-th provided value and right align in 20 spaces
        print("|{:<20}|{:<15}|{:<20}|{:<5}|".format(*row))  # *row == row element wise
        if idx == 0:
            print("-"*65)
    print("-"*65)

Output:

-----------------------------------------------------------------
|Track               |Artist         |Album               |Time |
-----------------------------------------------------------------
|Songname 1          |Artist 1       |Album 1             |7:15 |
|Songname 1          |Artist 2       |Album 2             |6:27 |
-----------------------------------------------------------------

Alternate solutions for the header extraction if you want to omit row times if and use adaptive widths without hardcoding lengths:

Demofile:

t = """Track,Artist,Album,Time
Very Long Songname 1,Artist 1,Album 1,7:15
Songname 1,Artist 2,Album 2 title with epic proportions,6:27
"""
with open("t.txt","w") as w:
    w.write(t)

Code:

import csv

def get_format_string(w):
    s = '|'+'|'.join(" {{:<{}}} ".format(l) for _,l in sorted(w.items()))+'|'
    return s.format

def get_widths(reader,filehandle):
    """Calculate maximum widths of columns. Rewinds filehandle to seek(0)."""
    widths = {}
    for row in reader:
        for i,c in enumerate(row):
            widths[i] = max(widths.get(i,0), len(c))
    # rewind
    filehandle.seek(0)
    return widths  

with open('t.txt', mode='r') as csv_file:
    csv_reader = csv.reader(csv_file)
    fmt = get_format_string(get_widths(csv_reader,csv_file))

    header = fmt(*(next(csv_reader)))
    w = len(header)
    print("-"*w)
    print(header)
    print("-"*w)

    # deal with the rest of the data
    for row in csv_reader:
        print(fmt(*row))

    # and the last line
    print("-"*w)

Output:

--------------------------------------------------------------------------------
| Track                | Artist   | Album                               | Time |
--------------------------------------------------------------------------------
| Very Long Songname 1 | Artist 1 | Album 1                             | 7:15 |
| Songname 1           | Artist 2 | Album 2 title with epic proportions | 6:27 |
--------------------------------------------------------------------------------
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • 1
    Checking `if idx == 0` for every iteration seems wasteful. – G_M Nov 28 '18 at 19:05
  • 1
    You don't need `idx` or `enumerate` in the second example. Also, you could pull that format string into a separate variable like `fmt = "|{:<20}|{:<15}|{:<20}|{:<5}|".format`. – G_M Nov 28 '18 at 19:09
  • 1
    The only problem with this (depending on OP's requirements) might be the hard-coded widths. If you want to dynamically size the table to be more "form-fitting", you would have to iterate through the csv file keeping track of the maximum length per column and then build the table accordingly. – G_M Nov 28 '18 at 19:14
  • Ya, I think your code is fine for what we know from OP so far! – G_M Nov 28 '18 at 19:17
1

I believe what you're trying to achieve is that the header of your table(i.e. the column names) should come off separately. If I am understanding you right, then I'll suggest why not use pandas.read_csv to import your csv file. It identifies the header by itself and if not then it does have this as an argument that how to select a row as the header.

Check out the documentation: Pandas- read_csv

Also, if in case you want dashes only or the way you mentioned it, then I believe you will have to add print statement after printing your first row which has the column names and then another print statement for dashes and then a loop for other info and then a final print for more dashes. So yeah that's one manual way to do it.

Edit: Since you asked for the manual way of doing it, here's the code:

import csv

with open('stacko.csv') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter='|')
    line_count = 0
    print("-----------------------------------------------------------------------------------")
    for row in csv_reader:
        if line_count == 0:
            print("|",row[1], " " * (5 - len(row[1])), "|",
                  row[2], " " * (10 - len(row[2])), "|",
                  row[3], " " * (5 - len(row[3])), "|",
                  row[4], " " * (5 - len(row[4])), "|")
            print("-----------------------------------------------------------------------------------")
            line_count=1
        else:
            print("|",row[1], " " * (5 - len(row[1])), "|",
                  row[2], " " * (10 - len(row[2])), "|",
                  row[3], " " * (5 - len(row[3])), "|",
                  row[4], " " * (5 - len(row[4])), "|")
    print("-----------------------------------------------------------------------------------")

Output:

-----------------------------------------------------------------------------------
|  Track             |  Artist             |  Album                   |  Time     |
-----------------------------------------------------------------------------------
|  Songname 1        |  Artist 1           |  Album 1                 |  7:15     |
|  Songname 1        |  Artist 2           |  Album 2                 |  6:27     |
|  Songname 1        |  Artist 3           |  Album 3                 |  5:24     |
|  Songname 1        |  Artist 4           |  Album 4                 |  2:50     |
|  Songname 1        |  Artist 5           |  Album 5                 |  4:39     |
|  Songname 1        |  Artist 6           |  Album 6                 |  5:11     |
|  Songname 1        |  Artist 7           |  Album 7                 |  2:56     |
|  Songname 1        |  Artist 8           |  Album 8                 |  4:29     |
|  Songname 1        |  Artist 9           |  Album 9                 |  3:17     |
|  Songname 1        |  Artist 10          |  Album 10                |  3:04     |
-----------------------------------------------------------------------------------
Amit Amola
  • 2,301
  • 2
  • 22
  • 37
  • So if I was to do it the manual way you suggested, how would I be able to keep the headings and the rest of the table content in line like it does on the 'output i'd like to achieve' in my original post – GregG Nov 28 '18 at 19:07
  • Added the code, take a look. I guess that print thing can be changed via length function and repeating the dashes as per the length, exactly how Patrick has somewhat done it, but right now I am just hardcoding it to a fixed length as per the table you've provided. Try to play around a bit and I'll strongly suggest to use Pandas.read_csv Have a nice day ahead Largo – Amit Amola Nov 28 '18 at 19:28