Put a character at the start of a line in a file? - python-3.x

I am making a song title guessing game, where the songs are fetched from a file, one is selected at random and only the first letter of each word is printed. I am at the stage where this works but I am unsure as to how you stop one of the songs repeating without removing it altogether from the file together. My idea was the print a hashkey in front of the line, and then when it goes back round to the start check if there is a hash. if there is a hashkey, it will select another song from the list. And then at the end of the program when the user gets a guess wrong, I will strip the file of all hashkeys. I am stuck on the part where you actually insert the hashkey. So far the code goes like this:
file.replace(random_song, ("#") + random_song)
When I use this code, Python doesn't display any errors but it also doesn't change the code.
If someone can give me a pointer or suggest a more efficient way of doing it then I would be grateful

I am assuming that the flow of inserting # is as follows:
def insertHash(filepath, song):
with open(filepath, 'r') as f:
lines = f.read()
lines.replace(song, ('#') + song)
with open(filepath, 'w') as f:
f.write(lines)
I think you are missing assigning the replace() back to the string. Replacing the replace line with lines = lines.replace(song, ('#') + song) should solve the issue.
Besides, you can use a set to store the songs that are already displayed to the user. Whenever you select a song, check if it already in the set. If it is, choose another. If not, insert it and display. That way, you won't be writing to a file every time.
shown_songs = set()
def isSongShown(song):
return song in shown_songs
def insertSong(song):
shown_songs.add(song)
if not isSongShown(song):
insertSong()
# display the song
else:
# select a new song
Hope this helps!

Related

How to remove lines from a file starting with a specific word python3

I am doing this as an assignment. So, I need to read a file and remove lines that start with a specific word.
fajl = input("File name:")
rec = input("Word:")
def delete_lines(fajl, rec):
with open(fajl) as file:
text = file.readlines()
print(text)
for word in text:
words = word.split(' ')
first_word = words[0]
for first in word:
if first[0] == rec:
text = text.pop(rec)
return text
print(text)
return text
delete_lines(fajl, rec)
At the last for loop, I completely lost control of what I am doing. Firstly, I can't use pop. So, once I locate the word, I need to somehow delete lines that start with that word. Additionally, there is also one minor problem with my approach and that is that first_word gets me the first word but the , also if it is present.
Example text from a file(file.txt):
This is some text on one line.
The text is irrelevant.
This would be some specific stuff.
However, it is not.
This is just nonsense.
rec = input("Word:") --- This
Output:
The text is irrelevant.
However, it is not.
You cannot modify an array while you are iterating over it. But you can iterate over a copy to modify the original one
fajl = input("File name:")
rec = input("Word:")
def delete_lines(fajl, rec):
with open(fajl) as file:
text = file.readlines()
print(text)
# let's iterate over a copy to modify
# the original one without restrictions
for word in text[:]:
# compare with lowercase to erase This and this
if word.lower().startswith(rec.lower()):
# Remove the line
text.remove(word)
newtext="".join(text) # join all the text
print(newtext) # to see the results in console
# we should now save the file to see the results there
with open(fajl,"w") as file:
file.write(newtext)
print(delete_lines(fajl, rec))
Tested with your sample text. if you want to erase "this". The startswith method will wipe "this" or "this," alike. This will only delete the text and let any blank lines alone. if you don't want them you can also compare with "\n" and remove them

Finding specific object after another one

I am creating a program that extracts the relevant information from a textfile with 500k lines.
What I've managed so far is to take the info from the textfile and make it into a list which each element being a line.
The relevant text is formatted like this:
*A title that informs that the following section will have the data I'm trying to extract *
*Valuable info in random amount of lines*
*-------------------*
and in between each relevant section of information, formatted in the same way but starting with another title i.e:
*A title that shows that this is data I don't want *
*Non-valuable info in random amount of lines *
*------------------- *
I've managed to list the indexes of the starting point with the follow code:
start = [i for i, x in enumerate(lines) if x[0:4] == searchObject1 and x[5:8] == searchObject2]
But I'm struggling to find the stopping points. I can't use the same method used when finding the starting points because the stopping line appears also after non-important info.
I'm quite the newbie to both Python and programming so the solution might be obvious.
A simple solution is to loop over the input file line by line, and keep only valuable lines. To know whether a line is valuable, we use a boolean variable that is:
set to true ("keep the lines") whenever we encounter a title marking the beginning of a section of interesting data,
set to false ("discard the lines") whenever we encounter an end of section mark. The variable is set to discard even when we encounter the end of a useless section, which doesn't change its state.
Here is the code (lines is the list of strings containing the data to parse):
bool keep = false;
data = []
for line in lines:
if line == <title of useful section> # Adapt
keep = true
elif line == <end of section> # Adapt
keep = false
else:
if keep:
data.append(line)
If none of the cases matched, the line was one of two things:
a line of data in a useless section
the title of a useless section
So it can be discarded.
Note that the titles and end of section lines are not saved.

Nested For loop over csv files

