How to detect EOF when input is not from a file? - python

I'm working through the HackerRank 30 days of code and I'm on day 8. The challenge is to take n lines of standard input of the form name phone-number and add these to a dictionary in key : value pairs of the format name : phone-number.
That part was fine.
The next part of the input is an arbitrary number of lines, each containing a name. The task is to print the phone-number of each name or print "Not found" if the name is not in the dictionary.
My trouble lies in determining the end of the input.
The second part of my code is as follows:
counter = 0 # To prevent infinite loop
while 1:
query = input()
if query in phone_book:
print("{}={}".format(query, phone_book[query]))
elif query not in phone_book:
print("Not found")
else:
break
counter += 1
if counter == 10000000:
break
The if and elif statements check whether or not the name is in the dictionary and the else statement is meant to break out of the loop if there is no more input.
However, I get an EOFError: EOF when reading a line error. My code passes all the tests, but I know there should be a better way to deal with the EOF than just setting an upper limit of 10000000 lines (if the input was more than 10000000 lines, I could just increase the counter limit, but I know that is not a good way of doing this).
I've looked at this page: How to find out whether a file is at its `eof`?
But I don't know how to implement this in my code, since the HackerRank input doesn't seem to be in a file from which I can read lines.
How could I manage the EOF issue and eliminate the need for a counter?
NB. The link to the HackerRank page: https://www.hackerrank.com/challenges/30-dictionaries-and-maps/problem

Just iterate over sys.stdin; then there is no need to explicitly check for the end of the input; the iterator will raise StopIteration when it reaches the ed of the input.
import sys
for query in sys.stdin:
query = query.strip()
if query in phone_book:
print("{}={}".format(query, phone_book[query]))
else:
print("Not found")

Related

How can I check for a string in a list of tuples and only output once if not found?

I have a list of tuples, holding information about people (one for each tuple, with things like (name, age, etc.)). I want to check the list to see whether any names match a user input. My problem is if I use a for loop I then get multiple lines returning false, rather than just one. I also cannot then ask the user to try again, until success. My current code is:
last_name = input("Please input person's last name")
for person in personList:
if person[0] == last_name.capitalize():
print("success")
else:
print("fail")
This will print out "fail" for each player, rather than just once, and will not prompt a user to try again. I know a while loop would enable multiple attempts but I can't see how to link the while with the for, and still output a "fail" only once.
As I'm trying to learn more about tuples, please don't suggest using objects. I know it would make a lot more sense but it doesn't help me understand tuples.
You need two modifications: a way to stop the loop if you find a match, and a way to print 'fail' only if you found no matches in the entire list.
You can get the first modification by adding a break in the if statement, and you can get the second one by adding an else clause to the for loop, which means "run this code if the loop ran to its full completion".
for person in personList:
if person[0] == last_name.capitalize():
print("success")
break
else:
print("fail")
You could simplify checking if user input value is in personList to one line like so and then check whether input matched at least once and if it did print 'success' and break loop, else print 'fail' and ask user again.
personList = [('Abc', 'Cba'), ('Xyz', 'Zyx')]
while True:
last_name = input("Please input person's last name: ").capitalize()
if any(last_name == i[0] for i in personList):
print("success")
break
else:
print("fail")
Output:
Please input person's last name: random
fail
Please input person's last name: xyz
success
So first of all lets understand whats happening.
For each person in the tuple you ask if his name is X.
So accordingly each person will answer you: "No", until you get to the right person, and only that person will say: "Yes", and even further, unless he is the last one it will go on until the very end.
In conclusion, you're asking every single tuple to say whether it matches the user input, or not.
But there is also an easy way of fixing this. So what can we do instead?
We will just collect every answer, and then check whether our input exists in the collection.
Lets write down in code:
total_collection = []
for person in personList:
if person[0] == last_name.capitalize():
total_collection.append("1")
else:
total_collection.append("0")
if "1" in total_collection:
print("Success!")
else:
print("Fail...")
In this code, the string "1" represents a match, and the string "0" represents no-match.
Also, this way you can say at which index the match/es was/were located.

Search in text file by date

