I am working on converting parts of a C++ program to Python, but I have some trouble replacing the C function strtod. The strings I'm working on consists of simple mathmatical-ish equations, such as "KM/1000.0". The problem is that the both constants and numbers are mixed and I'm therefore unable to use float().
How can a Python function be written to simulate strtod
which returns both the converted number and the position of the next character?
I'm not aware of any existing functions that would do that.
However, it's pretty easy to write one using regular expressions:
import re
# returns (float,endpos)
def strtod(s, pos):
m = re.match(r'[+-]?\d*[.]?\d*(?:[eE][+-]?\d+)?', s[pos:])
if m.group(0) == '': raise ValueError('bad float: %s' % s[pos:])
return float(m.group(0)), pos + m.end()
print strtod('(a+2.0)/1e-1', 3)
print strtod('(a+2.0)/1e-1', 8)
A better overall approach might be to build a lexical scanner that would tokenize the expression first, and then work with a sequence of tokens rather than directly with the string (or indeed go the whole hog and build a yacc-style parser).
You can create a simple C strtod
wrapper:
#include <stdlib.h>
double strtod_wrap(const char *nptr, char **endptr)
{
return strtod(nptr, endptr);
}
compile with:
gcc -fPIC -shared -o libstrtod.dll strtod.c
(if you're using Python 64 bit, the compiler must be 64-bit as well)
and call it using ctypes
from python (linux: change .dll
to .so
in the lib target and in the code below, this was tested on Windows):
import ctypes
_strtod = ctypes.CDLL('libstrtod.dll')
_strtod.strtod_wrap.argtypes = (ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p))
_strtod.strtod_wrap.restype = ctypes.c_double
def strtod(s):
p = ctypes.c_char_p(0)
s = ctypes.create_string_buffer(s.encode('utf-8'))
result = _strtod.strtod_wrap(s, ctypes.byref(p))
return result,ctypes.string_at(p)
print(strtod("12.5hello"))
prints:
(12.5, b'hello')
(It's not as hard as it seems, since I learned how to do that just 10 minutes ago)
Useful Q&As about ctypes
parse the number yourself.
a recursive-descent parser is very easy for this kind of input. first write a grammar:
float ::= ipart ('.' fpart)* ('e' exp)*
ipart ::= digit+
fpart ::= digit+
exp ::= ('+'|'-') digit+
digit = ['0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9']
now converting this grammar to a function should be straightforward...
I'd use a regular expression for this:
import re
mystring = "1.3 times 456.789 equals 593.8257 (or 5.93E2)"
def findfloats(s):
regex = re.compile(r"[+-]?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b", re.I)
for match in regex.finditer(mystring):
yield (match.group(), match.start(), match.end())
This finds all floating point numbers in the string and returns them together with their positions.
>>> for item in findfloats(mystring):
... print(item)
...
('1.3', 0, 3)
('456.789', 10, 17)
('593.8257', 25, 33)
('5.93E2', 38, 44)
来源:https://stackoverflow.com/questions/7564899/python-equivalent-to-c-strtod