Replace substring of given indices range - python

I am new in Python programming. I am stuck at one point. Let's say I have string "hello-world". I want to replace all the characters of this string with "*" except first & last. so the result will be "h***-****d".
One way to do this as below:
In [1]: s = "hello-world"
In [2]: s[0] + "*"*(len(s)-2) + s[-1]
Out[2]: 'h*********d'
If I want to replace all characters with "*" except first & last 2 characters
In [3]: s[:2] + "*"*(len(s)-4) + s[-2:]
Out[3]: 'he*******ld'
Is there any pretty way to handle these type of problems. Any help would be appreciated. Thanks.

I think what you want to do is this:
def obscure(string, n):
characters = list(string)
characters[n:-n] = '*' * len(characters[n:-n])
obscured = ''.join(characters)
return obscured
Turn the string into a list of characters. Replace the ones you want to obscure. Then join the list back into a string.

You can use str.join (and the string module to check against letters):
s[0] + ''.join(['*' if i in string.ascii_letters else i
for i in s[1:-1]]) + s[-1]
Since you said you wanted h****-****d where the hyphen isn't replaced, you would need to test whether the characters are letters or not. You could change string.ascii_letters to:
chars = 'abcdefghijklmnopqrstuvwxyz'
chars = chars + chars.upper() + '0123456789' # + 'some_other_chars'
...if you want to include other characters like numbers or punctuation. Or you can write out the letters you want to replace manually.
You may also want to perform a check to see whether the string is 3 characters or more so that no errors are raised.

You could define a function to not repeat yourself:
def replace(s, n):
if len(s) > n*2:
return s[:n] + '*'*(len(s)-n*2) + s[-n:]
return s
print(replace('hello-world', 1)) # h*********d
print(replace('hello-world', 2)) # he*******ld
print(replace('hello', 2)) # he*lo
print(replace('hello', 3)) # hello
You can also use some kind of string formatting instead of concatenation (which should be more efficient), e.g. f-strings available in 3.6+:
def replace(s, n):
if len(s) > n*2:
return f"{ s[:n] }{ '*'*(len(s)-n*2) }{ s[-n:] }"
return s

You can try this.
s="hello-world"
for i in s[1:-1]:
if i.isalpha():
s=s.replace(i,"*")

Related

Python use in the same line "For", "If" and "Else"

I want to learn python "secrets", it's possible to put all this code in one line?:
word_not_clean="Casà: 25$"
word_clean=""
for x in word_not_clean:
if x in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789':
word_clean+=x
else:
word_clean+='_'
This sentences work, but no replace especial characters:
word_clean="".join(x for x in word_not_clean if x in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
But this sentence with else doesn't works.
word_clean="".join(x for x in word_not_clean if x in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' else '_')
Thank you in advance.
If you want to do it using list comprehension (one-lining lines) add the if-else statement before the for:
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
word_clean = "".join([x if x in chars else '_' for x in word_not_clean])
You can try like the below and also, you can use string.ascii_uppercase + string.ascii_lowercase + string.digits.
import string
word_not_clean="Casà: 25$"
chars_check = string.ascii_uppercase + string.ascii_lowercase + string.digits
result = ''.join(x if x in chars_check else '_' for x in word_not_clean)
print(result)
# 'Cas___25_'
Explanation:
>>> import string
>>> string.ascii_uppercase + string.ascii_lowercase + string.digits
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
There are two different kinds of if statements in generator expressions. The first kind (which I’ll call an “inclusion if”) comes after the for statement, and indicates whether a given item from an iterator should be included in the resultant list. This is what you’re using in the first example.
The second kind (which I’ll call a “ternary if”) comes before the if statement, and used Python’s ternary operator __ if __ else __ to decide what should be outputted by the iterator. This is what you want for the second example, but you need to reorder your terms:
OKAY = “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”
word_clean = "".join(
x if x in OKAY else '_' # Ternary if
for x in word_not_clean
)

Python regex with number of occurences

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}

If a specific string, A, is present at the begining and/or end of a string B, how do we remove A from B?

My question is similar, but different from the following:
How do I remove a substring from the end of a string in Python?
Suppose we have:
input = "baabbbbb_xx_ba_xxx_abbbbbba"
We want to want to keep everything except the ba at the end and ba at the beginning.
1) Direct strip() fails
strip treats the string as a set. That is, strip will remove the letters a and b appearing in any order. We want to only remove the characters ba if they appear in that exact order. Also, unlike strip, we want only zero or one copies removed from the end of the string. "x\n\n\n\n".strip() will remove many new-lines, not just one.
input = "baabbbbb_xx_ba_xxx_abbbbbba"
output = input.strip("ba")
print(output)
prints "_xx_ba_xxx_"
2) Direct replace() fails
input = "xx_ba_xxx"
output = input.replace("ba", "")
print(output)
# prints `xx__xxx`
Not cool; we only want to remove the sequence "ba" from the beginning and end of the string, not the middle.
3) Just nope
input = "baabbbbb_xx_ba_xxx_abbbbbba"
output = "ba".join(input.rsplit("ba", 1))
print(output)
# output==input
Final Note
The solution must be general: a function accepting any two input strings, once of which might not be "ba". The undesired leading and trailing strings might contain ".", "*" and other characters not nice for use in regular expressions.
My solution uses basic hashing, however, be aware of hash collision.
Let me know if this helps you with your problem.
import functools
def strip_ed(pattern, string):
# pattern is not a substring of string
if len(pattern) > len(string):
return -1
base = 26
# Hash codes for the beginning of the string
string_hash_beginning = functools.reduce(lambda h, c: h * base + ord(c), string[:len(pattern)], 0)
# Hash codes for the ending of the string
string_hash_end = functools.reduce(lambda h, c: h * base + ord(c), string[-len(pattern):], 0)
# Hash codes for the pattern
pattern_hash = functools.reduce(lambda h, c: h * base + ord(c), pattern, 0)
while True:
if string_hash_beginning == string_hash_end and \
string_hash_beginning == pattern_hash and \
string[:len(pattern)] == pattern:
return string[len(pattern):-len(pattern)]
elif string_hash_beginning == pattern_hash and string[:len(pattern)] == pattern:
return string[len(pattern):]
elif string_hash_end == pattern_hash and string[-len(pattern):] == pattern:
return string[:-len(pattern)]
else:
return string
This seems to work:
def ordered_strip(whole, part):
center = whole
if whole.endswith(part):
center = center[:-len(part)]
if whole.startswith(part):
center = center[len(part):]
return center

