Python 相当于 C strtod

发布于 2024-12-06 19:37:46 字数 289 浏览 0 评论 0原文

我正在努力将 C++ 程序的部分内容转换为 Python,但在替换 C 函数 strtod。 我正在处理的字符串由简单的数学方程组成,例如“KM/1000.0”。问题是常量和数字是混合的,因此我无法使用 float()。

如何编写一个Python函数来模拟strtod,它返回转换后的数字和下一个字符的位置?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

阳光下慵懒的猫 2024-12-13 19:37:46

我不知道有任何现有功能可以做到这一点。

然而,使用正则表达式编写一个非常容易:

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 风格的解析器)。

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

活泼老夫 2024-12-13 19:37:46

您可以创建一个简单的 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:在 lib 目标和下面的代码中将 .dll 更改为 .so,这是在 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 的有用问答

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

笨死的猪 2024-12-13 19:37:46

自己解析这个数字。

对于这种输入,递归下降解析器非常容易。
首先写一个语法:

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

现在将此语法转换为函数应该很简单......

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...

我早已燃尽 2024-12-13 19:37:46

我将为此使用正则表达式:

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)

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文