Implementing error message for code in Python - python

def parse(expression):
operators= set("*/+-")
numbers= set("0123456789")#not used anywhere as of now
opExtrapolate= []
numExtrapolate= []
buff=[]
for i in expression:
if i in operators:
if len(buff) >0: #prevents buff if multiple operators
numExtrapolate.append(''.join(buff))
buff= []
opExtrapolate.append(i)
opExtrapolation=opExtrapolate
else:
buff.append(i)
numExtrapolate.append(''.join(buff))
numExtrapolation=numExtrapolate
print(numExtrapolation)
print("z:", len(opExtrapolation))
return numExtrapolation, opExtrapolation
def errors():
numExtrapolation,opExtrapolation=parse(expression)
#Error for muliple operators
if (len(numExtrapolation) ==3) and (len(opExtrapolation) !=2):
print("Bad1")
if (len(numExtrapolation) ==2) and (len(opExtrapolation) !=1):
print("Bad2")
#
I posted similar code in an older question however the premise for questions is different in this post.
The code above takes a mathematical input entered in a variable expression by the user and it splits it into operands and operators. The errors function will later print errors if the input is incorrect.
Input would look something like this , where the operators can only be in the set("*/+-") and operands are real numbers. so an example input would be 45/23+233
With the help of an SO user I was able to get one of the errors to work(error for multiple operators), but I am having trouble implementing a few more error messages.
1)If the input contains items that are not numbers or not the allowed operators then an error message is displayed
2)If a user enters a number such as .23 or something like 554. where there is no number before the decimal place or after the decimal place then a different error is displayed.(note that a number like 0.23 is fine).
3)If the user attempts to divide by zero an error is displayed.
::What I have tried:
In the else statement of parse(), I tried to put conditions on buff.append(i) so that it would only run that code if buff.isdigit()==true but I got errors saying that there were no digits in buff. I also tried creating a set called "numbers"(in code below) and limiting buff.append(i) to that set through a for statement similar to the initial for statement. But unfortunately nothing worked. Any and all help would be appreciated.
Please don't introduce large amounts of code more advanced than the code below. I am trying to fix a problem, not completely change my code. Thanks for all your help.

You can use regular expressions to do these checks:
If the input contains items that are not numbers or not the allowed operators then an error message is displayed
if not re.match(r'[\d.*/+\- ]+$', expression):
print("Bad3") # Characters exist that are not allowed
Explanation: [\d.*/+\- ] will only match digits, your operators, and spaces, the + means to allow one or more of those characters, and the $ matches at the very end of the string. re.match() starts at the beginning of the string so this means that only those characters are allowed.
If a user enters a number such as .23 or something like 554. where there is no number before the decimal place or after the decimal place then a different error is displayed.(note that a number like 0.23 is fine).
if re.search(r'(?<!\d)\.|\.(?!\d)', expression):
print("Bad4") # There is a '.' without a digit before or after it
Explanation: \. in a regex matches a literal '.' character. The | in the middle is an alternation, so the regex will match if the expression on either side of it matches. (?<!\d) means that the previous character is not a number, and (?!\d) means that the next character is not a number, so this regex means "match a '.' that is not preceeded by a digit OR match a '.' that is not followed by a digit".
If the user attempts to divide by zero an error is displayed.
if re.search(r'/ *[0.]+(?![.\d])', expression):
print("Bad5") # Division by 0
Explanation: This matches / followed by any number of spaces, then one or more 0 or . characters, so this will match if anywhere in expression you have something like / 0, / 0.0, or / 0.00. The (?![.\d]) means that the next character can't be a digit or ., which will prevent you from matching something like / 0.4.

I will give you some indications, but not solve it for you :).
If you need more, ask a precise question and I'll answer it.
The answers I give you are NOT directly related with your code.
You can test if a string variable can be an integer by trying to cast it :
try:
var2 = int(var)
I let you see what Error it gives
For a version that doesn't use try, you can look at the isdigit method
You can see if a string variable if one of your operator by checking it
if (var in ["+", "-", "/", "*"])
to check even more, you can look at the variable's length first
if len(var) != and ... see above
To check if a user inputs something like .543 and refuse it, and can look at the first element of your string variable :
if myvar[0] is ".":
To check if your user wants to divide by 0, you can simply check whether the last number is equals to 0
if int(myvar) == 0:
All these expect you to be able to get operators and numbers first though.
The other solution would be to use regular expressions to perform these checks before parsing your numbers and operators.
It seems quite complex compared to the exercise you are trying to achieve though as it is homework. Might be a good idea to look at them anyway.

