Error trapping combined with a counter not working - python

I'm making a code that makes a user enter RLE line by line. I then send the entered data to a function that decodes it. Within the function, I've included some basic error trapping.
My error is that when the user can only enter incorrect data the number of times they choose (they choose the amount of lines they want to enter). i.e. if the user enters 2 (out of 3) correct lines of RLE and then enters an incorrect line, the code won't ask to enter the RLE again, but if they enter an incorrect line at the first or second input it works.
Code:
if line_amount>2:
print ("Please enter the compressed data one line at a time")
while line_amount > counter:
compressed_data = input('->') #ENTER RLE DATA
counter+=1
RLEtoASCII(compressed_data,counter)
RLEtoASCII:
def RLEtoASCII(compressed_data,counter):
try:
pairs = [(int(compressed_data[i:i+2]), compressed_data[i+2]) for i in range(0, len(compressed_data), 3)]
global text
text = ''.join(n * c for n, c in pairs)
RLE_Inputs = open("Entered Data.txt", 'a+') #File that lists all the inputs
#[etc all the file writing]
except:
print('THERE WAS A PROBLEM WITH THE VALUES Please re-enter values.\n')
If I try calling RLEtoASCII after the except, it creates a loop. counter -=1 doesn't appear to work after the except either...

By moving the error handling out of your function and into the if loop, we can more easily control the iteration:
if line_amount>2:
print ("Please enter the compressed data one line at a time")
while line_amount > counter:
compressed_data = input('->') #ENTER RLE DATA
if compressed_data != '':
try:
RLEtoASCII(compressed_data)
counter+=1
except:
print('THERE WAS A PROBLEM WITH THE VALUES Please re-enter values.\n')
RLEtoASCII:
def RLEtoASCII(compressed_data):
pairs = [(int(compressed_data[i:i+2]), compressed_data[i+2]) for i in range(0, len(compressed_data), 3)]
global text
text = ''.join(n * c for n, c in pairs)
RLE_Inputs = open("Entered Data.txt", 'a+') #File that lists all the inputs
#[etc all the file writing]

Try changing the > in while line_amount > counter: to >=. This is because when you get to the third line it is testing for if 3>3 which is false, while 3>=3 is true.

The problem seems to be that counter is incremented even when you catch an error. I changed counter to a global so that it effectively is't incremented when there is an error. just make sure you put global counter above your definition. Let me know if this works for you.
if line_amount>2:
print ("Please enter the compressed data one line at a time")
while line_amount > counter:
compressed_data = input('->') #ENTER RLE DATA
counter+=1
RLEtoASCII(compressed_data)
RLEtoASCII:
def RLEtoASCII(compressed_data):
global counter
try:
pairs = [(int(compressed_data[i:i+2]), compressed_data[i+2]) for i in range(0, len(compressed_data), 3)]
global text
text = ''.join(n * c for n, c in pairs)
RLE_Inputs = open("Entered Data.txt", 'a+') #File that lists all the inputs
#[etc all the file writing]
except:
print('THERE WAS A PROBLEM WITH THE VALUES Please re-enter values.\n')
counter-=1

Related

Python: Loop to check if an input is valid or not line by line

