匹配Python整数字面量的正则表达式

4
有什么正则表达式可以匹配Python中的整数字面量,支持额外的内容如ol,但是不会匹配浮点型或者变量名中带数字的情况。我使用Python的re库,所以任何被它支持的语法都可以。 编辑:这是我的动机(显然这很重要)。我正在尝试修复http://code.google.com/p/sympy/issues/detail?id=3182。我想要做的是创建一个IPython钩子,将int/int(如1/2)自动转换为Rational(int,int)(如Rational(1,2))。原因是否则就无法将1/2注册为一个有理数,因为它是Python类型__div__ Python类型。在SymPy中,这可能会非常麻烦,因为像x **(1/2)这样的东西会创建x ** 0(或者与 __future__ 分割或Python 3一起使用x ** 0.5),而你想要的是x ** Rational(1,2),一个确切的数量。
我的解决方案是向IPython添加钩子,以自动包装输入中的所有整数字面量与Integer(SymPy的定制整数类)在一起(可通过除法得到Rational)。这将使我能够在isympy中添加一个选项,让希望使用它的人们更像传统的计算机代数系统。我希望这解释了为什么我需要它来匹配任何和所有字面量内部的Python表达式,这就是为什么它不需要匹配浮点文字和名称中有数字的变量的原因。
此外,既然每个人都对我尝试过的东西很感兴趣,那么这里还有:在放弃之前没做太多(正则表达式难度太大)。我用(?!\.)来避免捕获浮点型文字的第一部分,但是这似乎行不通(如果有人能告诉我为什么,例如re.sub(r"(\d*(?!\.))", r"S\(\1\)", "12.1"),我会很好奇)。 编辑2:由于我计划与re.sub一起使用,因此您可以将整个内容都括在括号中,这样我就可以使用\1 :)

你需要了解的一切都在Python文档中。 - Joel Cornett
我确实做了研究。我在谷歌上搜索了它,甚至自己尝试了一下。但是这些都没有帮助到我。我没有在问题中提及这一点,因为我觉得这并不相关。 - asmeurer
考虑到迄今为止没有任何答案能够满足我的需求,我认为这不是一个简单的问题。 - asmeurer
3
通常最好在问题中发布您错误/不完整的解决方案,而不是什么都不发,纯粹出于这个原因。另外,在提问中提及为什么想要做某事可能很方便,因为可能会有其他您没有预料到的更好的解决方案。 - Josh Smeaton
我同意@JoshSmeaton的观点。如果我有些粗鲁,我很抱歉。如果您编辑您的问题,我可以撤销我的反对票。 - Joel Cornett
6个回答

5

在3.x中,整数字面量的定义稍有不同,具体如下:

integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"+
nonzerodigit   ::=  "1"..."9"
digit          ::=  "0"..."9"
octinteger     ::=  "0" ("o" | "O") octdigit+
hexinteger     ::=  "0" ("x" | "X") hexdigit+
bininteger     ::=  "0" ("b" | "B") bindigit+
octdigit       ::=  "0"..."7"
hexdigit       ::=  digit | "a"..."f" | "A"..."F"
bindigit       ::=  "0" | "1"

所以,就像这样:
[1-9]\d*|0|0[oO][0-7]+|0[xX][\da-fA-F]+|0[bB][01]+

根据您说想要支持“l”,我猜您实际上想要2.x版本的定义

longinteger    ::=  integer ("l" | "L")
integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"
octinteger     ::=  "0" ("o" | "O") octdigit+ | "0" octdigit+
hexinteger     ::=  "0" ("x" | "X") hexdigit+
bininteger     ::=  "0" ("b" | "B") bindigit+
nonzerodigit   ::=  "1"..."9"
octdigit       ::=  "0"..."7"
bindigit       ::=  "0" | "1"
hexdigit       ::=  digit | "a"..."f" | "A"..."F"

这个可以写成

(?:[1-9]\d+|0|0[oO]?[0-7]+|0[xX][\da-fA-F]+|0[bB][01]+)[lL]?

我实际上两个都想要。谢谢! - asmeurer
这仍然适用于浮点字面值的第一部分和包含数字的变量的数字部分。 - asmeurer
我还没有写,但是从Python文档中的十进制示例看来,它几乎正是我想要的。 - asmeurer

4

该语法在http://docs.python.org/reference/lexical_analysis.html#integers中有所描述。下面是一种将其表达为正则表达式的方法:

(0|[1-9][0-9]*|0[oO]?[0-7]+|0[xX][0-9a-fA-F]+|0[bB][01]+)[lL]?

免责声明:本程序不支持负整数,因为在Python中,类似于-31的表达式中的-实际上不是整数字面量的一部分,而是一个独立的运算符。


缺少例如0755的十六进制文本格式;目前还需要在结尾加上[lL] - Danica
如果“-”是分开的,也没关系。对于我正在做的事情来说,它仍然可以正常工作。 - asmeurer
嗯,关于“-”的观点很有意思。现在我想想,它作为一个单独的运算符是有道理的。 - Joel Cornett
@Dougal:换句话说,我错过了两个“?”的实例。不知道怎么发生的。谢谢你指出来了;现在已经修复了。 - ruakh