Related

Regex python help for coordinate format

I'm working on a program that takes the input in a particular format:
example "(1,2)(2,3)(4,3)". They are coordinates and there can be infinitely many coordinates "(1,2)(2,3)(4,3)...(a,b)". I'm writing a function "checkFormat(str)" that returns true if the format is satisfied. I've tried writing a function without the use of regex but it proved too difficult. Need help with the regex expression.
Use ^ and $ to match the whole input. in between is one or more set of (...) filled with digits.
Assuming coordinates are integer and no extra space in between:
^((\((\d)+\,(\d)+)\))+$
if +/- is allowed and 0 has no sign and could not be extended (00 or 01 not accepted)
^(\(([-\+]?[1-9]\d*|0)\,(([-\+]?[1-9]\d*)|0)\))+$
If decimal numbers are included:
^(\(([-\+]?[1-9]\d*|0)([.]\d+)?\,(([-\+]?[1-9]\d*)|0)([.]\d+)?\))+$
To check if the input match or not:
import re
pattern=r'^(\(([-\+]?[1-9]\d*|0)([.]\d+)?\,(([-\+]?[1-9]\d*)|0)([.]\d+)?\))+$'
input='(0,2)(1,2)'
result=bool(re.match(pattern,input))

How to separate user's input with two separators? And controlling the users input

I want to separate the users input using two different separators which are ":" and ";"
Like the user should input 4 subject and it's amounts. The format should be:
(Subject:amount;Subject:amount;Subject:amount;Subject:amount)
If the input is wrong it should print "Invalid Input "
Here's my code but I can only used one separator and how can I control the users input?
B = input("Enter 4 subjects and amount separated by (;) like Math:90;Science:80:").split(";")
Please help. I can't figure it out.
If you are fine with using regular expressions in python you could use the following code:
import re
output_list = re.split("[;:]", input_string)
Where inside the square brackets you include all the characters (also known as delimiters) that you want to split by, just make sure to keep the quotes around the square brackets as that makes a regex string (what we are using to tell the computer what to split)
Further reading on regex can be found here if you feel like it: https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
However, if you want to do it without importing anything you could do this, which is another possible solution (and I would recommend against, but it gets the job done well):
input_string = input_string.replace(";", ":")
output_list = input_string.split(":")
Which works by first replacing all of the semicolons in the input string with colons (it could also work the other way around) and then splitting by the remaining character (in this case the colons)
Hope this helped, as it is my first answer on Stack overflow.

A few questions about string formating in python. Alignment

FIRST QUESTION
For example, if i want to print a lot of lines with the same width, i could use
print(f'{"INFO":=^50}')
print(f'{"some info":<50}')
print(f'{"another info":>50}')
And will get
=======================INFO=======================
some info
another info
But, what if I want to get something like this?
=======================INFO=======================
some info.............................another info
Ok.
I can do it
print(f'{"INFO":=^50}')
print('some info' + f'{"another info":.>{50-len("some info")}}')
Maybe python has another, the easiest way to do it?
SECOND QUESTION
For align we can use >, <, ^, and =
And = works only with numbers. And it works the same as >
For example
print(f'{13:.=5}')
print(f'{13:.>5}')
...13
...13
So Why do we need =, if it works the same? To be sure that the value is a number? What are the pluses it gives more?
For your second question, the answer is in Format Specification Mini-Language:
'='
Forces the padding to be placed after the sign (if any) but before the
digits. This is used for printing fields in the form ‘+000000120’.
This alignment option is only valid for numeric types. It becomes the
default when ‘0’ immediately precedes the field width.
This becomes clear when you have a signed number:
print(f'{-13:0=5}')
# -0013
print(f'{-13:0>5}')
# 00-13
What you are trying to do is an alignment inbetween two variables. That's quite specific. What then about alignment between three variables, four etc... ?
You can however approach it as an alignment problem for each of the two variables: split the 50 in two parts.
print(f'{"INFO":=^50}')
print(f'{"some info":.<25}{"another info":.>25}')
=======================INFO=======================
some info.............................another info

