I have a file format that I need to read which contains blocks of CSV data interspersed by non-CSV (or at least, inconsistent CSV) lines. Other than the very first line of the file (which has comma-separated headers), each repeated "block" has one line containing some metadata - including the number of actual CSV lines in that block - followed by the CSV data. The blocks are of varying length within the file. An example with dummy data can be seen at the end of the question.
I have written the following code which uses the TextFileReader in pandas to parse the metadata line into a dictionary, and then read each block. I need the metadata dicts in a list and the CSV data concatenated. This reader works, and gives me what I need, but it seems a bit hokey. The non-CSV line is still read as if it's a CSV ... I can extract the info I need, but it's awkward. I also couldn't figure out how to exit using the TextFileReader nicely without simply catching the StopIteration error, but I very much dislike using while True
.
Is there a cleaner way to exit using the iterative TextFileReader, and/or any other tidying this code could use?
from io import StringIO
import pandas as pd
# This is the reader function
def read_mixed(fname):
metalist = []
dflist = []
ibuff = 0
with pd.read_csv(fname, skipinitialspace=True, iterator=True) as reader:
try:
while True:
ibuff += 1
# Read the meta info
meta_buff = reader.read(1)
nlines = int(meta_buff.iloc[0, 0].split('=')[-1])
tmp_meta = {x.split('=')[0]: x.split('=')[1]
for x in meta_buff.iloc[0, 1:].dropna()}
tmp_meta.update({'LINES': nlines})
# Read the CSV info
tmp_df = reader.read(nlines)
tmp_df['ibuff'] = ibuff
metalist.append(tmp_meta)
dflist.append(tmp_df)
except StopIteration:
pass
return metalist, pd.concat(dflist, axis=0).reset_index(drop=True)
# This is a tester that illustrates the format of the real file(s)
if __name__ == "__main__":
fstr = StringIO("""a1,a2,a3,b1,b2,b3,c1,c2,c3
Comment line: LINES=5, CAR1=Top Fuel, CAR2=Funny, CAR3=Pro Stock
12,1,23,5,23,5,3,4,5
12,1,23,5,23,5,3,4,5
12,1,23,5,23,5,3,4,5
12,1,23,5,23,5,3,4,5
12,1,23,5,23,5,3,4,5
Comment line: LINES=5, CAR1=Funny, CAR2=Pro Stock, CAR3=Top Fuel
24,2,46,10,46,10,6,8,10
24,2,46,10,46,10,6,8,10
24,2,46,10,46,10,6,8,10
24,2,46,10,46,10,6,8,10
24,2,46,10,46,10,6,8,10
Comment line: LINES=3, CAR1=Funny, CAR2=Top Fuel, CAR3=Pro Stock
12,1,23,5,23,5,3,4,5
12,1,23,5,23,5,3,4,5
12,1,23,5,23,5,3,4,5
""")
output = read_mixed(fstr)
print(output)