I have code like this:
from datetime import datetime
from tabulate import tabulate
def search_projection_date():
projections = open('projections.txt','r').readlines()
date = input("Input projection date: ")
try:
date = date.strptime(date, "%d.%m.%Y.")
except:
print("Date input error!")
#search_projection_date()
for i in projections:
projections = i.strip("\n").split("|")
if date == projections[2]:
table = [['Code:',projections[0]],['Hall:',projections[1]],['Date:',projections[2]],['Start time:',projections[3]],['End time:', projections[4]],['Day(s):', projections[5]], ['Movie:', projections[6]], ['Price:', projections[7]]]
print (tabulate(table))
#break
else:
print("No projection on that date")
And text file like this:
0001|AAA|17.12.2017.|20:30|21:00|sunday|For a few dolars more|150
0002|BBB|17.12.2017.|19:30|21:15|saturday|March on the Drina|300
0003|GGG|19.12.2017.|18:00|19:00|tuesday|A fistful of Dolars|500
0004|GGG|16.12.2017.|21:15|00:00|sunday|The Good, the Bad and the Ugly|350
I try to search movie projections by date...
If there is a projection on the entered date it will find it and print the list, but before printing that list it will always print "Date input error" and after that list "No projection on that date". (if I put break in if statement it will print only the first found projection on entered day, withouth else statement, obvious)
Questions: How to print ONLY list of projections without "date input error" if date is correctly input.
How to print only "No projection on that date" if date is correct but there is no projection and how to ask user for input that until puts it correctly? In this way with recursion it will always throw exception and recursion search_projection_date() function.
There are a whole bunch of major problems with your code. As it happens, they showcase why some general advice we hear so often is actually good advice.
The line date = input("Input projection date: ") creates a string named date. input always returns a string. Strings in Python do not have a method called strptime. Which brings us to issue #2:
You should not catch generic exceptions. You were probably looking to trap a TypeError or ValueError in the except clause. However, you are getting an error that says AttributeError: 'str' object has no attribute 'strptime'. This is because you can't call methods that you want to exist but don't. Your except line should probably read something like except ValueError:.
Your except clause does nothing useful (beyond the problems listed above). If the string is not formatted correctly, you print a message but continue anyway. You probably want to use raise in the except clause to propagate the exception further. Luckily for you, you actually want the date to be a string, which brings us to issue #4:
Why are you attempting to convert the string to a date to begin with? You can not compare a date object and a string that you get from the file and ever expect them to be equal. You want to compare a string to a string. If you had some kind of validation in mind, that's fine, but use datetime.strptime and don't replace the original string; just raise an error if it doesn't convert properly.
The else clause in a for loop will execute whenever the loop terminates normally (i.e., without a break). Since you always iterate through all the lines, you will always trigger the else clause. You need to have another way to determine if you found matching items, like a boolean flag or a counter. I will show an example with a counter, since it is more general.
You never close your input file. Not a huge problem in this tiny example, but can cause major issues with bigger programs. Use a with block instead of raw open.
Your method of iterating through the file is not wrong per-se, but is inefficient. You load the entire file into memory, and then iterate over the lines. In Python, text files are already iterable over the lines, which is much more efficient since it only loads one line at a time into memory, and also doesn't make you process the file twice.
Combining all that, you can make your code look like this:
def search_projection_date():
counter = 0
with open('projections.txt','r') as projections:
date = input("Input projection date: ")
for line in projections:
projection = line.strip("\n").split("|")
if date == projection[2]:
table = [['Code:',projection[0]],
['Hall:',projection[1]],
['Date:',projection[2]],
['Start time:',projection[3]],
['End time:', projection[4]],
['Day(s):', projection[5]],
['Movie:', projection[6]],
['Price:', projection[7]]]
print(tabulate(table))
counter += 1
if not counter:
print("No projection on that date")
else:
print("Found {0} projections on {1}".format(counter, date))
I trusted your use of tabulate since I am not familiar with the module and have no intention of installing it. Keep in mind that the date verification is optional. If the user enters an invalid string, that's the end of it: you don't need to check for dates like 'aaaa' because they will just print No projection on that date. If you insist on keeping the verification, do it more like this:
from datetime import datetime
datetime.strftime(date, '%d.%m.%Y.')
That's it. It will raise a ValueError if the date does not match. You don't need to do anything with the result. If you want to change the error message, or return instead of raising an error, you can catch the exception:
try:
datetime.strftime(date, '%d.%m.%Y.')
except ValueError:
print('Bad date entered')
return
Notice that I am catching a very specific type of exception here, not using a generic except clause.