Indexing the wrong character for an expression

My program seems to be indexing the wrong character or not at all.
I wrote a basic calculator that allows expressions to be used. It works by having the user enter the expression, then turning it into a list, and indexing the first number at position 0 and then using try/except statements to index number2 and the operator. All this is in a while loop that is finished when the user enters done at the prompt.
The program seems to work fine if I type the expression like this "1+1" but if I add spaces "1 + 1" it cannot index it or it ends up indexing the operator if I do "1+1" followed by "1 + 1".
I have asked in a group chat before and someone told me to use tokenization instead of my method, but I want to understand why my program is not running properly before moving on to something else.
Here is my code:
https://hastebin.com/umabukotab.py
Thank you!
Strings are basically lists of characters. 1+1 contains three characters, whereas 1 + 1 contains five, because of the two added spaces. Thus, when you access the third character in this longer string, you're actually accessing the middle element.
Parsing input is often not easy, and certainly parsing arithmetic expressions can get tricky quite quickly. Removing spaces from the input, as suggested by #Sethroph is a viable solution, but will only go that far. If you all of a sudden need to support stuff like 1+2+3, it will still break.
Another solution would be to split your input on the operator. For example:
input = '1 + 2'
terms = input.split('+') # ['1 ', ' 2'] note the spaces
terms = map(int, terms) # [1, 2] since int() can handle leading/trailing whitespace
output = terms[0] + terms[1]
Still, although this can handle situations like 1 + 2 + 3, it will still break when there's multiple different operators involved, or there are parentheses (but that might be something you need not worry about, depending on how complex you want your calculator to be).
IMO, a better approach would indeed be to use tokenization. Personally, I'd use parser combinators, but that may be a bit overkill. For reference, here's an example calculator whose input is parsed using parsy, a parser combinator library for Python.
You could remove the spaces before processing the string by using replace().
Try adding in:
clean_input = hold_input.replace(" ", "")
just after you create hold_input.

regex does not match only upper case letters, despite being instructed to do so

I'm making a script to crawl through a web page and find all upper case names, equalling a number (ex. DUP_NB_FUNC=8). The part where my regular expression has to match only upper case letters however, does not seem to be working properly.
value = re.findall(r"[A-Z0-9_]*(?==\d).{2,}", input)
|tc_apb_conf_00.v:-:DUP_NB_FUNC=2
|:-:DUP_NB_FUNC=2
|:-:DUP_NB_FUNC=4
|:-:DUP_NB_FUNC=5
|tc_apb_conf_01.v:-:DUP_NB_FUNC=8
Desired output should look something like the above. However, I am getting:
|tc_apb_conf_00.v:-:=1" name="viewport"/>
|:-:DUP_NB_FUNC=2
|:-:DUP_NB_FUNC=4
|:-:DUP_NB_FUNC=5
|tc_apb_conf_01.v:-:DUP_NB_FUNC=8
Based on the input I can see its finding a match starting at =1. I don't however understand why as I've put only A-Z in the regex range. I'd really appreciate a bit of assistance and clearing up.
This should be help:
[A-Z0-9_]+(?==\d).{2,}
or
\b[A-Z0-9_]*(?==\d).{2,}\b
But anyway your regex quite weird, according to your requirement above I suggest this
[A-Z0-9_]+=\d+
Instead of using
(?==\d).{2,}: any letters two or more and make sure that the first two letter are = and a one integer respectively,
you can just use
=\d+
Try this.
value = re.findall(r"[A-Z0-9_]+(?==\d).{2,}", input)
You want the case sensitive match to match at least once, which means you want the + quantifier, not the * quantifier, that matches between zero and unlimited times.
I will suggest you define your pattern and check you input if it is available
for i in tlist:
value=re.compile(r"[A-Z0-9_:-.]+=\d+")
jee=value.match(i)
if jee is not None:
print i
tlist contains your input

Categories