>>> import re
>>> I = "I=2.7A"
>>> s = re.search(r"\d+(\.\d+)?", I)
>>> s.group(0)
'2.7'
>>> I = "A=3V"
>>> s = re.search(r"\d+(\.\d+)?", I)
>>> s.group(0)
'3'
>>> I = "I=2.723A"
>>> s = re.search(r"\d+(\.\d+)?", I)
>>> s.group(0)
'2.723'
Answer from Nolen Royalty on Stack Overflow>>> import re
>>> I = "I=2.7A"
>>> s = re.search(r"\d+(\.\d+)?", I)
>>> s.group(0)
'2.7'
>>> I = "A=3V"
>>> s = re.search(r"\d+(\.\d+)?", I)
>>> s.group(0)
'3'
>>> I = "I=2.723A"
>>> s = re.search(r"\d+(\.\d+)?", I)
>>> s.group(0)
'2.723'
RE is probably good for this, but as one RE answer has already been posted, I'll take your non-regex example and modify it:
One example I saw but wouldnt work was -
I="I=2.7A"
[int(s) for s in I.split() if s.isdigit()]
The good thing is that split() can take arguments. Try this:
extracted = float("".join(i for i in I.split("=")[1] if i.isdigit() or i == "."))
Incidentally, here's a breakdown of the RE you supplied:
"\d+.\d+"
\d+ #match one or more decimal digits
. #match any character -- a lone period is just a wildcard
\d+ #match one or more decimal digits again
One way to do it (correctly) would be:
"\d+\.?\d*"
\d+ #match one or more decimal digits
\.? #match 0 or 1 periods (notice how I escaped the period)
\d* #match 0 or more decimal digits
word2num: Convert complex "word numbers" to numerical values
Python-DOCX and extract numbered items from word document - Stack Overflow
regex - How to extract particular numbers by matching a word using python regular expression? - Stack Overflow
python - Extract digits from a string within a word - Stack Overflow
I'd use a regexp:
>>> import re
>>> re.findall(r'\d+', "hello 42 I'm a 32 string 30")
['42', '32', '30']
This would also match 42 from bla42bla. If you only want numbers delimited by word boundaries (space, period, comma), you can use \b:
>>> re.findall(r'\b\d+\b', "he33llo 42 I'm a 32 string 30")
['42', '32', '30']
To end up with a list of numbers instead of a list of strings:
>>> [int(s) for s in re.findall(r'\b\d+\b', "he33llo 42 I'm a 32 string 30")]
[42, 32, 30]
NOTE: this does not work for negative integers
If you only want to extract only positive integers, try the following:
>>> txt = "h3110 23 cat 444.4 rabbit 11 2 dog"
>>> [int(s) for s in txt.split() if s.isdigit()]
[23, 11, 2]
I would argue that this is better than the regex example because you don't need another module and it's more readable because you don't need to parse (and learn) the regex mini-language.
This will not recognize floats, negative integers, or integers in hexadecimal format. If you can't accept these limitations, jmnas's answer below will do the trick.
Hey all, I just published my first Python package called word2num. It converts written numbers like "one hundred and twenty-five" or "nine and three quarters" to their numerical values. There are a handful of other packages out there that do this already, but they're mostly no longer maintained and do not support fractional values (the key feature I need for my project).
It supports a variety of types of numbers and uses configurable fuzzy string matching to account for typos. It only supports English at the moment, but contributions for other languages would be more than welcome.
If you want to give it a try, you can pip install word2num.
from word2num import word2num
word2num("twenty nine and a half") # 29.5Check out the GitHub repo for more usage info and examples. My experience in Python is quite limited, so if anything comes across as non-Pythonic, I'd appreciate a heads-up! ๐
You can use
re.findall(r'(?<=[a-zA-Z])\d+|\d+(?=[a-zA-Z])', text)
This regex will extract all one or more digit chunks that are immediately preceded or followed with an ASCII letter.
A fully Unicode version for Python re would look like
(?<=[^\W\d_])\d+|\d+(?=[^\W\d_])
where [^\W\d_] matches any Unicode letter.
See the regex demo for reference.
An approach with str.translate, without the use of regex or re module:
from string import ascii_letters
delete_dict = {sp_character: '' for sp_character in ascii_letters}
table = str.maketrans(delete_dict)
text = 'I 77! need 1:5 this number inside my wor5d, but also this word3 and this 4word, but not this 1 and not this 555.'
print([res for s in text.rstrip('.').split()
if not (s2 := s.rstrip(',')).isnumeric() and (res := s2.translate(table)) and res.isnumeric()])
Out:
['5', '3', '4']
Performance
I was curious so I did some benchmark tests to compare performance against other approaches. Looks like str.translate is faster even than the regex implementation.
Here is my benchmark code with timeit:
import re
from string import ascii_letters
from timeit import timeit
_NUM_RE = re.compile(r'(?<=[a-zA-Z])\d+|\d+(?=[a-zA-Z])')
delete_dict = {sp_character: '' for sp_character in ascii_letters}
_TABLE = str.maketrans(delete_dict)
text = 'I need this number inside my wor5d, but also this word3 and this 4word, but not this 1 and not this 555.'
def main():
n = 100_000
print('regex: ', timeit("re.findall(r'(?<=[a-zA-Z])\d+|\d+(?=[a-zA-Z])', text)",
globals=globals(), number=n))
print('regex (opt): ', (timeit("_NUM_RE.findall(text)",
globals=globals(), number=n)))
print('iter_char: ', timeit("""
k=set()
for x in range(1,len(text)-1):
if text[x-1].isdigit() and text[x].isalpha():
k.add(text[x-1])
if text[x].isdigit() and text[x+1].isalpha():
k.add(text[x])
if text[x-1].isalpha() and text[x].isdigit() and text[x+1].isalpha():
k.add(text[x])
if text[x-1].isalpha() and text[x].isdigit():
k.add(text[x])
""", globals=globals(), number=n))
print('str.translate: ', timeit("""
[
res for s in text.rstrip('.').split()
if not (s2 := s.rstrip(',')).isnumeric() and (res := s2.translate(_TABLE)) and res.isnumeric()
]
""", globals=globals(), number=n))
if __name__ == '__main__':
main()
Results (Mac OS X - M1):
regex: 0.5315765410050517
regex (opt): 0.5069837079936406
iter_char: 2.5037198749923846
str.translate: 0.37348733299586456