Python "In" Comparison with *$("!£*!'. characters

Seems while learning as I'm going with Python that I'm running into every little roadblock I can.
Even though this is a simply "if" I can't seem to get it to work, and i was wondering if its because my input (pgName) has full stops within the string. e.g com.android.driver.software.
In my example scenario, I will enter com.android.driver.software, what is listed within the Dict.value is com.android.driver.software.7 I thought using the comparison "in" would work in this instance, but it doesn't seem to be handling the logic at all.. What am i doing wrong?
pgName = raw_input("Please enter a package name e.g com.android.driver.software: ")
#while loop that checks user input from pgName to see if it matches any device
#listed in the JSON output and if so printing all the entires for that value.
while True:
try:
jdata = data["result"]["items"][y]["tagValues"]["IdDevicesMap"]["value"]
for device in jdata.values():
if pgName in device:
print jdata[device]
print " "
else:
print 'false'
When i run it everything is false.

Code Stuck in a Loop and Need to Strip Characters from a File

I am trying to code a program that asks the user what the value of a variable is, opens a file, searches for a word, and then checks to see if the number after that word is equal to the user inputted variable, which means I have to get rid of the spaces in between that specific word and the number after it. Unfortunately, I am having some problems with my code and can’t figure out why it will not work. I am sorry if the question I am asking is fairly simple, I haven't coded in python in over a year and am extremely rusty to say the least.
def getword1(prompt):
while True:
filestr1 = input(prompt)
def getword2(prompt):
while True:
filestr2 = input(prompt)
def getword3(prompt):
while True:
filestr3 = input(prompt)
def openfile(prompt, missingfileerror):
"""Opens a file"""
while True:
try:
filestr = input(prompt)
return open(filestr)
except FileNotFoundError:
print(missingfileerror)
uservariable1 = getword1("What is the value of the first variable? If not applicable, please enter 0")
variable1search = ("Word1", uservariable1)
uservariable2 = getword2("What is the value of the second variable? If not applicable, please enter 0")
variable2search = ("Word2", uservariable2)
uservariable3 = getword3("What is the value of the third variable? If not applicable, please enter 0")
variable3search = ("Word3", uservariable3)
file = openfile("Enter the name of the file that contains the variables.")
if uservariable1 == ("0"):
print("No uservariable1")
else:
if variable1search in file:
print("The variable values match.")
else:
print("The variable values do not match.")
if uservariable2 == ("0"):
print("No uservariable2")
else:
if variable2search in file:
print("The variable values match.")
else:
print("The variable values do not match.")
if uservariable3 == ("0"):
print("No uservariable3")
else:
if variable3search in file:
print("The variable values match.")
else:
print("The variable values do not match.")
file.close()
When I run the code in terminal, the code asks my first question, but after I give it an answer, it is stuck in a loop of asking me the same first question over and over again. I also cannot remember how to properly use the .strip() function in this instance. I am searching for a string as my variable because the value of the variable will include a comma, such as 750,000 or 2,000. If I can strip the comma and also maybe a $ sign from the opened file, I would be able to search for an int instead of a string. Thanks for your time!
Remove the while loop from the openfile function. It has no use.
def openfile(prompt, missingfileerror):
"""Opens a file"""
try:
filestr = input(prompt)
return open(filestr)
except FileNotFoundError:
print(missingfileerror)
NB. One other slight problem with your setup is that you should always close files that you've opened. But in this function, you cannot do that anymore, because the code after return will not be executed (if there are no errors).
Another problem is that your code is not DRY (look up the principle). You've duplicated many functions that are almost identical. There is probably a way to do this in 1/3 of the code.
Another problem is that you have added while loops in getword<x> as well, they are not needed either.
To answer your first point, the code is getting stuck asking the first question repeatedly because of your while True in getword1().
There is an also an awful lot wrong with the rest of your code:
Unnecessary while loops everywhere
You don't return anything from your getword() functions, (so uservariable1 etc. will end up as None)
You don't need 3 separate getword() functions, one would do
Actually, you don't really need any, (if you're not going to do any validation), you could just write uservariable1 = input("What is the value ...") etc.
if variable2search in file will never return True - you need to iterate over the file's contents
If you do want to do some user input validation, you could try something like:
def getword(prompt):
while True:
foo = input(prompt)
if <some evaluation criteron here>:
return foo #Return input and break out of while loop
I suggest reading up on while and file access, as a minimum.
Finally, try writing your code in small chunks and proving each bit works before moving on to the next, it's easier to debug that way.