I have 2 .csv datasets from the same source. I was attempting to check if any of the items from the first dataset are still present in the second.
#!/usr/bin/python
import csv
import json
import click
#click.group()
def cli(*args, **kwargs):
"""Command line tool to compare and generate a report of item that still persists from one report to the next."""
pass
#click.command(help='Compare the keysets and return a list of keys old keys still active in new keyset.')
#click.option('--inone', '-i', default='keys.csv', help='specify the file of the old keyset')
#click.option('--intwo', '-i2', default='keys2.csv', help='Specify the file of the new keyset')
#click.option('--output', '-o', default='results.json', help='--output, -o, Sets the name of the output.')
def compare(inone, intwo, output):
csvfile = open(inone, 'r')
csvfile2 = open(intwo, 'r')
jsonfile = open(output, 'w')
reader = csv.DictReader(csvfile)
comparator = csv.DictReader(csvfile2)
for line in comparator:
for row in reader:
if row == line:
print('#', end='')
json.dump(row, jsonfile)
jsonfile.write('\n')
print('|', end='')
print('-', end='')
cli.add_command(compare)
if __name__ == '__main__':
cli()
say each csv files has 20 items in it. it will currently iterate 40 times and end when I was expecting it to iterate 400 times and create a report of items remaining.
Everything but the iteration seems to be working. anyone have thoughts on a better approach?
Iterating 40 times sounds just about right - when you iterate through your DictReader, you're essentially iterating through the wrapped file lines, and once you're done iterating it doesn't magically reset to the beginning - the iterator is done.
That means that your code will start iterating over the first item in the comparator (1), then iterate over all items in the reader (20), then get the next line from the comparator(1), then it won't have anything left to iterate over in the reader so it will go to the next comparator line and so on until it loops over the remaining comparator lines (18) - resulting in total of 40 loops.
If you really want to iterate over all of the lines (and memory is not an issue), you can store them as lists and then you get a new iterator whenever you start a for..in loop, so:
reader = list(csv.DictReader(csvfile))
comparator = list(csv.DictReader(csvfile2))
Should give you an instant fix. Alternatively, you can reset your reader 'steam' after the loop with csvfile.seek(0).
That being said, if you're going to compare lines only, and you expect that not many lines will differ, you can load the first line in csv.reader() to get the 'header' and then forgo the csv.DictReader altogether by comparing the lines directly. Then when there is a change you can pop in the line into the csv.reader() to get it properly parsed and then just map it to the headers table to get the var names.
That should be significantly faster on large data sets, plus seeking through the file can give you the benefit of never having the need to store in memory more data than the current I/O buffer.

how would i go about reading a .txt file then ordering the data in descending order

ok so i would like it that when the user wants to check the high scores the output would print the data in descending order keep in mind that there are both names and numbers on the .txt file which is why im finding this so hard. If there is anything else you need please tell me in the
def highscore():
global line #sets global variable
for line in open('score.txt'):
print(line)
#=================================================================================
def new_highscores():
global name, f #sets global variables
if score >= 1:#if score is equal or more than 1 run code below
name = input('what is your name? ')
f = open ('score.txt', 'a') #opens score.txt file and put it into append mode
f.write (str(name)) #write name on .txt file
f.write (' - ') #write - on .txt file
f.write (str(score)) #write score on .txt file
f.write ('\n') #signifies end of line
f.close() #closes .txtfile
if score <= 0: #if score is equal to zero go back to menu 2
menu2()
I added this just in case there was a problem in the way i was writing on the file
The easiest thing to do is just maintain the high scores file in a sorted state. That way every time you want to print it out, just go ahead and do it. When you add a score, sort the list again. Here's a version of new_highscores that accomplishes just that:
def new_highscores():
""""Adds a global variable score to scores.txt after asking for name"""
# not sure you need name and f as global variables without seeing
# the rest of your code. This shouldn't hurt though
global name, f # sets global variables
if score >= 1: # if score is equal or more than 1 run code below
name = input('What is your name? ')
# here is where you do the part I was talking about:
# get the lines from the file
with open('score.txt') as f:
lines = f.readlines()
scores = []
for line in lines:
name_, score_ = line.split(' - ')
# turn score_ from a string to a number
score_ = float(score_)
# store score_ first so that we are sorting by score_ later
scores.append((score_, name_))
# add the data from the user
scores.append((score, name))
# sort the scores
scores.sort(reverse=True)
# erase the file
with open('score.txt', 'w') as f:
# write the new data
for score_, name_ in scores:
f.write('{} - {}\n'.format(name_, score_))
if score <= 0: # if score is equal to zero go back to menu 2
menu2()
You'll notice I'm using the with statement. You can learn more about that here, but essentially it works like this:
with open(filename) as file:
# do stuff with file
# file is **automatically** closed once you get down here.
Even if you leave the block for another reason (an Exception is thrown, you return from a function early, etc.) Using a with statement is a safer way to deal with files, because you're basically letting Python handle the closing of the file for you. And Python will never forget like a programmer will.
You can read more about split and format here and here
P.S., There is a method called binary search that would be more efficient, but I get the feeling you're just starting so I wanted to keep it simple. Essentially what you would do is search for the location in the file where the new score should be inserted by halving the search area at each point. Then when you write back to the file, only write the stuff that's different (from the new score onward.)

How to add the line number at the beginning of each line in a file

So.. I need to read a file and add the line number at the beginning of each line. Just as the title. How do you do it?
For example, if the content of the file was:
This
is
a
simple
test
file
These 6 lines, I should turn it into
1. This
2. is
3. a
4. simple
5. test
6. file
Keep the original content, but just adding the line number at the beginning.
My code looks like this so far:
def add_numbers(filename):
f = open(filename, "w+")
line_number = 1
for line in f.readlines():
number_added = str(line_number) + '. ' + f.readline(line)
line_number += 1
return number_added
But it doesn't really show anything as the result. I have no clues how to do it. Any help?
A few problems I see in your code:
You indentation is not correct. Everything below the def add_numbers(): should be indented one level.
It is good practice to close a file handle at the end of your method.
A similar question to yours was asked here. Looking at the various solutions posted there, using fileinput seems like your best bet because it allows you to edit your file in-place.
import fileinput
def add_numbers(filename):
line_number = 1
for line in fileinput.input(filename, inplace=True):
print("{}. {}".format(line_number, line))
line_number += 1
Also note that I use format to combine two strings instead adding them together, because this handles different variable types more easily. A good explanation of the use of format can be found here.

Resources