0

So I have multiple txt documents that are formatted like this:

james  
M  
18  
72   
170  
teresa  
F  
19  
63  
115  

Some have only two names, others have 50. Basically, what I'm trying to do is format the info so that it is rewitten to a new text file with the following format:

Name: james  
Gender: M  
Age: 18  
Height: 72  
Weight: 170  
Name: teresa  
Gender: F  
Age: 19  
Height: 63  
Weight: 115  

So far I have:

def tagInfo(fileName):
    with open(fileName) as infile, open("altered.txt","w") as outfile:
        for i,line in enumerate(infile):
            if i == 0:
                outfile.write("Name: "+line.strip()+"\n")
            if i == 1:
                outfile.write("Gender: "+line.strip()+"\n")
            if i == 2:
                outfile.write("Age: "+line.strip()+"\n")
            if i == 3:
                outfile.write("Height: "+line.strip()+"\n")
            if i == 4:
                outfile.write("Weight: "+line.strip()+"\n")
    outfile.close()

All this program does is edit the first 5 lines. I'm trying to make it so if I give it a list of 50 people to change, it changes the info of all 50 people, not just the first five lines. With my current solution, that's not possible. I'm not sure where to go from here and I feel like it may be best to scrap everything and take a different approach.

Do you guys have any solutions?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
user1707398
  • 5
  • 1
  • 1
  • 5
  • Not really related to your problem, but I'm surprised no one has pointed out yet that `outfile.close()` is superfluous. The whole point of using the with statement is that the file context manager will close the file upon exiting the with statement block. – Iguananaut Dec 07 '12 at 16:47

4 Answers4

3

If your files have a fixed list of fields, then define the fields as a list and use the line number with a modulus as an index to get the field name:

fields = ('Name', 'Gender', 'Age', 'Height', 'Weight')

def tagInfo(fileName):
    with open(fileName) as infile, open("altered.txt","w") as outfile:
        for i, line in enumerate(infile):
            field = fields[i % len(fields)]
            outfile.write('{0}: {1}\n'.format(field, line))

You can add fields as needed if there are more than 5.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • +1. But it the files were not produced by machine, I would add at least some basic, very simple checking like: string of letters, F/M, integer from 1 to 150, etc. – pepr Dec 08 '12 at 19:23
1

Change your checks to be i % 5 == 0 etc. That way the check will return true for every fifth row.

if i % 5 == 0:
    outfile.write("Name: "+line.strip()+"\n")
elif i % 5 == 1:
    outfile.write("Gender: "+line.strip()+"\n")
elif i % 5 == 2:
    outfile.write("Age: "+line.strip()+"\n")
elif i % 5 == 3:
    outfile.write("Height: "+line.strip()+"\n")
elif i % 5 == 4:
    outfile.write("Weight: "+line.strip()+"\n")
poke
  • 369,085
  • 72
  • 557
  • 602
1

Solution

Modified Solution

Just use the mod, short and simple:

def tagInfo(fileName):
    with open(fileName) as infile, open("altered.txt","w") as outfile:
        for i,line in enumerate(infile):
            if i % 5 == 0:
                outfile.write("Name: "+line.strip()+"\n")
            elif i % 5 == 1:
                outfile.write("Gender: "+line.strip()+"\n")
            elif i % 5 == 2:
                outfile.write("Age: "+line.strip()+"\n")
            elif i % 5 == 3:
                outfile.write("Height: "+line.strip()+"\n")
            elif i % 5 == 4:
                outfile.write("Weight: "+line.strip()+"\n")
    outfile.close()


Rewritten Solution

This solution would be much better as it'd eliminate a lot of dupes in your code, not to mention make it cleaner:

fields = ("Name", "Gender", "Age", "Height", "Weight") # can be edited as per req.s
l = len(fields)

def tagInfo(fileName):
    with open(fileName) as infile, open("altered.txt","w") as outfile:
        for (index, s) in enumerate(infile):
             field = fields[index % l]
             outfile.write("{}: {}\n".format(field, s.strip())
    outfile.close()


The mod function

From the docs:

The % (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first converted to a common type. A zero right argument raises the ZeroDivisionError exception. The arguments may be floating point numbers, e.g., 3.14%0.7 equals 0.34 (since 3.14 equals 4*0.7 + 0.34.) The modulo operator always yields a result with the same sign as its second operand (or zero); the absolute value of the result is strictly smaller than the absolute value of the second operand.


TL;DR:

The modulo function (also called the mod function) basically just gives the remainder of dividing the first no. by the second. So a % b = cmp(b, 0) * abs(a - (a // b)) (I think).

Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
  • This is the simplest solution, along the lines of the original code. (Alternate approach: Write a function that reads five lines from an already open file, and call it over and over till the end of the file. That would work better for more complicated cases.) – alexis Dec 07 '12 at 17:57
  • @alexis How would the 2nd solution be? i think better as it'd require the least modifications for adding a field or anything. And it follows KISS and DRY. – Yatharth Agarwal Dec 07 '12 at 18:37
  • I added it as a new answer. – alexis Dec 07 '12 at 19:00
  • `field = fields[index] + ": "` – You might want to use `fields[index % 5]` here, if you don’t want an IndexError. – poke Dec 08 '12 at 15:42
  • @poke Oops, typo. It should really be `... % len(fields)` – Yatharth Agarwal Dec 08 '12 at 16:00
0

Just another way to look into the problem

fields = ('Name', 'Gender', 'Age', 'Height', 'Weight')
with open("infile.txt") as fin, open("altered.txt","w") as fout:
    while True:
        data =  ''.join(map(':'.join,zip(fields,fin)))
        if data:
            fout.write(data)
        else:
            break
Abhijit
  • 62,056
  • 18
  • 131
  • 204