Home > Software engineering >  Looping through a txt file
Looping through a txt file

Time:10-23

I have a txt file with a list of artists, songs, and genres in the following format:

song 1
genre 1 
artist 1

song 2
genre 2
artist 2

etc.

I'm given an artists name and if the artist is in the file I have to return the name of their song. The code I've managed to write is:

afile = open('music.txt')
header = afile.readline()
artists = afile.readlines()
afile.close()

for art in artists:
    if art == artist:

How could I get the name of the song that is two lines above the artists name? It's also possible that an artists appears several times with different songs.

CodePudding user response:

First, read your file into a list. I'm assuming the format of your file is fixed: it contains

  • A line specifying song name
  • A line specifying genre
  • A line specifying artist
  • A blank line
  • Repeat

Note that since there doesn't seem to be a header, you don't need the initial header = afile.readline()

Let's say you read all the lines of your file into a list called lines

lines = [line.strip() for line in afile] 
# You could also do 
# lines = afile.readlines()
# but that would leave behind trailing line breaks at the end of each line

Now, you know that

  • Starting at the first line, every fourth line is the song name. So slice the lines list to take every fourth line, starting at the first line and save it as a list called songs
songs = lines[0::4]
  • Do the same thing for the other information:
genres = lines[1::4]
artists = lines[2::4]

Now, we can zip() these lists to iterate over them simultaneously, and print the songs for the artists that match the one we're looking for:

look_for_artist = "artist 2"

print(f"Songs by {look_for_artist}:")
for artist, genre, song in zip(artists, genres, songs):
    if artist == look_for_artist:
        print(song, genre)
        # if you know that every artist has only one song, you can break the loop here since you found it already
        # break   

If you were doing this for a bunch of artists, I'd recommend you read the data into a dictionary (or a collections.defaultdict) first. Then, you can look up the value of the dictionary for the given artist and this will be much faster than looping over the lists.

To account for the case when a single artist can have multiple songs, we're going to use a dictionary where the keys are the artist's name, and values are a list containing all the songs by them.

import collections
lookup_dict = collections.defaultdict(list)
for artist, genre, song in zip(artists, genres, songs):
    lookup_dict[artist].append((genre, song))

Then, all you need to do is:

for genre, song in lookup_dict[look_for_artist]:
   print(song, genre)

You can remove the need to read the entire file into a list and then process it into a dictionary by reading the file line-by-line in groups of four lines, but I will leave that as an exercise for you.

CodePudding user response:

Assuming that each artist has only one song (or you are searching for the first match) you could solve it like this:


def check_artist(chosen_artist):
    afile = open('music.txt')
    while afile:
        song = afile.readline()
        afile.readline() # Ignore the second line
        artist = afile.readline()
        if atrist == chosen_artist:
            return song.split("\n")
        afile.readline() # Ignore the empty line
    afile.close()
    return "The artists do not have a song"

CodePudding user response:

Start at the second element (as that is where the first artist is) and scan every 4th element for the artist. If the i-th element of linelist matches artist, print the song (which is at i-2).

for i in range(2, 100, 4):
    if linelist[i] == artist:
        print(linelist[i-2])

CodePudding user response:

Yes, that is normal behavior. You basically read to the end of the file the first time (you can sort of picture it as reading a tape), so you can't read any more from it unless you reset it, by either using f.seek(0) to reposition to the start of the file, or to close it and then open it again which will start from the beginning of the file.

If you prefer you can use the with syntax instead which will automatically close the file for you.

***Eg.`with open('baby1990.html', 'rU') as f:

for line in f:

 print liner code `,

once this block is finished executing, the file is automatically closed for you, so you could execute this block repeatedly without explicitly closing the file yourself and read the file this way over again

  • Related