I need to write a function that takes a string (str), and two other strings (call it replace1 and replace2), and an integer (n). The function shall return a new string, where all the string inputs from replace1 in the first string (str) and replace the new string with replace1 depending on where you want the new input. I am not supposed to use built-in functions, but I can use lens (we can suppose that replace1 has the length 1). Example ( call it replaceChoice):
>>> replaceChoice(“Mississippi”, “s”, “l”, 2)
'Mislissippi'
I hope that I explained it well. Here is my attempt:
def replaceChoice(str1, replace1,n):
newString=""
for x in str:
if x=="str1":
newString=newString+replace
else:
newString=newString+x
return newString
I assume from your question that you want to replace the nth occurrence of r1 with r2.
Is this what you want?
>>> def replaceChoice(str1, r1, r2, n):
... new_str = ""
... replaced = False
... for i in str1:
... if i==r1:
... n-=1
... if n==0 and not replaced:
... replaced = True
... new_str+=r2
... else:
... new_str+=i
... return new_str
...
>>> replaceChoice("Mississippi", "s", "l", 2)
'Mislissippi'
Related
Hi I'm looking for a regular expression that would allow me not only to replace characters but also to annotate the occurrence number.
For example I would like to replace all special characters with "s", all letters with "c" and all number with "d" and annotate their occurrence between "{}".
If I have "123-45AB-78!£", I would like to get d{3}s{1}d{3}c{2}s{1}d{2}s{2}.
Is there a way to do that with regex?
Many thanks
Here is one approach using re.sub with a callback function:
import re
def repl(m):
c = m.group()
if re.search(r'^[A-Za-z]+$', c):
return 'c{' + str(len(c.decode('utf8'))) + '}'
elif re.search(r'^\d+$', c):
return 'd{' + str(len(c.decode('utf8'))) + '}'
else:
return 's{' + str(len(c.decode('utf8'))) + '}'
x = "123-45AB-78!£"
print(re.sub('[A-Za-z]+|\d+|\D+', repl, x))
# d{3}s{1}d{2}c{2}s{1}d{2}s{2}
Note that since your input string contains non ASCII characters, we cannot simply use len() to find the numbes of characters in the string. Assuming a UTF-8 character set and a string str, we can use the following formula:
len(str.decode('utf8'))
Here is a method that first replaces each character by its type-character, then counts them with itertools.groupby. I'm not sure it is any faster than the good answer given by Tim, but it should be comparable.
x = "123-45AB-78!£"
for pat, sub in [(r"[A-Za-z]", "c"), (r"\d", "d"), (r"[^\d\w]", "s")]:
x = re.sub(pat, sub, x)
print(x) # dddsddccsddss
y = "".join([f"{k}{{{len(list(g))}}}" for k, g in groupby(x)])
print(y) # d{3}s{1}d{2}c{2}s{1}d{2}s{2}
I'm trying to replace the "i", "s", and "p" in the string "Mississippi" with the second character of each list in the list below. When replacement is printed by itself, it will replace all of the characters with the character specified, but it will only actually replace the first character that I want to replace in the new string, not all of them. (Ex. Mlsslsslppl instead of Ml$$l$$lzzl) I don't really know where to go from here to fix this problem.
def substitutePairs(myString, pairsList):
for item in myString:
for separateList in pairsList:
for char in separateList:
if item == char:
replacement = separateList[1]
newString = myString.replace(item, replacement)
return newString
def main():
myString = "Mississippi"
pairsList = [['i', 'l'],['s', '$'],['p', 'z']]
print(substitutePairs(myString, pairsList))
if __name__ == "__main__":
main()
You need to iteratively apply replace to the previous result so that you can accumulate all of the substitutions into one string. A single loop over pairs_list is all you need; replace will do the work for you of iterating through my_string.
>>> def subst_pairs(my_string, pairs_list):
... for x, y in pairs_list:
... my_string = my_string.replace(x, y)
... return my_string
...
>>> subst_pairs("Mississippi", [['i', 'l'],['s', '$'],['p', 'z']])
'Ml$$l$$lzzl'
That really looks like a job for str.translate instead:
>>> 'Mississippi'.translate(str.maketrans('isp', 'l$z'))
'Ml$$l$$lzzl'
If you really have such a list (and can't use the above), you can still build the translation table from it:
def subst_pairs(my_string, pairs_list):
return my_string.translate({ord(k): v for k, v in pairs_list})
Or:
def subst_pairs(my_string, pairs_list):
return my_string.translate(str.maketrans(*map(''.join, zip(*pairs_list))))
I'm trying to compress a string in a way that any sequence of letters in strict alphabetical order is swapped with the first letter plus the length of the sequence.
For example, the string "abcdefxylmno", would become: "a6xyl4"
Single letters that aren't in order with the one before or after just stay the way they are.
How do I check that two letters are successors (a,b) and not simply in alphabetical order (a,c)? And how do I keep iterating on the string until I find a letter that doesn't meet this requirement?
I'm also trying to do this in a way that makes it easier to write an inverse function (that given the result string gives me back the original one).
EDIT :
I've managed to get the function working, thanks to your suggestion of using the alphabet string as comparison; now I'm very much stuck on the inverse function: given "a6xyl4" expand it back into "abcdefxylmno".
After quite some time I managed to split the string every time there's a number and I made a function that expands a 2 char string, but it fails to work when I use it on a longer string:
from string import ascii_lowercase as abc
def subString(start,n):
L=[]
ind = abc.index(start)
newAbc = abc[ind:]
for i in range(len(newAbc)):
while i < n:
L.append(newAbc[i])
i+=1
res = ''.join(L)
return res
def unpack(S):
for i in range(len(S)-1):
if S[i] in abc and S[i+1] not in abc:
lett = str(S[i])
num = int(S[i+1])
return subString(lett,num)
def separate(S):
lst = []
for i in S:
lst.append(i)
for el in lst:
if el.isnumeric():
ind = lst.index(el)
lst.insert(ind+1,"-")
a = ''.join(lst)
L = a.split("-")
if S[-1].isnumeric():
L.remove(L[-1])
return L
else:
return L
def inverse(S):
L = separate(S)
for i in L:
return unpack(i)
Each of these functions work singularly, but inverse(S) doesn't output anything. What's the mistake?
You can use the ord() function which returns an integer representing the Unicode character. Sequential letters in alphabetical order differ by 1. Thus said you can implement a simple funtion:
def is_successor(a,b):
# check for marginal cases if we dont ensure
# input restriction somewhere else
if ord(a) not in range(ord('a'), ord('z')) and ord(a) not in range(ord('A'),ord('Z')):
return False
if ord(b) not in range(ord('a'), ord('z')) and ord(b) not in range(ord('A'),ord('Z')):
return False
# returns true if they are sequential
return ((ord(b) - ord(a)) == 1)
You can use chr(int) method for your reversing stage as it returns a string representing a character whose Unicode code point is an integer given as argument.
This builds on the idea that acceptable subsequences will be substrings of the ABC:
from string import ascii_lowercase as abc # 'abcdefg...'
text = 'abcdefxylmno'
stack = []
cache = ''
# collect subsequences
for char in text:
if cache + char in abc:
cache += char
else:
stack.append(cache)
cache = char
# if present, append the last sequence
if cache:
stack.append(cache)
# stack is now ['abcdef', 'xy', 'lmno']
# Build the final string 'a6x2l4'
result = ''.join(f'{s[0]}{len(s)}' if len(s) > 1 else s for s in stack)
I'm trying to creating a code that prints till 'n' and replaces the rest of the characters with "*".
This is my code so far"
def replace(s,n):
return s.replace(s[n:], "*")
However it outputs
replace("hello", 2)
'he*'
it should be 'he***'
You should multiply "*" by the number of characters you want to replace. In addition, you should add (len(s)-n)*"*" after s[n:] instead of replacing (as the same set of characters may appear in several places in the string). You may do that as follows:
def replace(s,n):
return s[:n]+(len(s)-n)*"*"
replace('hello', 2)
This prints 'he***'
There are two fundamental issues. First, s.replace will replace the entire first argument with the second. And perhaps even more important, it replaces it anywhere it finds it on the string. So, consider the following example:
>>> def replace(s,n):
... return s.replace(s[n:], "*")
...
>>> replace('lalahahalala', 8)
'*haha*'
>>>
Instead, you should take a different approach, iterate the string, returning the character in that string if the index is < n, else, return '*':
>>> def replace(s, n):
... return ''.join(c if i < n else '*' for i,c in enumerate(s))
...
>>> replace("hello", 2)
'he***'
>>> replace('lalahahalala', 8)
'lalahaha****'
Here is a version of the above using a for-loop instead of a generator expression:
>>> def replace(s, n):
... char_list = []
... for i, c in enumerate(s):
... if i < n:
... char_list.append(c)
... else:
... char_list.append('*')
... return ''.join(char_list)
...
>>> replace('hello', 2)
'he***'
>>> replace('lalahahalala', 8)
'lalahaha****'
>>>
The replace approach is fundamentally flawed, because replace looks for a substring to replace anywhere in the source string, it's not for replacing characters at some position. Even with the "fixed" version by #MiriamFarber (now he edited it, look at the revision history) you'd get erroneous output like
replace("chachacha", 6) # returns *********
What you want is to truncate the string at the requested position, and append to it a string of as many asterisks as the characters you removed.
def replace(s, n):
return s[:n] + '*'*(len(s)-n)
You can slice s until n position and then concatenate the rest of the string using * operator. You don't need to use replace method:
def replace(s,n):
return s[:n] + (len(s)-n)*'*'
The output is:
replace('hello', 2)
'he***'
I need to replace a string in case sensitive way. For example
abc -> def
Abc -> Def
aBc -> dEf
abC -> deF
What can I do this with Python?
from string import maketrans
"Abc".translate(maketrans("abcABC", "defDEF"))
Expanding on Mark Byers' answer, Here's a solution which works for replacement text of any length.
The trick is to send a function to re.sub().
import re
def case_sensitive_replace(string, old, new):
""" replace occurrences of old with new, within string
replacements will match the case of the text it replaces
"""
def repl(match):
current = match.group()
result = ''
all_upper=True
for i,c in enumerate(current):
if i >= len(new):
break
if c.isupper():
result += new[i].upper()
else:
result += new[i].lower()
all_upper=False
#append any remaining characters from new
if all_upper:
result += new[i+1:].upper()
else:
result += new[i+1:].lower()
return result
regex = re.compile(re.escape(old), re.I)
return regex.sub(repl, string)
print case_sensitive_replace("abc Abc aBc abC ABC",'abc','de')
print case_sensitive_replace("abc Abc aBc abC ABC",'abc','def')
print case_sensitive_replace("abc Abc aBc abC ABC",'abc','defg')
Result:
de De dE de DE
def Def dEf deF DEF
defg Defg dEfg deFg DEFG
Here's a method using regular expressions. The key point is that when it finds a match it first modifies the replacement string to match the casing of the matched string. This works because re.sub can take a function as a replacement instead of just a string.
import re
def case_sensitive_replace(s, before, after):
regex = re.compile(re.escape(before), re.I)
return regex.sub(lambda x: ''.join(d.upper() if c.isupper() else d.lower()
for c,d in zip(x.group(), after)), s)
test = '''
abc -> def
Abc -> Def
aBc -> dEf
abC -> deF
'''
result = case_sensitive_replace(a, 'abc', 'def')
print(result)
Result:
def -> def
Def -> Def
dEf -> dEf
deF -> deF
Long time lurker, thought I'd post a suggestion here as some of these seem fairly convoluted.
print map(lambda a, b: b.lower() if a.islower() else b.upper(), "aBc", "def")
It does assume both strings are the same length, however you could easily replace the lambda with a proper function and check for None on the first input.
Not the most efficient way, and it's very crude, but probably something like this could work:
def case_insensitive_replace(string, old, new):
upper_indices = [idx for idx, char in enumerate(string) if char.isupper()]
replaced = list(string.lower().replace(old.lower(), new.lower()))
for idx in upper_indices:
replaced[idx] = replaced[idx].upper()
return "".join(replaced)
I understand that You want to change the second string case according to the first string.
Am i right? So, my solution is the following. String s2 change its case according to the corresponding string s1. The result is storing in s3. One assumption here is the two strings has the same length.
s1 = "AaBb"
s2 = "cdef"
s3 = ""
index = 0
length = len(s1)
while(True):
if s1[index].isupper():
temp = s2[index].upper()
else:
temp = s2[index].lower()
s3 = s3 + temp
index +=1
if index == length:
break
print s3
This will work, although you probably want to add some checks that the string lengths are the same:
string1 = "AbcDEFghiJKLmnO"
string2 = "some other text"
string2 = "".join((string2[i].upper() if string1[i].isupper() else string2[i].lower()
for i in range(len(string1))))