Hello I'm trying to split a string without removing the delimiter and it can have multiple delimiters.
The delimiters can be 'D', 'M' or 'Y'
For example:
>>>string = '1D5Y4D2M'
>>>re.split(someregex, string) #should ideally return
['1D', '5Y', '4D', '2M']
To keep the delimiter I use Python split() without removing the delimiter
>>> re.split('([^D]+D)', '1D5Y4D2M')
['', '1D', '', '5Y4D', '2M']
For multiple delimiters I use In Python, how do I split a string and keep the separators?
>>> re.split('(D|M|Y)', '1D5Y4D2M')
['1', 'D', '5', 'Y', '4', 'D', '2', 'M', '']
Combining both doesn't quite make it.
>>> re.split('([^D]+D|[^M]+M|[^Y]+Y)', string)
['', '1D', '', '5Y4D', '', '2M', '']
Any ideas?
I'd use findall() in your case. How about:
re.findall(r'\d+[DYM]', string
Which will result in:
['1D', '5Y', '4D', '2M']
(?<=(?:D|Y|M))
You need 0 width assertion split.Can be done using regex module python.
See demo.
https://regex101.com/r/aKV13g/1
You can split at the locations right after D, Y or M but not at the end of the string with
re.split(r'(?<=[DYM])(?!$)', text)
See the regex demo. Details:
(?<=[DYM]) - a positive lookbehind that matches a location that is immediately preceded with D or Y or M
(?!$) - a negative lookahead that fails the match if the current position is the string end position.
Note
In the current scenario, (?<=[DYM]) can be used instead of a more verbose (?<=D|Y|M) since all alternatives are single characters. If you have multichar delimiters, you would have to use a non-capturing group, (?:...), with lookbehind alternatives inside it. For example, to separate right after Y, DX and MZB you would use (?:(?<=Y)|(?<=DX)|(?<=MZB)). See Python Regex Engine - "look-behind requires fixed-width pattern" Error
I think it will work fine without regex or split
time complexity O(n)
string = '1D5Y4D2M'
temp=''
res = []
for x in string:
if x=='D':
temp+='D'
res.append(temp)
temp=''
elif x=='M':
temp+='M'
res.append(temp)
temp=''
elif x=='Y':
temp+='Y'
res.append(temp)
temp=''
else:
temp+=x
print(res)
using translate
string = '1D5Y4D2M'
delimiters = ['D', 'Y', 'M']
result = string.translate({ord(c): f'{c}*' for c in delimiters}).strip('.*').split('*')
print(result)
>>> ['1D', '5Y', '4D', '2M']
Related
For example I get following input:
-9x+5x-2-4x+5
And I need to get following list:
['-9x', '5x', '-2', '-4x', '5']
Here is my code, but I don't know how to deal with minuses.
import re
text = '-3x-5x+2=9x-9'
text = re.split(r'\W', text)
print(text)
warning: I cannot use any libraries except re and math.
You could re.findall all groups of characters followed by + or - (or end-of-string $), then strip the + (which, like -, is still part of the following group) from the substrings.
>>> s = "-9x+5x-2-4x+5"
>>> [x.strip("+") for x in re.findall(r".+?(?=[+-]|$)", s)]
['-9x', '5x', '-2', '-4x', '5']
Similarly, for the second string with =, add that to the character group and also strip it off the substrings:
>>> s = '-3x-5x+2=9x-9'
>>> [x.strip("+=") for x in re.findall(r".+?(?=[+=-]|$)", s)]
>>> ['-3x', '-5x', '2', '9x', '-9']
Or apply the original comprehension to the substrings after splitting by =, depending on how the result should look like:
>>> [[x.strip("+") for x in re.findall(r".+?(?=[+-]|$)", s2)] for s2 in s.split("=")]
>>> [['-3x', '-5x', '2'], ['9x', '-9']]
In fact, now that I think of it, you can also just findall that match an optional minus, followed by some digits, and an optional x, with or without splitting by = first:
>>> [re.findall(r"-?\d+x?", s2) for s2 in s.split("=")]
[['-3x', '-5x', '2'], ['9x', '-9']]
One of many possible ways:
import re
term = "-9x+5x-2-4x+5"
rx = re.compile(r'-?\d+[a-z]?')
factors = rx.findall(term)
print(factors)
This yields
['-9x', '5x', '-2', '-4x', '5']
For your example data, you might split on either a plus or equals sign or split when asserting a minus sign on the right which is not at the start of the string.
[+=]|(?=(?<!^)-)
[+=] Match either + or =
| Or
(?=(?<!^)-) Positive lookahead, assert what is on the right is - but not at the start of the string
Regex demo | Python demo
Output for both example strings
['-9x', '5x', '-2', '-4x', '5']
['-3x', '-5x', '2', '9x', '-9']
How to remove alphabets and extract numbers using regex in python?
import re
l=["098765432123 M","123456789012"]
s = re.findall(r"(?<!\d)\d{12}", l)
print(s)
Expected Output:
123456789012
If all you want is to have filtered list, consisting elements with pure digits, use filter with str.isdigit:
list(filter(str.isdigit, l))
Or as #tobias_k suggested, list comprehension is always your friend:
[s for s in l if s.isdigit()]
Output:
['123456789012']
I would suggest to use a negative lookahead assertion, if as stated you want to use regex only.
l=["098765432123 M","123456789012"]
res=[]
for a in l:
s = re.search(r"(?<!\d)\d{12}(?! [a-zA-Z])", a)
if s is not None:
res.append(s.group(0))
The result would then be:
['123456789012']
To keep only digits you can do re.findall('\d',s), but you'll get a list:
s = re.findall('\d', "098765432123 M")
print(s)
> ['0', '9', '8', '7', '6', '5', '4', '3', '2', '1', '2', '3']
So to be clear, you want to ignore the whole string if there is a alphabetic character in it? Or do you still want to extract the numbers of a string with both numbers and alphabetic characters in it?
If you want to find all numbers, and always find the longest number use this:
regex = r"\d+"
matches = re.finditer(regex, test_str, re.MULTILINE)
\d will search for digits, + will find one or more of the defined characters, and will always find the longest consecutive line of these characters.
If you only want to find strings without alphabets:
import re
regex = r"[a-zA-Z]"
test_str = ("098765432123 M", "123456789012")
for x in test_str:
if not re.search(regex, x):
print(x)
I would like to split a string into sections of numbers and sections of text/symbols
my current code doesn't include negative numbers or decimals, and behaves weirdly, adding an empty list element on the end of the output
import re
mystring = 'AD%5(6ag 0.33--9.5'
newlist = re.split('([0-9]+)', mystring)
print (newlist)
current output:
['AD%', '5', '(', '6', 'ag ', '0', '.', '33', '--', '9', '.', '5', '']
desired output:
['AD%', '5', '(', '6', 'ag ', '0.33', '-', '-9.5']
Your issue is related to the fact that your regex captures one or more digits and adds them to the resulting list and digits are used as a delimiter, the parts before and after are considered. So if there are digits at the end, the split results in the empty string at the end to be added to the resulting list.
You may split with a regex that matches float or integer numbers with an optional minus sign and then remove empty values:
result = re.split(r'(-?\d*\.?\d+)', s)
result = filter(None, result)
To match negative/positive numbers with exponents, use
r'([+-]?\d*\.?\d+(?:[eE][-+]?\d+)?)'
The -?\d*\.?\d+ regex matches:
-? - an optional minus
\d* - 0+ digits
\.? - an optional literal dot
\d+ - one or more digits.
Unfortunately, re.split() does not offer an "ignore empty strings" option. However, to retrieve your numbers, you could easily use re.findall() with a different pattern:
import re
string = "AD%5(6ag0.33-9.5"
rx = re.compile(r'-?\d+(?:\.\d+)?')
numbers = rx.findall(string)
print(numbers)
# ['5', '6', '0.33', '-9.5']
As mentioned here before, there is no option to ignore the empty strings in re.split() but you can easily construct a new list the following way:
import re
mystring = "AD%5(6ag0.33--9.5"
newlist = [x for x in re.split('(-?\d+\.?\d*)', mystring) if x != '']
print newlist
output:
['AD%', '5', '(', '6', 'ag', '0.33', '-', '-9.5']
How do i find string in nested brackets
Lets say I have a string
uv(wh(x(yz))
and I want to find all string in brackets (so wh, x, yz)
import re
s="uuv(wh(x(yz))"
regex = r"(\(\w*?\))"
matches = re.findall(regex, s)
The above code only finds yz
Can I modify this regex to find all matches?
To get all properly parenthesized text:
import re
def get_all_in_parens(text):
in_parens = []
n = "has something to substitute"
while n:
text, n = re.subn(r'\(([^()]*)\)', # match flat expression in parens
lambda m: in_parens.append(m.group(1)) or '', text)
return in_parens
Example:
>>> get_all_in_parens("uuv(wh(x(yz))")
['yz', 'x']
Note: there is no 'wh' in the result due to the unbalanced paren.
If the parentheses are balanced; it returns all three nested substrings:
>>> get_all_in_parens("uuv(wh(x(yz)))")
['yz', 'x', 'wh']
>>> get_all_in_parens("a(b(c)de)")
['c', 'bde']
Would a string split work instead of a regex?
s='uv(wh(x(yz))'
match=[''.join(x for x in i if x.isalpha()) for i in s.split('(')]
>>>print(match)
['uv', 'wh', 'x', 'yz']
>>> match.pop(0)
You could pop off the first element because if it was contained in a parenthesis, the first position would be blank, which you wouldn't want and if it wasn't blank that means it wasn't in the parenthesis so again, you wouldn't want it.
Since that wasn't flexible enough something like this would work:
def match(string):
unrefined_match=re.findall('\((\w+)|(\w+)\)', string)
return [x for i in unrefined_match for x in i if x]
>>> match('uv(wh(x(yz))')
['wh', 'x', 'yz']
>>> match('a(b(c)de)')
['b', 'c', 'de']
Using regex a pattern such as this might potentially work:
\((\w{1,})
Result:
['wh', 'x', 'yz']
Your current pattern escapes the ( ) and doesn't treat them as a capture group.
Well if you know how to covert from PHP regex to Python , then you can use this
\(((?>[^()]+)|(?R))*\)
I have a string like "F(230,24)F[f(22)_(23);(2)%[+(45)FF]]", where each character except for parentheses and what they enclose represents a kind of instruction. A character can be followed by an optional list of arguments specified in optional parentheses.
Such a string i would like to split the string into
['F(230,24)', 'F', '[', 'f(22)', '_(23)', ';(2)', '%', '[', '+(45)', 'F', 'F', ']', ']'], however at the moment i only get ['F(230,24)', 'F', '[', 'f(22)_(23);(2)', '%', '[', '+(45)', 'F', 'F', ']', ']'] (a substring was not split correctly).
Currently i am using list(filter(None, re.split(r'([A-Za-z\[\]\+\-\^\&\\\/%_;~](?!\())', string))), which is just a mess of characters and a negative lookahead for (. list(filter(None, <list>)) is used to remove empty strings from the result.
I am aware that this is likely caused by Python's re.split having been designed not to split on a zero length match, as discussed here.
However i was wondering what would be a good solution? Is there a better way than re.findall?
Thank you.
EDIT: Unfortunately i am not allowed to use custom packages like regex module
You can use re.findall to find out all single character optionally followed by a pair of parenthesis:
import re
s = "F(230,24)F[f(22)_(23);(2)%[+(45)FF]]"
re.findall("[^()](?:\([^()]*\))?", s)
['F(230,24)',
'F',
'[',
'f(22)',
'_(23)',
';(2)',
'%',
'[',
'+(45)',
'F',
'F',
']',
']']
[^()] match a single character except for parenthesis;
(?:\([^()]*\))? denotes a non-capture group(?:) enclosed by a pair of parenthesis and use ? to make the group optional;
I am aware that this is likely caused by Python's re.split having been designed not to split on a zero length match
You can use the VERSION1 flag of the regex module. Taking that example from the thread you've linked - see how split() produces zero-width matches as well:
>>> import regex as re
>>> re.split(r"\s+|\b", "Split along words, preserve punctuation!", flags=re.V1)
['', 'Split', 'along', 'words', ',', 'preserve', 'punctuation', '!']
Another solution. This time the pattern recognize strings with the structure SYMBOL[(NUMBER[,NUMBER...])]. The function parse_it returns True and the tokens if the string match with the regular expression and False and empty if don't match.
import re
def parse_it(string):
'''
Input: String to parse
Output: True|False, Tokens|empty_string
'''
pattern = re.compile('[A-Za-z\[\]\+\-\^\&\\\/%_;~](?:\(\d+(?:,\d+)*\))?')
tokens = pattern.findall(string)
if ''.join(tokens) == string:
res = (True, tokens)
else:
res = (False, '')
return res
good_string = 'F(230,24)F[f(22)_(23);(2)%[+(45)FF]]'
bad_string = 'F(2a30,24)F[f(22)_(23);(2)%[+(45)FF]]' # There is an 'a' in a bad place.
print(parse_it(good_string))
print(parse_it(bad_string))
Output:
(True, ['F(230,24)', 'F', '[', 'f(22)', '_(23)', ';(2)', '%', '[',
'+(45)', 'F', 'F', ']', ']'])(False, '')