Given the following inputs that simulate a grid with the respective dimensions:
totLines = int(input("Indicate the number os lines: "))
totColunms = int(input("Indicate the number of columns: "))
I want to check whether the input is valid or not (every digit must be between 0 and 20). The code should check each line of the grid and tell the user if the input is valid and if not ask for another input. The problem is that my code only checks the first digit of line1, the second digit of the line2, and so on. For instance, in a 3x3 grid, if I input the following numbers for line1 (separated by spaces) - 21 10 10 - the code is gonna tell me that the input is not valid, but if I put the incorrect digit in position 2 - 10 21 10 - the code is not gonna find any problem. I know the problem must be related to how I´m doing the loop but I can´t understand how to solve this. This is something easy to do but It´s my first time learning a programming language and I still have a lot to learn. Thanks!
for lin in range (1, totLines+1):
print("Line", str(lin))
while True:
sMoves = input("Movement for this line: ")
listMoves = sMoves.split()
listMovesINT = list(map(int, listMoves))
if listMovesINT[lin-1] > 0 and listMovesINT[lin-1] <= 20:
break
else:
print ("Not a valid integer")
continue
I'd suggest putting the logic to get a valid list of moves in its own function, using all() to test all the values in the list (you don't need the current value of lin or anything else in the main loop to figure this out, and putting this task in its own function makes it easier to focus on it without accidentally mixing in other parts of your program):
def get_movement() -> list[int]:
"""Get a list of movement values between 1 and 20."""
while True:
try:
moves = [int(n) for n in input("Movement for this line:").split()]
if not all(1 <= n <= 20 for n in moves):
raise ValueError("all values must be between 1 and 20")
return moves
except ValueError as e:
print("Not a valid integer:", e)
and then in your main loop you can do:
for lin in range (1, totLines+1):
print("Line", lin)
moves = get_movement()
# do whatever thing with moves
As you noticed, you are "binding" the items in your input to your "board" (sort of the "board"... the totLines variable).
But you really just want to verify one input at a time, right? When the user writes the set of movements for any given line, it doesn't really matter whether it's for the first line of the "board" or for the last. Maybe you want the line index to display a nice prompt message, but that's about it. When it comes to ensuring that the input is valid, you really don't need the line for anything, isn't it?
So it all boils down to verifying that the inputs for any given line are valid, and keep pestering the user until they (all) are. Something like:
valid_input = False
while not valid_input:
sMoves = input("Movement for this line: ")
listMoves = sMoves.split()
listMovesINT = list(map(int, listMoves))
valid_input = all((0 < move <= 20) for move in listMovesINT)
if not valid_input:
print("One of the movements is not valid")
If you wanna use the number of total lines (totLines) to show a little bit more human-friendly prompt and then make the code a bit more like what you have it (with a for loop), you can just wrap the while not valid_input like this:
totLines = int(input("Indicate the number os lines: "))
for lineNum in range(1, totLines + 1):
valid_input = False
while not valid_input:
sMoves = input(f"Movement for line {lineNum}: ")
listMoves = sMoves.split()
listMovesINT = list(map(int, listMoves))
valid_input = all((0 < move <= 20) for move in listMovesINT)
if not valid_input:
print("One of the movements is not valid")
First, your code only check one integer per line.
You have an index but it is a line index (lin-1), not a number index.
Was the "while True" supposed to iterate over numbers. If yes, it should be after the "split()".
Here's a working example:
matrix = []
totlines=3
for i in range( totlines):
line = input( "... ")
numbers = [ int( text) for text in line.split()] # convert list of asciiNumbers to list of 3 integers
for number in numbers:
if (number <= 0) or (number>20):
print( "Invalid number in line", i, ":", numbers)
matrix.append( numbers)
print( matrix)

A program that uses while loop to find average of numbers inputted, and uses a break statement to exit the loop