4

我不认为使用正则表达式是解决问题的方法。Python有tokenizeastsymbolparser模块,可以用于解析/处理/操作/重写Python代码...

>>> s = "33.2 + 6 * 0xFF - 0744"
>>> from StringIO import StringIO
>>> import tokenize
>>> t = list(tokenize.generate_tokens(StringIO(s).readline))
>>> t
[(2, '33.2', (1, 0), (1, 4), '33.2 + 6 * 0xFF - 0744'), (51, '+', (1, 5), (1, 6), '33.2 + 6 * 0xFF - 0744'), (2, '6', (1, 7), (1, 8), '33.2 + 6 * 0xFF - 0744'), (51, '*', (1, 9), (1, 10), '33.2 + 6 * 0xFF - 0744'), (2, '0xFF', (1, 11), (1, 15), '33.2 + 6 * 0xFF - 0744'), (51, '-', (1, 16), (1, 17), '33.2 + 6 * 0xFF - 0744'), (2, '0744', (1, 18), (1, 22), '33.2 + 6 * 0xFF - 0744'), (0, '', (2, 0), (2, 0), '')]
>>> nums = [eval(i[1]) for i in t if i[0] == tokenize.NUMBER]
>>> nums
[33.2, 6, 255, 484]
>>> print map(type, nums)
[<type 'float'>, <type 'int'>, <type 'int'>, <type 'int'>]

http://docs.python.org/library/tokenize.html网址中有一个示例,它将浮点数重写为decimal.Decimal


那是个好观点。我想知道用这种方法是否有显著的速度差异。 - asmeurer
1
@asmeurer 感谢您接受了答案 - 它的效果如何?(是否有链接可以查看更新?) - Jon Clements
请看 https://github.com/sympy/sympy/pull/1470。具有讽刺意味的是,困难的部分是让IPython自动执行此操作。事实证明,它们的API需要更新。 - asmeurer

2

如果你真的想匹配两个“方言”,你会遇到一些歧义,例如在八进制中(在Python 3中需要o)。但是以下内容应该有效:

r = r"""(?xi) # Verbose, case-insensitive regex
(?<!\.)       # Assert no dot before the number
\b            # Start of number
(?:           # Match one of the following:
 0x[0-9a-f]+| # Hexadecimal number
 0o?[0-7]+|   # Octal number
 0b[01]+|     # Binary number
 0+|          # Zero
 [1-9]\d*     # Other decimal number
)             # End of alternation
L?            # Optional Long integer
\b            # End of number
(?!\.)        # Assert no dot after the number"""

是的,我知道我将不得不为不同的Python使用不同的方法,但这对我来说并不重要,因为我只关心正在运行的Python版本,所以一个简单的sys.version_info就足够了。 - asmeurer
应该使用原始字符串吗? - asmeurer
此外,除非我对\1的括号使用不正确,否则它似乎不能正确处理浮点数(它只匹配.前后的两个整数)。 - asmeurer
我认为你应该放弃符号部分。更准确的做法是使用 [+-]*,因为Python允许像 +--+-+1 这样的东西,但是,正如我所说,我不需要它(而且,至少在我的括号中,它似乎也没有被包含在匹配中)。 - asmeurer
@asmeurer:实际上,我认为如果对于问题“我应该使用哪个正则表达式?”的最佳答案是“不要使用正则表达式,改用这个”,那么你应该接受这个答案。选择帮助你最多的答案(并且后来的访问者将从中学到最多的知识)。 - Tim Pietzcker
显示剩余5条评论

1

这样的东西可以吗?

r = r"""
(?<![\w.])               #Start of string or non-alpha non-decimal point
    0[X][0-9A-F]+L?|     #Hexadecimal
    0[O][0-7]+L?|        #Octal
    0[B][01]+L?|         #Binary
    [1-9]\d*L?           #Decimal/Long Decimal, will not match 0____
(?![\w.])                #End of string or non-alpha non-decimal point
"""

(使用标志re.VERBOSE | re.IGNORECASE


你应该使用 (?<![\w.]) 替代 (?:^|[^\w\.]),并且同样地使用 (?![^\w.]) 替代 (?:$|[^\w\.])。否则,在数字前后的字符将成为匹配的一部分。 - Tim Pietzcker
此外,八进制只能到数字“7”。而您可以使用“re.I”标志使您的正则表达式更易读。 - Tim Pietzcker

0

这相当接近:

re.match('^(0[x|o|b])?\d+[L|l]?$', '0o123l')

啊,看了一些答案后,我的代码会出现很多误报,并且完全跳过十六进制字面量。 - Josh Smeaton
哇,即使我提到了限制,我的不完整的答案还是被踩了。看来没有点赞已经足够了。 - Josh Smeaton
2
根据我的经验,你必须删除错误的答案,否则它们将被投票淹没(尽管说实话,如果我是你,我不会太担心我的声誉,因为你已经有了10.3k的声望)。 - asmeurer
2
@asmeurer 是的,你说得对 - 我不太担心声誉,更关注教育吧。 - Josh Smeaton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接