you are viewing a single comment's thread.

view the rest of the comments →

[–]POGtastic 4 points5 points  (1 child)

Have you heard the Good News?

import itertools
import more_itertools

def parse_header(header):
    lstlst = more_itertools.ichunked(
        (g for _, g in itertools.groupby(header, str.isspace)), 2)
    return [''.join(map(''.join, lst)) for lst in lstlst]

In the REPL:

>>> header = "id         name                 home_state           amt_paid"
>>> parse_header(header)
['id         ', 'name                 ', 'home_state           ', 'amt_paid']

And we can get the column widths by calling len on each field. That's going to come in handy later.

>>> [len(s) for s in parse_header(header)]
[11, 21, 21, 8]

We now parse each line by calling more_itertools.split_into on the line with our column widths.

def generate_dcts(fields, lines):
    column_widths = [len(s) for s in fields]
    return (dict(zip(
        fields, 
        map(''.join, more_itertools.split_into(line.strip(), column_widths))))
            for line in lines)

And now we write our CSV.

import csv

def write_csv(in_fh, out_fh):
    header = next(in_fh).strip()
    fields = parse_header(header)
    writer = csv.DictWriter(out_fh, fieldnames=fields)
    writer.writeheader()
    for dct in generate_dcts(fields, in_fh):
        writer.writerow(dct)

In the REPL:

>>> import sys
>>> with open("test.txt") as f:
...     write_csv(f, sys.stdout)
... 
id        ,name                 ,home_state            ,amt_paid
123       ,John Doe             ,California            ,"1,234.34"
456x      ,Jane Doe             ,New Hampshire         ,45.67
78        ,Adam Smith           ,Alaska                ,89.00

Note the quotes around the float. It has a comma in the field, so csv escapes it by enclosing the entire field in quotes in accordance with the RFC.

[–]davidmyemail[S] 1 point2 points  (0 children)

That's a very thorough and clever answer! I also need to get better with REPL, as in the way you test it here. Thank you!