I would like to write a program that uses a while loop to repeatedly prompt the user for numbers and adds the numbers to a running total. When a blank line is entered, the program should print the average of all the numbers entered. I also would like to use a break statement to exit the while loop.
My Incorrect Work:
y = "\n"
total = 0
k = 0
while True:
x = input("Enter your number here: ")
x = float(x)
total = total + float(x)
k = k + 1
if type(x) != int:
print(total/k)
break
Be aware that the function input() will always outputs a string, so type(input()) != int will always be true.
Try using try-except function, when there is ValueError (example unable to convert blank/letters to float), the exception will be raised and break the loop:
total = 0
k = 0
while True:
x = input("Enter your number here: ")
try:
total += float(x)
k += 1
except ValueError:
if k > 0: #to avoid division by zero
print("Average: ", total/k)
break
Output:
Enter your number here: 3
Enter your number here: 4
Enter your number here: 5
Enter your number here:
Average: 4.0
Bearing in mind the comments already made, here is one such way to perform your task and finishing up when a blank entry is encountered.
total = 0.0
k = 0.0
while True:
x = input("Enter your number here: ")
if (x == " "): # Check for a blank line entry here before attempting to convert to float
print("Average is:", (total/k))
break
x = float(x)
total = total + float(x)
k = k + 1
As noted in the comments, one should check for the blank line entry prior to attempting to convert the entry.
You are immediately casting the value of x that is inputted to a float. So,
if type(x) != int
always is true, meaning the loop breaks after one iteration every time.
Others have already solved your problem in different ways, but I think that explaining our thinking might also be useful.
Currently, your program is not checking correclty the exit condition (empty line is entered instead of a number). When a new line is entered, your program should do one of the two possible scenarios:
when an empty line is entered: print result & exit (break)
else (assume a number is entered): add number to total
No third option is specified, so for now, let's assume that every line will either be an empty line or a number. Will expand it later.
After you decided what to do, the actions should just be easily wrapped in a while True: block - so it should be:
initialize_variables_total_and_count
while True:
read_line
decide_what_to_do:
# in case line was a number
convert_line_to_float
add_float_to_total
increment_count
other_case:
# empty line was entered
calculate_and_print
break
With only two options, you only need to decide once what to do. You can swap around the cases by deciding which condition to check for (and that also results in the other being the "default" behavior for other cases).
It's simpler to check for the line being empty with if line_entered == "":. In this case, any non-empty line is treated like a number, and if it were not one, the float() function will error out and your program crashes.
Checking if a string (the entered line) can be converted to a float is a bit harder. There is just no built-in for that in python, but there is a trick: you can try to convert it to a float, and if that works, it was convertible, and if that errors, it was not. There are other ways too, but this is the simplest - see this question on the topic.
In this case, every number will be added to the total, and every non-number (including the empty line, but also random strings like "asdf") will cause the program to calculate the total and stop.
You can avoid putting both cases into an if-else block by using break or continue. (technicly, you never need to use break or continue, all programs can be written without them. In this case, you could have a boolean variable, named run for example, write while run: and instead of break, do run = False). You can use the fact that both break and continue end the loop early to avoid placing the second case inside an else-block and still have the same behavior (as break and continue already causes skipping the rest of the loop body).
So an example implementation: (testing for == "", not using unstructured control flow)
total = 0
count = 0
run = True
while run:
line = input("Enter your number here: ")
if line == "":
print(total / count)
run = False
else:
total += float(line)
count += 1
I also renamed k to count, x to line and used in-place addition operators.
Another implementation, with break, testing for float with try/except (and re-using that for the entire control flow):
total = 0
count = 0
while True:
line = input("Enter your number here: ")
try:
# order matters here. If the first line errors out, the second won't happen so the count will only be inremented if it was indeed a float
total += float(line)
count += 1
except:
print(f"Average is: {total / count}")
break
Here I removed the run variable, and used a format string to print a bit fancier.
And an example using both continue and break:
total = 0
count = 0
while True:
line = input("Enter your number here: ")
if line != "":
total += float(line)
count += 1
continue
print(f"Average is: {total / count}")
break
You can fancy it a bit with adding more error handling - use three cases:
user entered empty line: print & exit
user entered a number: add to total
user entered something else: ignore line, but tell user what to do
I only provide one example implementation for this, but as you can see, it can be implemented in many ways.
total = 0
count = 0
# good practice to tell the user what to do
print("Average calcuator. Enter numbers one per line to calulate average of, enter empty line to print result & exit!")
while True:
line = input("Enter your number here: ")
if line == "":
print(f"Average is: {total / count}")
break
else:
try:
total += float(line)
count += 1
except ValueError:
print("You should enter a number or an empty line to calculate & exit!")

How to get python to only read the contents of a text file once

