So I am trying to make a game where the 'GameMaster' picks the first word from a .txt file, then the user tries to guess the word. Once the user correctly guess the word, the GameMaster looks to the next line in the file and the user has to guess again, so on and so forth...
The problem I am having, is getting the program to assign variables as the game continues. The program should iteratively look until there are no more words to choose from, whether that be 2 or infinity.
Since I don't have much experience working with file interaction in python, the best example I have is something like this:
file "input.txt" will contain:
dog
cat
bird
rat
mouse
And I am looking at what in in the .txt file with this:
def file_read():
with open ('/Users/someone/Desktop/input.txt', 'r') as myfile:
data = myfile.read()
for line in data:
line.rstrip()
return data
Your function returns the entire contents of the file, unaltered. myfile.read() returns the data from the file as a string. The for loop then iterates over every character in that string, not the lines. Furthermore, rstrip() operates only on each character. It does not affect the contents of data because data is an immutable string and the return value of rstrip() is not stored anywhere.
Something like this would better suit:
def file_read():
with open('/Users/someone/Desktop/input.txt') as myfile:
return [line.rstrip() for line in myfile]
This will return a list of the stripped lines from the file. Your word guessing code would then iterate over the list.
The above will work, however, it is not very efficient if the input file is large because all of the file would be read into memory to construct the list. A better way is to use a generator which yields a stripped line one at a time:
def file_read():
with open('/Users/someone/Desktop/input.txt') as myfile:
for line in myfile:
yield line.rstrip()
Now that function is so simple, it seems pointless to bother with it. Your code could simply be:
with open('/Users/someone/Desktop/input.txt') as myfile:
for line in myfile:
user_guess_word(line.rstrip())
where user_guess_word() is a function that interacts with the user to guess what the word is, and returns once the guess it correct.
This way uses readlines to get file contents in a list line by line. readlines returns a list containing lines.
Now iterate through list to check if user input matches with line content (which is a word in this case).
with open ('/Users/someone/Desktop/input.txt', 'r') as myfile:
words = myfile.readlines()
while x < len(words):
if words[x] == input('Enter word to guess'):
print('Predicted word correctly')
else:
print('Wrong word. Try again')
x -= 1
x += 1
You can do it like,
def fun():
data = open('filename', 'r').readlines()
user_guess, i = None, 0
while i < len(data):
user_guess = input()
if user_guess not None and user_guess == data[i]:
i = i + 1
Please trim() / strip() also while you compare user_guess and data[i]
Related
Code:
with open("filename.txt" 'r') as f: #I'm not sure about reading it as r because I would be removing lines.
lines = f.readlines() #stores each line in the txt into 'lines'.
invalid_line_count = 0
for line in lines: #this iterates through each line of the txt file.
if line is invalid:
# something which removes the invalid lines.
invalid_line_count += 1
print("There were " + invalid_line_count + " amount of invalid lines.")
I have a text file like so:
1,2,3,0,0
2,3,0,1,0
0,0,0,1,2
1,0,3,0,0
3,2,1,0,0
The valid line structure is 5 values split by commas.
For a line to be valid, it must have a 1, 2, 3 and two 0's. It doesn't matter in what position these numbers are.
An example of a valid line is 1,2,3,0,0
An example of an invalid line is 1,0,3,0,0, as it does not contain a 2 and has 3 0's instead of 2.
I would like to be able to iterate through the text file and remove invalid lines.
and maybe a little message saying "There were x amount of invalid lines."
Or maybe as suggested:
As you read each line from the original file, test it for validity. If it passes, write it out to the new file. When you're finished, rename the original file to something else, then rename the new file to the original file.
I think that the csv module may help so I read the documentation and it doesn't help me.
Any ideas?
You can't remove lines from a file, per se. Rather, you have to rewrite the file, including only the valid lines. Either close the file after you've read all the data, and reopen in mode "w", or write to a new file as you process the lines (which takes less memory in the short term.
Your main problem with detecting line validity seems to be handling the input. You want to convert the input text to a list of values; this is a skill you should get from learning your tools. The ones you need here are split to divide the line, and int to convert the values. For instance:
line_vals = line.split(',')
Now iterate through line_vals, and convert each to integer with int.
Validity: you need to count the quantity of each value you have in this list. You should be able to count things by value; if not back up to your prior lessons and review basic logic and data flow. If you want the advanced method for this, use collections.Counter, which is a convenient type of dictionary that accumulates counts from any sequence.
Does that get you moving? If you're still lost, I recommend some time with a local tutor.
One of the possible right approaches:
with open('filename.txt', 'r+') as f: # opening file in read/write mode
inv_lines_cnt = 0
valid_list = [0, 0, 1, 2, 3] # sorted list of valid values
lines = f.read().splitlines()
f.seek(0)
f.truncate(0) # truncating the initial file
for l in lines:
if sorted(map(int, l.split(','))) == valid_list:
f.write(l+'\n')
else:
inv_lines_cnt += 1
print("There were {} amount of invalid lines.".format(inv_lines_cnt))
The output:
There were 2 amount of invalid lines.
The final filename.txt contents:
1,2,3,0,0
2,3,0,1,0
3,2,1,0,0
This is a mostly language-independent problem. What you would do is open another file for writing. As you read each line from the original file, test it for validity. If it passes, write it out to the new file. When you're finished, rename the original file to something else, then rename the new file to the original file.
For a line to be valid, each line must have a 1, 2, 3 and 2 0's. It doesn't matter in what position these numbers are.
CHUNK_SIZE = 65536
def _is_valid(line):
"""Check if a line is valid.
A line is valid if it is of length 5 and contains '1', '2', '3',
in any order, as well as '0', twice.
:param list line: The line to check.
:return: True if the line is valid, else False.
:rtype: bool
"""
if len(line) != 5:
# If there's not exactly five elements in the line, return false
return False
if all(x in line for x in {"1", "2", "3"}) and line.count("0") == 2:
# Builtin `all` checks if a condition (in this case `x in line`)
# applies to all elements of a certain iterator.
# `list.count` returns the amount of times a specific
# element appears in it. If "0" appears exactly twice in the line
# and the `all` call returns True, the line is valid.
return True
# If the previous block doesn't execute, the line isn't valid.
return False
def get_valid_lines(path):
"""Get the valid lines from a file.
The valid lines will be written to `path`.
:param str path: The path to the file.
:return: None
:rtype: None
"""
invalid_lines = 0
contents = []
valid_lines = []
with open(path, "r") as f:
# Open the `path` parameter in reading mode.
while True:
chunk = f.read(CHUNK_SIZE)
# Read `CHUNK_SIZE` bytes (65536) from the file.
if not chunk:
# Reaching the end of the file, we get an EOF.
break
contents.append(chunk)
# If the chunk is not empty, add it to the contents.
contents = "".join(contents).split("\n")
# `contents` will be split in chunks of size 65536. We need to join
# them using `str.join`. We then split all of this by newlines, to get
# each individual line.
for line in contents:
if not _is_valid(line=line):
invalid_lines += 1
else:
valid_lines.append(line)
print("Found {} invalid lines".format(invalid_lines))
with open(path, "w") as f:
for line in valid_lines:
f.write(line)
f.write("\n")
I'm splitting this up into two functions, one to check if a line is valid according to your rules, and a second one to manipulate a file. If you want to return the valid lines instead, just remove the second with statement and replace it with return valid_lines.
I have multiple files, each with a line with, say ~10M numbers each. I want to check each file and print a 0 for each file that has numbers repeated and 1 for each that doesn't.
I am using a list for counting frequency. Because of the large amount of numbers per line I want to update the frequency after accepting each number and break as soon as I find a repeated number. While this is simple in C, I have no idea how to do this in Python.
How do I input a line in a word-by-word manner without storing (or taking as input) the whole line?
EDIT: I also need a way for doing this from live input rather than a file.
Read the line, split the line, copy the array result into a set. If the size of the set is less than the size of the array, the file contains repeated elements
with open('filename', 'r') as f:
for line in f:
# Here is where you do what I said above
To read the file word by word, try this
import itertools
def readWords(file_object):
word = ""
for ch in itertools.takewhile(lambda c: bool(c), itertools.imap(file_object.read, itertools.repeat(1))):
if ch.isspace():
if word: # In case of multiple spaces
yield word
word = ""
continue
word += ch
if word:
yield word # Handles last word before EOF
Then you can do:
with open('filename', 'r') as f:
for num in itertools.imap(int, readWords(f)):
# Store the numbers in a set, and use the set to check if the number already exists
This method should also work for streams because it only reads one byte at a time and outputs a single space delimited string from the input stream.
After giving this answer, I've updated this method quite a bit. Have a look
<script src="https://gist.github.com/smac89/bddb27d975c59a5f053256c893630cdc.js"></script>
The way you are asking it is not possible I guess. You can't read word by word as such in python . Something of this can be done:
f = open('words.txt')
for word in f.read().split():
print(word)
I am trying to print a specific line from the file "Scores", which is option B. This is my code:
print("Option A: Show all scores\nOption B: Show a record\nOption Q: Quit")
decision = input("Enter A, B, C or Q: ")
myFile = open("Scores.txt", "rt")
if decision == "A":
record = myFile.read()
print(record)
myFile.close()
elif decision == "B" or decision == "b":
playerName = input("Enter a player name to view their scores: ")
record = myFile.read()
answer = record.find(playerName)
for line in answer:
print(line)
elif decision == "Q" or decision == "q":
exit
I went for Option B, then I entered a player name that holds the score of the player, but it throws this error message:
line 12, in <module>
for line in answer():
TypeError: 'int' object is not callable
A few cents from my side :
file = open("file")
lines = file.readlines()
for line in lines:
if playername in line:
print line
file.close()
Hope it works!
find() method returns a positive index if it succeeds, -1 otherwise
You should loop on your content line by line, as follows:
for line in myFile:
if line.find(playerName):
print(line)
A safer way to read the file and find data, so that you will not have OutOfMemory issues when storing the whole file in memory.
playerName = input("Enter a player name to view their scores: ")
with open("Scores.txt", 'r') as f:
for row in f:
if playerName in row:
print row
This way you will be using with that will close the file by itself either when the program ends or Garbage Collection kicks in. This way python will read the file line by line and store only 1 line in memory. So you can use huge files and do not worry about memory issues.
Hope it helps :)
Working with str methods will take more acrobatics. Try the following,
import re
p = re.compile(r"\b{}\b".format(playername)) # keep it ready
# inside option B
for line in myfile: # no need to `.read()` it
match = p.search(line)
if match:
print(line)
break # if there is only one record for playername
See if it works for you.
similar thing here:
Reading specific lines only (Python)
fp = open("file")
for i, line in enumerate(fp):
if line == playername:
print line
fp.close()
I also notice you don't close your file for each decision, should make that happen.
Few python idioms and small optimization
Here are many answer, my sample brings in few python idioms and optimize it a bit:
fname = "Scores.txt"
player_name = "Quido"
with open(fname) as f:
for line in f:
if player_name in line:
print line
break
print "Going on doing following tasks."
The with block will close the open file on exiting the inner block. No need to f.close(), safe
in case of problems to read the file.
for line in f: shows, that iterating over file open in text mode we get one line per iteration.
break after we print the line with the player will effectively stop iterating over lines assuming,
there is only one such line or that we are happy with the very first one. If this is not the case,
removing the break allows printing all lines containing the player name.
As lines returned from text file iterator contain new line, you may prefer to get rid of them. Use
print line.strip() in such case what will remove all blank characters from start and end of the line.
Final print is proving, the program continues after it processes all the lines.
It may happen, that you get no output for name, which appears to be present in the file. In such a
case, you might need to clarify letter case. For example, if your text file contains all the names
in exact casing, you have to enter the name properly.
Other option is to lower-case the player_name and compare it against lower cased line:
fname = "Scores.txt"
player_name = "Quido"
normalized_player_name = player_name.lower()
with open(fname) as f:
for line in f:
if normalized_player_name in line.lower():
print line.strip()
break # comment out to print all lines with the player
print "Going on doing following tasks."
Note, that we normalize the player_name outside from the loop to be a bit faster. Lower-casing inside the
loop would work, but would do the same task repeatedly.
The line is printed using exact letter cases as in the file.
After being faced with a syntax error for some noticeable time and having realised I made a foolish mistake, I proceeded to correct my way only to encounter a runtime error. So far I'm trying to produce a program which is able to read the amount of words from a file, however, instead of counting the number of words the program seems to count the number of letters which is not benefital for the outcome of my program. Please find the appropriate code below. Thanks for any and all contributions!
def GameStage02():
global FileSelection
global ReadFile
global WordCount
global WrdCount
FileSelection = filedialog.askopenfilename(filetypes=(("*.txt files", ".txt"),("*.txt files", "")))
with open(FileSelection, 'r') as file:
ReadFile = file.read()
SelectTextLabel.destroy()
WrdCount=0
for line in ReadFile:
Words=line.split()
WrdCount=WrdCount+len(Words)
print(WrdCount)
GameStage01Button.config(state=NORMAL)
Let's break it down:
ReadFile = file.read() will give you a string.
for line in ReadFile will iterate over the characters in that string.
Words=line.split() will give you a list with one or zero characters in it.
That's probably not what you want. Change
ReadFile = file.read()
to
ReadFile = file.readlines()
This will give you a list of lines, which you can iterate over and/or split into lists of words.
In addition, note that file is not a good variable name (in Python2), because that's already the name of a builtin.
As a continuation of timgeb's answer, here is a working piece of code that does this:
import re
#open file.txt, read and
#split the file content with \n character as the delimiter(basically as lines)
lines = open('file.txt').read().splitlines()
count = 0
for line in lines:
#split the line with whitespace delimiter and get the list of words in the line
words = re.split(r'\s', line)
count += len(words)
print count
I have to write a program that iterates over each line in a text file and then over each character in each line in order to count the number of entries in each line.
Here is a segment of the text file:
N00000031,B,,D,D,C,B,D,A,A,C,D,C,A,B,A,C,B,C,A,C,C,A,B,D,D,D,B,A,B,A,C,B,,,C,A,A,B,D,D
N00000032,B,A,D,D,C,B,D,A,C,C,D,,A,A,A,C,B,D,A,C,,A,B,D,D
N00000033,B,A,D,D,C,,D,A,C,B,D,B,A,B,C,C,C,D,A,C,A,,B,D,D
N00000034,B,,D,,C,B,A,A,C,C,D,B,A,,A,C,B,A,B,C,A,,B,D,D
The first and last lines are "unusable lines" because they contain too many entries (more or less than 25). I would like to count the amount of unusable lines in the file.
Here is my code:
for line in file:
answers=line.split(",")
i=0
for i in answers:
i+=1
unusable_line=0
for line in file:
if i!=26:
unusable_line+=1
print("Unusable lines in the file:", unusable_line)
I tried using this method as well:
alldata=file.read()
for line in file:
student=alldata.split("\n")
answer=student.split(",")
My problem is each variable I create doesn't exist when I try to run the program. I get a "students" is not defined error.
I know my coding is awful but I'm a beginner. Sorry!!! Thank you and any help at all is appreciated!!!
A simplified code for your method using list,count and if condition
Code:
unusable_line = 0
for line in file:
answers = line.strip().split(",")
if len(answers) < 26:
unusable_line += 1
print("Unusable lines in the file:", unusable_line)
Notes:
Initially I have created a variable to store count of unstable lines unusable_line.
Then I iterate over the lines of the file object.
Then I split the lines at , to create a list.
Then I check if the count of list is less then 26. If so I increment the unusable_line varaiable.
Finally I print it.
You could use something like this and wrap it into a function. You don't need to re-iterate the items in the line, str.split() returns a list[] that has your elements in it, you can count the number of its elements with len()
my_file = open('temp.txt', 'r')
lines_count = usable = ununsable = 0
for line in my_file:
lines_count+=1
if len(line.split(',')) == 26:
usable+=1
else:
ununsable+=1
my_file.close()
print("Processed %d lines, %d usable and %d ununsable" % (lines_count, usable, ununsable))
You can do it much shorter:
with open('my_fike.txt') as fobj:
unusable = sum(1 for line in fobj if len(line.split(',')) != 26)
The line with open('my_fike.txt') as fobj: opens the file for reading and closes it automatically after leaving the indented block. I use a generator expression to go through all lines and add up all that have a length different from 26.