Regex will do
import re
strings = '''
Hi my name is hazza 50 test test test
Hi hazza 60 test test test
hazza 50 test test test
hazza test test test
'''
for s in re.findall('([a-zA-Z ]*)\d*.*',strings):
print(s)
Gives
Hi my name is hazza
Hi hazza
hazza
hazza test test test
Answer from codenewbie on Stack OverflowRegex will do
import re
strings = '''
Hi my name is hazza 50 test test test
Hi hazza 60 test test test
hazza 50 test test test
hazza test test test
'''
for s in re.findall('([a-zA-Z ]*)\d*.*',strings):
print(s)
Gives
Hi my name is hazza
Hi hazza
hazza
hazza test test test
is_digit = False
str = "Hi my name is hazza 50 test test test"
r = 0
for c in str:
if c.isdigit():
# is_digit = True
r = str.index(c)
print(str[0:r-2])
r is index of 5 r-2 because you want the string without that space before 50
read : https://www.learnpython.org/
regex - Extract Number before a Character in a String Using Python - Stack Overflow
python 3.x - Extract a number after a particular string - Software Engineering Stack Exchange
python - extract a number after and before some string from text using python3 - Stack Overflow
python - Extract a number from a string, after a certain character - Stack Overflow
You may use a simple (\d+)M regex (1+ digit(s) followed with M where the digits are captured into a capture group) with re.findall.
See IDEONE demo:
import re
s = "107S33M15H\n33M100S\n12M100H33M"
print(re.findall(r"(\d+)M", s))
And here is a regex demo
You can use rpartition to achieve that job.
s = '107S33M15H'
prefix = s.rpartition('M')[0]
Regular expressions to the rescue!
I see you are already on the right path, by using a regular expression. But you could try using a regular expression with a capture in order to capture the digits following the string "Episode".
Here is a small example to get you going:
import re
m = re.search('Episode (\d+)', 'series[Episode 37]-03th_July_2010-YouTube', re.IGNORECASE)
m.group(1)
The last statement, m.group(1), returns the contents of the first group (what's inside the parentheses in the regular expression). In this case it will be the string '37', which is the digits that follows the string "Episode ".
Also notice I'm using the IGNORECASE flag, so this will work regardless of the casing of the string "Episode". So "episode" and even "ePISODE" will work too.
Kind of broad, pulling from a text file, but will do for many applications of getting both integers and floats out of strings.
The file, commaseperated.txt has a row looking like this:
Input: (0, 1, 2.4), 1, 5, 8, 99.7, 0.1), (, )()!@!#, 9
import re
# a function to pull the numbers from many lists of strings
def func(a, z, c):
# -- Read .txt -- #
filename = 'commaseperated.txt'
f1 = open(filename, 'r') # open the file for reading
data = f1.readlines() # read the entire file as a list of strings
f1.close()
#
for line in data:
cells = line.strip().split(',')
# empty string
b = ""
# range through cells of from a text file split by commas
for i in range(a, len(cells[z:c])+a):
# no commas in the cells so make dummy commas for new string
b += cells[i] + ','
# find all the numbers
z = re.findall(r'\d+(?:\.\d+)?', b)
# return a list of floated values from the text string
return [float(z[s]) for s in range(len(z))]
Output: [0, 1, 2.4, 1, 5, 8, 99.7, 0.1, 9]
Use one expression for all of them:
\b[A-Z]?\d[/\d]*\b(?:\s+days)?
See a demo on regex101.com.
You'd need to precisize the "account number" format here.
I would use a regex pattern with an alternation for all your possible matches:
\d{2}/\d{2}/\d{4}|\d+ days|[A-Z0-9]{10,}
This matches either a date, a number of days, or an account number. For account numbers, I assume that there are of length 10 or greater, consisting only of letters and numbers.
input_file = """my bday is on 04/01/1997 and
frnd bday on 28/12/2018,
account no is A000142116 and
valid for 30 days for me and
for my frnd only 4 DAYS.my roll no is 130302101786
and register number is 1600523941. Admission number is
181212001103"""
results = re.findall(r'\d{2}/\d{2}/\d{4}|\d+ days|[A-Z0-9]{10,}', input_file, flags=re.IGNORECASE)
print(results)
['04/01/1997', '28/12/2018', 'A000142116', '30 days', '4 DAYS', '130302101786',
'1600523941', '181212001103']
Assuming:
- You know there is a comma in the string, so you don't have to search the entire string to find out if there is or not.
- You know the pattern is
'many_not_digits,few_digits'so there is a big imbalance between the size of the left/right parts either side of the comma. - You can get to the end of the string without walking it, which you can in Python because string indexing is constant time
Then you could start from the end and walk backwards looking for the comma, which would be less overall work for your examples than walking from the left looking for the comma.
Doing work in Python code is way slower than using Python engine code written in C, right? So would it really be faster?
- Make a string "aaaaa....,12"
- use the
timeitmodule to compare each approach - split, or right-walk. - Timeit does a million runs of some code.
- Extend the length of "aaaaaaaaaaaaaaaa....,12" to make it extreme.
How do they compare?
- String split: 1400 "a"'s run a million times took 1 second.
- String split: 4000 "a"'s run a million times took 2 seconds.
- Right walk: 1400 "a"'s run a million times took 0.4 seconds.
- Right walk: 999,999 "a"'s run a million times took ... 0.4 seconds.
!
from timeit import timeit
_split = """num = x.split(',')[-1]"""
_rwalk = """
i=-1
while x[i] != ',':
i-=1
num = x[i+1:]
"""
print(timeit(_split, setup='x="a"*1400 + ",12"'))
print(timeit(_rwalk, setup='x="a"*999999 + ",12"'))
e.g.
1.0063155219977489 # "aaa...,12" for 1400 chars, string split
0.4027107510046335 # "aaa...,12" for 999999 chars, rwalked. Faster.
Try it online at repl.it
I don't think this is algorithmically better than O(n), but with the constraints of the assumptions I made you have more knowledge than str.split() has, and can leverage that to skip walking most of the string and beat it in practise - and the longer the text part, and shorter the digit part, the more you benefit.
If you are worried about using split from the left because of lots of unwanted characters in the beginning, use rsplit.
s = "abcdef,12"
s.rsplit(",", 1)[-1]
Here, rsplit will start splitting the string from the right and the optional second argument we used will stop rsplit to proceed further than the first comma operator it encountered.
(eg):
s = "abc,def,12"
s.rsplit(",", 1)[-1]
# Outputs 12
s = "abcdef12"
s.rsplit(",", 1)[-1]
# Outputs abcdef12
This will be lot simpler and cleaner to get the string of numbers in the end rather than doing anything manually.
Not to mention, it will be lot easier if we wish to check whether we get only numbers with this. Even if it is a list of strings.
def get_numbers(string_list, skip_on_error=True):
numbers_list = []
for input_string in string_list:
the_number = input_string.rsplit(",", 1)[-1]
if the_number.isdigit():
numbers_list.append(the_number)
elif skip_on_error:
numbers_list.append("")
else:
raise Exception("Wrong Format occurred: %s" % (input_string))
return numbers_list
And if you are looking for even further optimization and sure that most(if not all) strings will be of the correct format, you can even use try except if you are going to go with an integer list instead of string list. Like this:
# Instead of the if.. elif.. else construct
try:
numbers_list.append(int(the_number))
except ValueError:
if skip_on_error:
numbers_list.append(0)
else:
raise Exception("Wrong Format occurred: %s" % (input_string))
But always remember the Zen Of Python and using split/rsplit follows these:
- Beautiful is better than ugly
- Explicit is better than implicit
- Simple is better than complex
- Readability counts
- There should be one-- and preferably only one --obvious way to do it
And also remember Donald Knuth:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%
Hi I am new to Python but am working on a project where I want to extract numbers from a string. For example I have the string "CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16" where I want to extract the "16" from "16GB" as well as the "2" and "8" in "(2x8GB)" and "3200" from "3200MHZ".
What is the best way to do this? Thanks in advance!
Edit: Thanks everyone for the help! Regex seems to help do the trick
You want to look into regular expressions (regex) and Python's re module.
import re
string = "CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16"
matches = re.findall("(\d+)", string)
print(matches)
Output: ['16', '2', '8', '4', '3200', '16']
The regex pattern \d+ matches 1 or more numerical characters in sequence. By putting it in brackets ( ) it captures each occurrence of that pattern as a group. And the re.findall method returns those groups in a list. Note that they are still strings, not numbers.
You can use regex:
>>> import re
>>> re.findall('[0-9]+', 'CORSAIR VENGEANCE RGB 16GB (2X8GB) DDR4 3200MHZ CL 16')
['16', '2', '8', '4', '3200', '16']
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.