I am trying to figure out how to get Python to only read the contents of a .txt file once. It is for a class project to encrypt and decrypt a message using all printable ASCII characters. Doing it this way is not required. This is only my fourth program I have written in Python and I really don't know what I am doing but I enjoy trying to come up with different ways to approach assignments. I hope I have entered everything correctly on here. I know this an easy fix, I just haven't been able to find the answer. The assignment will be an ongoing project for the next 6 weeks. My plan is to make the encryption much more complex (yes I know that you should never use Python for encryption). So I am writing it with a bigger picture in mind.
That being said. If anyone wants to go beyond answering my question, feel free to tear the entire thing apart. Let me know what I've done wrong, why, how I could do it better. I would love to get some feedback.
import random
print("1. Encrypt")
print("2. Decrypt")
print(" ")
selection = int(input("What would you like to do? [1,2]? "))
while selection == 1:
plainText = input('Enter the message you wish to encrypt: ')
# right now the program encrypts the string at random between 1 and 95.
# All of the printable ASCII characters.
# the code below is written for when I can take the parameters of 1-95
# off and any input will simply loop around.
distance = random.randint(1, 95)
if distance < 1 or distance > 95:
print('Try Again')
continue
else:
# saves the random integer or (key)
# directly to a file without the user seeing it.
f = open('..\\Desktop\\encryptPractice\\theKey.txt', 'w+')
for key in range(1):
number = distance
f.write(str(number))
f.close()
code = ""
for ch in plainText:
ordvalue = ord(ch)
ordvalue = ordvalue + distance
while ordvalue < 32:
ordvalue += 95
while ordvalue > 126:
ordvalue -= 95
code += chr(ordvalue)
# saves the encrypted message
# directly to a file without the user seeing it.
f = open('..\\Desktop\\encryptPractice\\theMessage.txt', 'w+')
for theMessage in range(1):
secret = code
f.write(str(secret))
f.close()
print('Your message has been saved to the file named theMessage.txt')
break
# This is the decryption block - OPTION
# 2.)*********************************************
while selection == 2:
"""
I want to simply be able to open the file with the 'encrypted'
message on it and then open the file with the 'key' on it and
have the program decrypt the message and save it back to the
same file.
Both of the solutions below cause the program to read the
'encrypted' message over and over and over and...you get it.
"""
f = open('..Desktop\\encryptPractice\\theMessage.txt','r')
for line in f:
print(line)
f = open('..Desktop\\encryptPractice\\theMessage.txt','r')
while True:
line = f.readline()
if line == ""
break
print(line)
It's hard to understand your code because it's not indented properly. But here's my guess: the lines while selection == 1: and while selection == 2: make the block of code run in a loop until the value of selection changes. If think you are looking for if selection == 1: and if selection == 2:.
Now a couple other comments about the code you shared:
It's impossible for distance to be less than 1 or more than 95 because randint(1, 95) returns an integer in the range [1, 95].
distance = random.randint(1, 95)
if distance < 1 or distance > 95:
print('Try Again')
continue
You are using a for loop that iterates over a sequence of size 1 (range(1)):
for theMessage in range(1):
secret = code
f.write(str(secret))
This block can be reduced to f.write(str(code))

How can I terminate a loop when pressing Enter

I want to write a program that repeatedly asks the user to enter an integer or to terminate input by pressing Enter key, and then prints the even integers from those numbers entered.
Now, I am pretty much done with this program, I've mentioned the code I've come up with below. I am facing only one problem: how can I terminate the program when the users presses the Enter key?
def evenMem(aList):
mnew = []
for i in aList:
if (i % 2) == 0:
mnew.append(i)
return mnew
def main():
m = []
while True:
n = int(input('Enter a number: '))
m.append(n)
print(evenMem(m))
main()
In case you're using Python 3.x, make the while loop look like this:
while True:
line = input('Enter a number: ')
if not line:
break
n = int(line)
m.append(n)
You might want to surround the conversion to int with a try-catch to handle the case where the user enters something which is not parseable as an int.
With Python 2.x, the input() function will raise an exception if the input is empty (or EOF), so you could do this instead:
while True:
try:
n = int(input('Enter a number: '))
except:
break
m.append(n)

How to append a list with raw_input in Python and print min and max values

I am trying to build a list with raw inputs.
while True:
inp = raw_input("Enter a number: ")
#edge cases
if inp == "done" : break
if len(inp) < 1 : break
#building list
try:
num_list = []
num = int(inp)
num_list.append(num)
except:
print "Please enter a number."
continue
#max and min functions
high = max(num_list)
low = min(num_list)
#print results
print "The highest number: ", high
print "The lowest number: ", low
print "Done!"
At the moment it seems to only save one of the inputs at a time and therefore the the last raw input is printed as both the max and min.
Any ideas? I am new to Python and could use some direction. So far I have been unable to find the answer in StackOverflow or in the Python documentation.
Thanks in advance!
That's because you keep erasing the list with each iteration. Put num_list = [] outside the while loop:
num_list = []
while True:
...
You should also put these two lines:
high = max(num_list)
low = min(num_list)
outside the loop. There is no reason to keep executing them over and over again.

Categories