How to match 'K', 'M', 'G', 'Ki', 'Mi', 'Gi' etc. but not solitary 'i' affix

Viewed 192

What I am trying to do is match shorthand notation with ISO prefixes (1k = 1000, -1ki = -1024, etc.). This regex is close:

^([+-]?)(\d+)((?i)[KMGTPEZY]?(?i)i?$)

But it matches 1i, so I am trying to find a regex that will only match the i if it is preceded by one of the letters in the character class. I tried using a lookbehind:

^([+-]?)(\d+)((?i)[KMGTPEZY]?(?<=(?i)[KMGTPEZY])(?i)i?$)

This won't match 1i, but now it won't match a number with no prefix like 1, and it seems... inelegant to have to repeat the (?i)[KMGTPEZY] so I was hoping there is a more graceful way of doing this... that also works in Python :-).

In case it affects the answer, the complete problem is I want to handle things like 1,2,3,[5-10),20-25,1k-2k,2Mi-3Gi,10T... substitute the appropriate number for the prefix shorthand (1k=1000, 10ki=10240, etc) and then generate a list of the actual sequence (so expand [5-10) to 5,6,7,8,9, 20-25 is equivalent to [20-25] or 20,21,22,23,24,25), but right now, I am at the first step which is just matching the prefix shorthand notation.

2 Answers

Yes, one could probably address this with fancy features. But no, that's not necessary.

Simple alternation would suffice.

^([+-]?\d+)([KMGTPEZY]i|[KMGTPEZY]|)$

That is saying, e.g., that we'd like to match 'Ki', or match 'K', or match...

This is actually quite straightforward:

(?i)^([+-])?(\d+)([KMGTPEZY]i?)?$

Here we specify an optional group ([KMGTPEZY]i?)? which itself contains the optional match i?.

Note that, as a global flag, (?i) need only be specified once (and is traditionally specified at the start of the regex where it's easily spotted). If you want to specify that the optional i part of the suffix is case sensitive, global flags won't help you, and you'll have to do something like this:

^([+-])?(\d+)([KkMmGgTtPpEeZzYy]i?)?$

Examples:

>>> import re
>>> pattern = re.compile('(?i)^([+-])?(\d+)([KMGTPEZY]i?)?$')

>>> pattern.search('12').groups()
(None, '12', None)

>>> pattern.search('-34').groups()
('-', '34', None)

>>> pattern.search('+56m').groups()
('+', '56', 'm')

>>> pattern.search('-78ki').groups()
('-', '78', 'ki')

>>> pattern.search('90i').groups()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'groups'
Related