Python - if and else inside for loop with count not working

This is a simple login program - the logic works in VB.Net but I cannot translate it into Python. The Python code does NOT work, however the VB.Net code provided by two users (un_lucky and Jerry) on Stackoverflow does. Can anyone point out a fix for the python code for me please? It basically produces a series of "Access Denied" instead of just one, when the password is incorrect, and if the username or password is the SECOND in the array, then it produces one Access Denied followed by an Access Granted.
count=0
for counter in range(0,len(fields)):
if textlogin.get()==fields[counter] and textpassword.get()==fields[counter+1]:
count=count+1
if count > 0:
welcome=Label(myGui,text="Access Granted. Loading Profile ....")
welcome.pack()
else:
denied=Label(myGui,text="Access Denied")
denied.pack()
and here is the VB.net code (logic) that does work to achieve pretty much the same thing except that the python program above is reading from a text file.
It now works perfectly (nearly) but a blank entry in username and password also produces "Access Granted"...can't figure out why!
Whole code below:
def verifylogin():
fin=open("moosebook.txt","r")
data=fin.readlines()
for line in data:
fields=line.split()
fields=[i.rstrip("','") for i in fields] #strips the named character from END of field
fields=[i.replace("'",'') for i in fields]#when reading the list, you want to remoe the ',' so it isn't part of the username or password
fields=[i.replace("(",'') for i in fields] #simiarly, remove the bracket and replace it
fields=[i.replace(")",'') for i in fields] #simiarly, remove the bracket and replace it
line=line.rstrip()
print(fields)
flag=0
for counter in range(0,len(fields)):
if textlogin.get()==fields[counter] and textpassword.get()==fields[counter+1]:
flag=flag+1
if flag>0:
welcome=Label(myGui,text="Access Granted. Loading Profile ....")
welcome.pack()
else:
denied=Label(myGui,text="Access Denied")
denied.pack()
Indentation is important in Python. You put your if test inside the loop. You need to remove the extra indentation to place it outside the loop:
for counter in range(0,len(fields)):
if textlogin.get()==fields[counter] and textpassword.get()==fields[counter+1]:
count=count+1
if count > 0:
welcome=Label(myGui,text="Access Granted. Loading Profile ....")
welcome.pack()
else:
denied=Label(myGui,text="Access Denied")
denied.pack()
Note how the if and else lines now start at the same column as the for line.
Because in your code the if statement was indented to a deeper level (the same as the if test checking for the password) you created the labels multiple times, once for each run through the loop.
Indentation works to delimit blocks of code the same way that the VB.NET code uses Next and End If to delimit the loop and the conditional tests explicitly.
The loop can probably be cleaned up; calling textlogin.get() and textpassword.get() just the once, and using zip() to pair up the field values, and any() to see if there is a first match (which is enough here):
login, pw = textlogin.get(), textpassword.get()
if any(pair == (login, pw) for pair in zip(fields, fields[1:])):
welcome=Label(myGui,text="Access Granted. Loading Profile ....")
welcome.pack()
else:
denied=Label(myGui,text="Access Denied")
denied.pack()
If you meant to loop through the fields as pairs (and not as a sliding window), then use:
login, pw = textlogin.get(), textpassword.get()
fields_iter = iter(fields)
if any(pair == (login, pw) for pair in zip(fields_iter, fields_iter)):
welcome=Label(myGui,text="Access Granted. Loading Profile ....")
welcome.pack()
else:
denied=Label(myGui,text="Access Denied")
denied.pack()

Categories

Resources