1

My code is supposed to return None if the string:

Contains non-supported operators or non-numbers. Supported operators are: **,* , ^ , - , + , / , ( , )

Examples

"4.0 + 2" is valid

"3.88327 - $3.4" is invalid (since "$")

"a + 24" is invalid (since "a")

"2+6" is valid

"4+/3" is invalid (since "+/" is two operators next to each other)

"4**-3" is valid

How would I do this?

Here's my code:

def checkvalid(string1):
    temp = string1.split()
    for i in len(temp):
        if i in "*^-+/()":
            return None
        if not i.isnumeric():
            return None

    return string1

But this doesn't always work. It only works for regular integer numbers like "22 66" -> this works, it returns the string, but nothing else seems to work, it always returns None.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Gary Wayne
  • 25
  • 5
  • Current code will fail for floats beacause `'4.0' in '*^+/()` if always `False` and `'4.0'.isnumeric()` is always `False`. – Ch3steR Mar 09 '20 at 19:21
  • 1
    `for i in len(temp):` That loops over the _indexes_, i.e. `0 1 2 3` etc. Presumably you wanted to check `temp[i]`, not `i` itself. – John Gordon Mar 09 '20 at 19:22
  • Does this answer your question? [Efficient way to search for invalid characters in python](https://stackoverflow.com/questions/5698267/efficient-way-to-search-for-invalid-characters-in-python) – Salman Farsi Mar 09 '20 at 19:23
  • Why are you even bothering to split the string? It's perfectly natural to iterate over the characters in a string directly: `for ch in string1:` – John Gordon Mar 09 '20 at 19:26
  • 1
    @JohnGordon cuz how would I know if it was a float if i went through each character of a string – Gary Wayne Mar 09 '20 at 19:30
  • Oh I see, the input string can contain several separate numbers, and that's why you're splitting. – John Gordon Mar 09 '20 at 19:40
  • @JohnGordon yea and then the other problem is the numbers can't be split like "6+2" since there are no spaces and even tho this string is valid it keeps giving me None – Gary Wayne Mar 09 '20 at 19:41
  • Is something like this `'4++++2'` be valid? – Ch3steR Mar 09 '20 at 20:02
  • @Ch3steR no, it can't have two operators next to each other kinda like math, so "4++2" is invalid but "4+-2" is valid since "-" in this case means "-2". good point I should have included that – Gary Wayne Mar 09 '20 at 20:04

3 Answers3

1

Updated Answer

Since my original answer, you've added seven new requirements to this question. I'm disengaging as I think you need to better understand the scope of the problem you're facing before asking for more help.

However, I will throw one more snippet up that might set you on the right path, as it appears that you're trying to find valid mathematical expressions. The following code will do that:

def check_valid(data):
    errors = (SyntaxError, NameError)
    try:
        eval(data)
    except errors:
        for i in data.split():
            try:
                eval(i)
            except errors:
                return None
    return data

test = ["4++2", "4+-2", "4.0 + 2", "3.88327 - $3.4", "a + 24", "2+6", "4+/3"]

for t in test:
    try:
        assert check_valid(t)
        print(f"{t} valid")
    except AssertionError:
        print(f"{t} not valid")

Output

4++2 valid
4+-2 valid
4.0 + 2 valid
3.88327 - $3.4 not valid
a + 24 not valid
2+6 valid
4+/3 not valid

In Python, + can repeat any number of times and still be a valid math expression, as it's just changing the sign of the integer repeatedly.


Original Answer

There are a number of ways to approach this. Given your example, there are a few flaws in your logic:

  • "4.0" is not numeric. Numeric is in 0-9 or unicode numerics. Docs here
  • You're checking a string against another string with the in keyword. With your first example string, the sequence "4.0" is clearly not in the sequence "*^-+/()". Example of how this works:
>>> "4.0" in "asdf4.012345"
True
>>> "4.0" in "0.4"
False

A quick fix using similar logic would be to check character-by-character rather than word-by-word, and combine the two conditionals with and. Try the following snippet:

def check_valid(data):
    for word in data.split():
        for character in word:
            if character not in "*^-+/()." and not character.isnumeric():
                return None

    return data

test = ["4.0 + 2", "3.88327 - $3.4", "a + 24", "22 66", "2+6"]

for t in test:
    print(f"Test: {check_valid(t)}")

Output

Test: 4.0 + 2
Test: None
Test: None
Test: 22 66
Test: 2+6

Note: I changed some names to more closely follow python code style best practices.

Sam Morgan
  • 2,445
  • 1
  • 16
  • 25
  • This is assuming exactly the same things as your code. The str.split() method splits on spaces unless a delimiter is given. Perhaps you should restate your question with more information, because given the requirements this code does exactly as you asked. – Sam Morgan Mar 09 '20 at 20:08
  • Did you actually try the code? 2+6 is valid here as well. – Sam Morgan Mar 09 '20 at 20:11
  • I see. The examples you've mentioned so far weren't in your original question. Again, it's probably best to state the problem you're trying to solve rather than give a tiny dataset to check against. For instance, it appears that you're trying to check for valid math expressions here. – Sam Morgan Mar 09 '20 at 20:17
  • Also, why is +- valid, but ++ is not? +4 is normally a valid number in most math operations (specifies a positive integer). Maybe you meant 4+++2? – Sam Morgan Mar 09 '20 at 20:30
  • 1
    I'm going to disengage at this point. I suspect that no matter what anyone comes up with given your requirements, you will still find holes, because you don't understand the full scope of the problem and aren't communicating it well. I'll add one more example that may set you on the right path. Sorry I can't be of more help! – Sam Morgan Mar 09 '20 at 20:38
  • 1
    nah it's all good bro you helped me more than enough gonna accept ur answer cuz it's the best one so far. it defintley helped me on my track. thank you – Gary Wayne Mar 09 '20 at 20:40
0

Adding a few checks to your eval can make it a bit more secure although not ideal.

import re

def checkvalid(string1):
    string1 = string1.replace(" ", "")
    checkStatements = ["++", "-+", "---"]
    checkOut = [x for x in checkStatements if x not in string1]
    # If you require anything further characters to be avoided place them in the regex
    if re.match('[a-zA-Z]', string1) or len(checkOut) != len(checkStatements):
        return False
    else:
        try:
            output = eval(string1)
            if isinstance(output, float) or isinstance(output, int):
                return True
            else:
                return False
        except:
            return False
difurious
  • 1,523
  • 3
  • 19
  • 32
-1

An alternative might be to use regex to check if the expression contains invalid characters; or to use a string parser. Because your expressions are simple, let's make python do our job

def check_valid(expression: str):
try:
    eval(expression) # Execute the expression; If the expression is valid, return the result of the evaluation; if is invalid; raise exception
    return True
except Exception as _:
    return False

if __name__ == '__main__':
    print("{expression}: {validity}".format(expression="4.0 + 2", validity=check_valid("4.0 + 2")))
    print("{expression}: {validity}".format(expression="3.88327 - $3.4", validity=check_valid("3.88327 - $3.4")))
    print("{expression}: {validity}".format(expression="a + 24", validity=check_valid("a + 24")))
    print("{expression}: {validity}".format(expression="2+6", validity=check_valid("2+6")))
Dorcioman
  • 499
  • 5
  • 6