Posts: 4,498
Threads: 69
Joined: Jan 2018
adt Wrote:can it be presumed that at present there is no built in function in python that can return True for a string like "4.5" ? There is no such function. The easiest way to check if a string represents a float is to call the float() function and catch the potential ValueError.
Posts: 1,344
Threads: 2
Joined: May 2019
Easy enough to write -
def is_it_a_duck(str):
try :
foo = float(str)
return True
except :
return False
print(is_it_a_duck("4.5")) This returns true for things that float. OK it catches all exceptions, not just Value, but it works for the task at hand...
Posts: 67
Threads: 17
Joined: Aug 2019
Microsoft VBA has a Val function with the syntax: Val(string)
It returns the numbers contained in a string as a numeric value of appropriate type. The required string argument is any valid string expression.
The Val function stops reading the string at the first character it can't recognize as part of a number. Blanks, tabs, and linefeed characters get stripped from the argument.
This function can be helpful in extracting numerical data from textual records. Some samples are placed below:
# Caution: Not Python: It Is Microsoft VBA Code:
? Val("45")
45
? Val("4.5")
4.5
? Val("4.5ABC")
4.5
? Val("ABCD")
0
? Val("AB45")
0
? Val(" 2 45 7 ")
2457
? Val(" 2 4 . 5 7 ")
24.57 Would there be any objection if a similar built-in function were to get incorporated in python too ?
A.D.Tejpal
Posts: 4,498
Threads: 69
Joined: Jan 2018
Oct-06-2019, 06:16 AM
(This post was last modified: Oct-06-2019, 06:16 AM by Gribouillis.)
adt Wrote:Would there be any objection if a similar built-in function were to get incorporated in python too ? I don't have an objection but I'm not in charge of the contents of the standard library. Why do you need the function to be built-in? You can very well design a separate library that contains this. Here is an example, I don't say it's the best way to do it, a pure regex implementation may be better.
import ast
import re
from tokenize import generate_tokens, NUMBER
import io
def func(s):
t = re.sub(r'\s+', '', s)
f = io.StringIO(t)
for tok in generate_tokens(f.readline):
if tok[0] == NUMBER:
return ast.literal_eval(tok[1])
return 0
return 0
if __name__ == '__main__':
sample = [
("45", 45),
("4.5", 4.5),
("4.5ABC", 4.5),
("ABCD", 0),
("AB45", 0),
(" 2 45 7 ", 2457),
(" 2 4 . 5 7 ", 24.57),
]
for inp, out in sample:
assert out == func(inp) It won't work with complex numbers however. Also it needs to be changed if there is a sign + or - in front of the number. How does the Val function handle a sign?
Posts: 67
Threads: 17
Joined: Aug 2019
(Oct-06-2019, 06:16 AM)Gribouillis Wrote: How does the Val function handle a sign?
Val function handles the signs smoothly, for example:
Val("+4.5") returns 4.5 while Val("-4.5") returns -4.5
A.D.Tejpal
Posts: 4,498
Threads: 69
Joined: Jan 2018
Oct-06-2019, 06:51 AM
(This post was last modified: Oct-06-2019, 06:51 AM by Gribouillis.)
What about ++++45 or -+54 ? Here is a corrected version
def func(s):
t = re.sub(r'\s+', '', s)
f = io.StringIO(t)
for tok in generate_tokens(f.readline):
if tok[1] in ('-', '+'):
continue
if tok[0] == NUMBER:
return ast.literal_eval(t[:tok[3][1]])
return 0
return 0
Posts: 67
Threads: 17
Joined: Aug 2019
(Oct-06-2019, 06:51 AM)Gribouillis Wrote: What about ++++45 or -+54 ? Here is a corrected version
Glad to inform that your revised function works nicely, giving correct results for all our test strings, even if carrying + sign. However, with negative signed string, it still gives AssertionError.
For "++++45" & " -+54", VBA's Val() function returns 0 in both cases.
It is seen that for pure number strings, even if signed, python's built-in eval() function provides results similar to those by VBA's Val() function. For example:
print(eval("45"))
#>>>45
print(eval("+4.5"))
#>>>4.5
print(eval("-4.5"))
#>>>-4.5 However, for mixed strings like "4.5ABC" or " 2 45 7 ", eval() throws error.
A.D.Tejpal
Posts: 8,090
Threads: 154
Joined: Sep 2016
Oct-06-2019, 08:51 AM
(This post was last modified: Oct-06-2019, 08:52 AM by buran.)
@ adt, It's better to use ast.literal_eval as it is safer compared to eval
from the docs:
Quote:Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.
This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself. It is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.
Posts: 4,498
Threads: 69
Joined: Jan 2018
Oct-06-2019, 08:56 AM
(This post was last modified: Oct-06-2019, 08:56 AM by Gribouillis.)
Here is a new version. You don't need to import the io module.
def func(s):
t = re.sub(r'\s+', '', s)
sign = True
for tok in generate_tokens(iter([t, '']).__next__):
if sign and tok[1] in ('-', '+'):
sign = False
continue
if tok[0] == NUMBER:
return ast.literal_eval(t[:tok[3][1]])
return 0
return 0 If there is still an error, please give full bug report!
Posts: 67
Threads: 17
Joined: Aug 2019
Oct-06-2019, 12:19 PM
(This post was last modified: Oct-06-2019, 12:31 PM by adt.)
(Oct-06-2019, 08:51 AM)buran Wrote: It's better to use ast.literal_eval as it is safer compared to eval
Thanks buran, for your kind suggestion. Noted for future use.
It is seen that ast.literal_eval() too, throws error in case of mixed strings.
(Oct-06-2019, 08:56 AM)Gribouillis Wrote: If there is still an error, please give full bug report!
Test results for the latest version of func() are placed below:
import ast
import re
from tokenize import generate_tokens, NUMBER
#import io
def func(s):
t = re.sub(r'\s+', '', s)
sign = True
for tok in generate_tokens(iter([t, '']).__next__):
if sign and tok[1] in ('-', '+'):
sign = False
continue
if tok[0] == NUMBER:
return ast.literal_eval(t[:tok[3][1]])
return 0
return 0
if __name__ == '__main__':
sample = [
("45", 45),
("4.5", 4.5),
("4.5ABC", 4.5),
("ABCD", 0),
("AB45", 0),
(" 2 45 7 ", 2457),
(" 2 4 . 5 7 ", 24.57),
("+4.5", 4.5),
("-4.5", 4.5), # AssertionError (see Error Box)
]
for inp, out in sample:
assert out == func(inp)
print(func(inp)) Output: 45
4.5
4.5
0
0
2457
24.57
4.5
Error For Last String "-4.5"
Error: Traceback (most recent call last):
File "J:\AdtPython-Practice\00-RoughTesting.py", line 92, in <module>
assert out == func(inp)
AssertionError
A.D.Tejpal
|