Python эквивалентно C strtod - PullRequest
       40

Python эквивалентно C strtod

6 голосов
/ 27 сентября 2011

Я работаю над преобразованием частей программы на C ++ в Python, но у меня возникли некоторые проблемы при замене функции C strtod .Строки, над которыми я работаю, состоят из простых математических уравнений, таких как «KM / 1000.0».Проблема в том, что и константы, и числа смешаны, и поэтому я не могу использовать float ().

Как можно написать функцию Python для имитации strtod, которая возвращает преобразованное число и позициюследующего персонажа?

Ответы [ 4 ]

3 голосов
/ 27 сентября 2011

Я не знаю ни о каких существующих функциях, которые бы это делали.

Однако написать с помощью регулярных выражений довольно просто:

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)

Лучшим общим подходом могло бы стать создание лексического сканера , который сначала токенизировал бы выражение, а затем работал с последовательностью токенов, а не непосредственно со строкой (или, действительно, собирал всю библиотеку и собирал синтаксический анализатор в стиле yacc).

2 голосов
/ 21 мая 2018

Вы можете создать простую оболочку C strtod:

#include <stdlib.h>

double strtod_wrap(const char *nptr, char **endptr)
{
   return strtod(nptr, endptr);
}

скомпилировать с:

gcc -fPIC -shared -o libstrtod.dll strtod.c

(если вы используете 64-битную версию Python, компилятор также должен быть 64-битной)

и вызовите его, используя ctypes из python (linux: измените .dll на .so в целевой библиотеке lib и в приведенном ниже коде, это было протестировано в 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"))

печать:

(12.5, b'hello')

(это не так сложно, как кажется, так как я научился это делать всего 10 минут назад)

Полезные вопросы и ответы о ctypes

0 голосов
/ 27 сентября 2011

разберите число самостоятельно.

парсер рекурсивного спуска очень прост для такого рода ввода.сначала напишите грамматику:

float ::= ipart ('.' fpart)* ('e' exp)*
ipart ::= digit+
fpart ::= digit+
exp   ::= ('+'|'-') digit+
digit = ['0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9']

Теперь преобразование этой грамматики в функцию должно быть простым ...

0 голосов
/ 27 сентября 2011

Я бы использовал для этого регулярное выражение:

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())

Это находит все числа с плавающей запятой в строке и возвращает их вместе с их позициями.

>>> for item in findfloats(mystring):
...     print(item)
...
('1.3', 0, 3)
('456.789', 10, 17)
('593.8257', 25, 33)
('5.93E2', 38, 44)
...