How to replace part of string with a character

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***'

Python - making a function that would add "-" between letters

I'm trying to make a function, f(x), that would add a "-" between each letter:
For example:
f("James")
should output as:
J-a-m-e-s-
I would love it if you could use simple python functions as I am new to programming. Thanks in advance. Also, please use the "for" function because it is what I'm trying to learn.
Edit:
yes, I do want the "-" after the "s".
Can I try like this:
>>> def f(n):
... return '-'.join(n)
...
>>> f('james')
'j-a-m-e-s'
>>>
Not really sure if you require the last 'hyphen'.
Edit:
Even if you want suffixed '-', then can do like
def f(n):
return '-'.join(n) + '-'
As being learner, it is important to understand for your that "better to concat more than two strings in python" would be using str.join(iterable), whereas + operator is fine to append one string with another.
Please read following posts to explore further:
Any reason not to use + to concatenate two strings?
which is better to concat string in python?
How slow is Python's string concatenation vs. str.join?
Also, please use the "for" function because it is what I'm trying to learn
>>> def f(s):
m = s[0]
for i in s[1:]:
m += '-' + i
return m
>>> f("James")
'J-a-m-e-s'
m = s[0] character at the index 0 is assigned to the variable m
for i in s[1:]: iterate from the second character and
m += '-' + i append - + char to the variable m
Finally return the value of variable m
If you want - at the last then you could do like this.
>>> def f(s):
m = ""
for i in s:
m += i + '-'
return m
>>> f("James")
'J-a-m-e-s-'
text_list = [c+"-" for c in text]
text_strung = "".join(text_list)
As a function, takes a string as input.
def dashify(input):
output = ""
for ch in input:
output = output + ch + "-"
return output
Given you asked for a solution that uses for and a final -, simply iterate over the message and add the character and '-' to an intermediate list, then join it up. This avoids the use of string concatenations:
>>> def f(message)
l = []
for c in message:
l.append(c)
l.append('-')
return "".join(l)
>>> print(f('James'))
J-a-m-e-s-
I'm sorry, but I just have to take Alexander Ravikovich's answer a step further:
f = lambda text: "".join([c+"-" for c in text])
print(f('James')) # J-a-m-e-s-
It is never too early to learn about list comprehension.
"".join(a_list) is self-explanatory: glueing elements of a list together with a string (empty string in this example).
lambda... well that's just a way to define a function in a line. Think
square = lambda x: x**2
square(2) # returns 4
square(3) # returns 9
Python is fun, it's not {enter-a-boring-programming-language-here}.

Categories

Resources