Эквивалент Python для atoi/atof

Python любит создавать исключения, которые обычно велики. Но я сталкиваюсь с некоторыми строками, которые я отчаянно хочу преобразовать в целые числа, используя C atoi/atof semantics - например. atoi из "3 из 12", "3/12", "3/12", должны стать 3; atof ( "3,14 секунды" ) должно стать 3,14; atoi ( "-99 баллов" ) должно стать -99. Python, конечно, имеет atoi и atof функции, которые не ведут себя как atoi и atof и точно так же, как собственные конструкторы int и float Python.

Самое лучшее, что у меня есть до сих пор, которое действительно уродливо и трудно распространяться на различные доступные форматы float:

value = 1
s = str(s).strip()
if s.startswith("-"):
    value = -1
    s = s[1:]
elif s.startswith("+"):
    s = s[1:]
try:
    mul = int("".join(itertools.takewhile(str.isdigit, s)))
except (TypeError, ValueError, AttributeError):
    mul = 0
return mul * value

Ответ 1

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

>>> import re
>>> p = re.compile(r'[^\d-]*(-?[\d]+(\.[\d]*)?([eE][+-]?[\d]+)?)')
>>> def test(seq):
        for s in seq:
            m = p.match(s)
            if m:
                result = m.groups()[0]
                if "." in result or "e" in result or "E" in result:
                    print "{0} -> {1}".format(s, float(result))
                else:
                    print '"{0}" -> {1}'.format(s, int(result))
            else:
                print s, "no match"

>>> test(s)
"1 0" -> 1
"3 of 12" -> 3
"3 1/2" -> 3
"3/12" -> 3
3.15 seconds -> 3.15
3.0E+102 -> 3e+102
"what about 2?" -> 2
"what about -2?" -> -2
2.10a -> 2.1

Ответ 2

Если вы так заинтересованы в том, чтобы точно реализовать функции c atoi, почему бы не использовать его напрямую? Например, на моем Mac,

>>> import ctypes, ctypes.util
>>> whereislib = ctypes.util.find_library('c')
>>> whereislib
'/usr/lib/libc.dylib'
>>> clib = ctypes.cdll.LoadLibrary(whereislib)
>>> clib.atoi('-99foobar')
-99

В Linux, Windows и т.д. идентичный код должен работать, за исключением того, что вы увидите другой путь, если вы исследуете whereislib (только на действительно, действительно необычных установках, если этот код никогда не сможет найти библиотеку времени выполнения C).

Если вы заинтересованы в том, чтобы не использовать прямое использование библиотеки C, я думаю, вы могли бы получить соответствующий префикс, например. с RE, например r'\s*([+-]?\d+)', и попробуйте int.

Ответ 3

Я думаю, что итеративная версия лучше, чем рекурсивная версия

# Iterative
def atof(s):
    s,_,_=s.partition(' ') # eg. this helps by trimming off at the first space
    while s:
        try:
            return float(s)
        except:
            s=s[:-1]
    return 0.0

# Recursive
def atof(s):
    try:
        return float(s)
    except:
        if not s:
            return 0.0
        return atof(s[:-1])


print atof("3 of 12")
print atof("3/12")
print atof("3 / 12")
print atof("3.14 seconds")
print atof("314e-2 seconds")
print atof("-99 score")
print atof("hello world")

Ответ 4

Возможно, используйте быстрое регулярное выражение, чтобы захватить только первую часть строки, которая может считаться числовой? Что-то вроде...

-?[0-9]+(?:\.[0-9]+)?

для float и для ints просто,

-?[0-9]+

Ответ 5

Я думаю, что сделаю это char на char:

def myatof(s):
    try:
        return float(s);
    except:
        last_result = None
        for i in range(1, len(s)):
            try:
                last_result = float(s[:i])
            except:
                return last_result
    return last_result

Ответ 6

Как насчет этого?

num=int(q.join(re.findall(r'[\d-]',s)))