I'm trying to remove specific characters from a string using Python. This is the code I'm using right now. Unfortunately it appears to do nothing to the string.
for char in line:
if char in " ?.!/;:":
line.replace(char,'')
How do I do this properly?
Strings in Python are immutable (can't be changed). Because of this, the effect of line.replace(...) is just to create a new string, rather than changing the old one. You need to rebind (assign) it to line in order to have that variable take the new value, with those characters removed.
Also, the way you are doing it is going to be kind of slow, relatively. It's also likely to be a bit confusing to experienced pythonators, who will see a doubly-nested structure and think for a moment that something more complicated is going on.
Starting in Python 2.6 and newer Python 2.x versions *, you can instead use str.translate, (see Python 3 answer below):
line = line.translate(None, '!##$')
or regular expression replacement with re.sub
import re
line = re.sub('[!##$]', '', line)
The characters enclosed in brackets constitute a character class. Any characters in line which are in that class are replaced with the second parameter to sub: an empty string.
Python 3 answer
In Python 3, strings are Unicode. You'll have to translate a little differently. kevpie mentions this in a comment on one of the answers, and it's noted in the documentation for str.translate.
When calling the translate method of a Unicode string, you cannot pass the second parameter that we used above. You also can't pass None as the first parameter. Instead, you pass a translation table (usually a dictionary) as the only parameter. This table maps the ordinal values of characters (i.e. the result of calling ord on them) to the ordinal values of the characters which should replace them, or—usefully to us—None to indicate that they should be deleted.
So to do the above dance with a Unicode string you would call something like
translation_table = dict.fromkeys(map(ord, '!##$'), None)
unicode_line = unicode_line.translate(translation_table)
Here dict.fromkeys and map are used to succinctly generate a dictionary containing
{ord('!'): None, ord('#'): None, ...}
Even simpler, as another answer puts it, create the translation table in place:
unicode_line = unicode_line.translate({ord(c): None for c in '!##$'})
Or, as brought up by Joseph Lee, create the same translation table with str.maketrans:
unicode_line = unicode_line.translate(str.maketrans('', '', '!##$'))
* for compatibility with earlier Pythons, you can create a "null" translation table to pass in place of None:
import string
line = line.translate(string.maketrans('', ''), '!##$')
Here string.maketrans is used to create a translation table, which is just a string containing the characters with ordinal values 0 to 255.
Am I missing the point here, or is it just the following:
string = "ab1cd1ef"
string = string.replace("1", "")
print(string)
# result: "abcdef"
Put it in a loop:
a = "a!b#c#d$"
b = "!##$"
for char in b:
a = a.replace(char, "")
print(a)
# result: "abcd"
>>> line = "abc##!?efg12;:?"
>>> ''.join( c for c in line if c not in '?:!/;' )
'abc##efg12'
With re.sub regular expression
Since Python 3.5, substitution using regular expressions re.sub became available:
import re
re.sub('\ |\?|\.|\!|\/|\;|\:', '', line)
Example
import re
line = 'Q: Do I write ;/.??? No!!!'
re.sub('\ |\?|\.|\!|\/|\;|\:', '', line)
'QDoIwriteNo'
Explanation
In regular expressions (regex), | is a logical OR and \ escapes spaces and special characters that might be actual regex commands. Whereas sub stands for substitution, in this case with the empty string ''.
The asker almost had it. Like most things in Python, the answer is simpler than you think.
>>> line = "H E?.LL!/;O:: "
>>> for char in ' ?.!/;:':
... line = line.replace(char,'')
...
>>> print line
HELLO
You don't have to do the nested if/for loop thing, but you DO need to check each character individually.
For the inverse requirement of only allowing certain characters in a string, you can use regular expressions with a set complement operator [^ABCabc]. For example, to remove everything except ascii letters, digits, and the hyphen:
>>> import string
>>> import re
>>>
>>> phrase = ' There were "nine" (9) chick-peas in my pocket!!! '
>>> allow = string.letters + string.digits + '-'
>>> re.sub('[^%s]' % allow, '', phrase)
'Therewerenine9chick-peasinmypocket'
From the python regular expression documentation:
Characters that are not within a range can be matched by complementing
the set. If the first character of the set is '^', all the characters
that are not in the set will be matched. For example, [^5] will match
any character except '5', and [^^] will match any character except
'^'. ^ has no special meaning if it’s not the first character in the
set.
line = line.translate(None, " ?.!/;:")
>>> s = 'a1b2c3'
>>> ''.join(c for c in s if c not in '123')
'abc'
Strings are immutable in Python. The replace method returns a new string after the replacement. Try:
for char in line:
if char in " ?.!/;:":
line = line.replace(char,'')
This is identical to your original code, with the addition of an assignment to line inside the loop.
Note that the string replace() method replaces all of the occurrences of the character in the string, so you can do better by using replace() for each character you want to remove, instead of looping over each character in your string.
I was surprised that no one had yet recommended using the builtin filter function.
import operator
import string # only for the example you could use a custom string
s = "1212edjaq"
Say we want to filter out everything that isn't a number. Using the filter builtin method "...is equivalent to the generator expression (item for item in iterable if function(item))" [Python 3 Builtins: Filter]
sList = list(s)
intsList = list(string.digits)
obj = filter(lambda x: operator.contains(intsList, x), sList)))
In Python 3 this returns
>> <filter object # hex>
To get a printed string,
nums = "".join(list(obj))
print(nums)
>> "1212"
I am not sure how filter ranks in terms of efficiency but it is a good thing to know how to use when doing list comprehensions and such.
UPDATE
Logically, since filter works you could also use list comprehension and from what I have read it is supposed to be more efficient because lambdas are the wall street hedge fund managers of the programming function world. Another plus is that it is a one-liner that doesnt require any imports. For example, using the same string 's' defined above,
num = "".join([i for i in s if i.isdigit()])
That's it. The return will be a string of all the characters that are digits in the original string.
If you have a specific list of acceptable/unacceptable characters you need only adjust the 'if' part of the list comprehension.
target_chars = "".join([i for i in s if i in some_list])
or alternatively,
target_chars = "".join([i for i in s if i not in some_list])
Using filter, you'd just need one line
line = filter(lambda char: char not in " ?.!/;:", line)
This treats the string as an iterable and checks every character if the lambda returns True:
>>> help(filter)
Help on built-in function filter in module __builtin__:
filter(...)
filter(function or None, sequence) -> list, tuple, or string
Return those items of sequence for which function(item) is true. If
function is None, return the items that are true. If sequence is a tuple
or string, return the same type, else return a list.
Try this one:
def rm_char(original_str, need2rm):
''' Remove charecters in "need2rm" from "original_str" '''
return original_str.translate(str.maketrans('','',need2rm))
This method works well in Python 3
Here's some possible ways to achieve this task:
def attempt1(string):
return "".join([v for v in string if v not in ("a", "e", "i", "o", "u")])
def attempt2(string):
for v in ("a", "e", "i", "o", "u"):
string = string.replace(v, "")
return string
def attempt3(string):
import re
for v in ("a", "e", "i", "o", "u"):
string = re.sub(v, "", string)
return string
def attempt4(string):
return string.replace("a", "").replace("e", "").replace("i", "").replace("o", "").replace("u", "")
for attempt in [attempt1, attempt2, attempt3, attempt4]:
print(attempt("murcielago"))
PS: Instead using " ?.!/;:" the examples use the vowels... and yeah, "murcielago" is the Spanish word to say bat... funny word as it contains all the vowels :)
PS2: If you're interested on performance you could measure these attempts with a simple code like:
import timeit
K = 1000000
for i in range(1,5):
t = timeit.Timer(
f"attempt{i}('murcielago')",
setup=f"from __main__ import attempt{i}"
).repeat(1, K)
print(f"attempt{i}",min(t))
In my box you'd get:
attempt1 2.2334518376057244
attempt2 1.8806643818474513
attempt3 7.214925774955572
attempt4 1.7271184513757465
So it seems attempt4 is the fastest one for this particular input.
Here's my Python 2/3 compatible version. Since the translate api has changed.
def remove(str_, chars):
"""Removes each char in `chars` from `str_`.
Args:
str_: String to remove characters from
chars: String of to-be removed characters
Returns:
A copy of str_ with `chars` removed
Example:
remove("What?!?: darn;", " ?.!:;") => 'Whatdarn'
"""
try:
# Python2.x
return str_.translate(None, chars)
except TypeError:
# Python 3.x
table = {ord(char): None for char in chars}
return str_.translate(table)
#!/usr/bin/python
import re
strs = "how^ much for{} the maple syrup? $20.99? That's[] ricidulous!!!"
print strs
nstr = re.sub(r'[?|$|.|!|a|b]',r' ',strs)#i have taken special character to remove but any #character can be added here
print nstr
nestr = re.sub(r'[^a-zA-Z0-9 ]',r'',nstr)#for removing special character
print nestr
You can also use a function in order to substitute different kind of regular expression or other pattern with the use of a list. With that, you can mixed regular expression, character class, and really basic text pattern. It's really useful when you need to substitute a lot of elements like HTML ones.
*NB: works with Python 3.x
import re # Regular expression library
def string_cleanup(x, notwanted):
for item in notwanted:
x = re.sub(item, '', x)
return x
line = "<title>My example: <strong>A text %very% $clean!!</strong></title>"
print("Uncleaned: ", line)
# Get rid of html elements
html_elements = ["<title>", "</title>", "<strong>", "</strong>"]
line = string_cleanup(line, html_elements)
print("1st clean: ", line)
# Get rid of special characters
special_chars = ["[!##$]", "%"]
line = string_cleanup(line, special_chars)
print("2nd clean: ", line)
In the function string_cleanup, it takes your string x and your list notwanted as arguments. For each item in that list of elements or pattern, if a substitute is needed it will be done.
The output:
Uncleaned: <title>My example: <strong>A text %very% $clean!!</strong></title>
1st clean: My example: A text %very% $clean!!
2nd clean: My example: A text very clean
My method I'd use probably wouldn't work as efficiently, but it is massively simple. I can remove multiple characters at different positions all at once, using slicing and formatting.
Here's an example:
words = "things"
removed = "%s%s" % (words[:3], words[-1:])
This will result in 'removed' holding the word 'this'.
Formatting can be very helpful for printing variables midway through a print string. It can insert any data type using a % followed by the variable's data type; all data types can use %s, and floats (aka decimals) and integers can use %d.
Slicing can be used for intricate control over strings. When I put words[:3], it allows me to select all the characters in the string from the beginning (the colon is before the number, this will mean 'from the beginning to') to the 4th character (it includes the 4th character). The reason 3 equals till the 4th position is because Python starts at 0. Then, when I put word[-1:], it means the 2nd last character to the end (the colon is behind the number). Putting -1 will make Python count from the last character, rather than the first. Again, Python will start at 0. So, word[-1:] basically means 'from the second last character to the end of the string.
So, by cutting off the characters before the character I want to remove and the characters after and sandwiching them together, I can remove the unwanted character. Think of it like a sausage. In the middle it's dirty, so I want to get rid of it. I simply cut off the two ends I want then put them together without the unwanted part in the middle.
If I want to remove multiple consecutive characters, I simply shift the numbers around in the [] (slicing part). Or if I want to remove multiple characters from different positions, I can simply sandwich together multiple slices at once.
Examples:
words = "control"
removed = "%s%s" % (words[:2], words[-2:])
removed equals 'cool'.
words = "impacts"
removed = "%s%s%s" % (words[1], words[3:5], words[-1])
removed equals 'macs'.
In this case, [3:5] means character at position 3 through character at position 5 (excluding the character at the final position).
Remember, Python starts counting at 0, so you will need to as well.
In Python 3.5
e.g.,
os.rename(file_name, file_name.translate({ord(c): None for c in '0123456789'}))
To remove all the number from the string
How about this:
def text_cleanup(text):
new = ""
for i in text:
if i not in " ?.!/;:":
new += i
return new
Below one.. with out using regular expression concept..
ipstring ="text with symbols!##$^&*( ends here"
opstring=''
for i in ipstring:
if i.isalnum()==1 or i==' ':
opstring+=i
pass
print opstring
Recursive split:
s=string ; chars=chars to remove
def strip(s,chars):
if len(s)==1:
return "" if s in chars else s
return strip(s[0:int(len(s)/2)],chars) + strip(s[int(len(s)/2):len(s)],chars)
example:
print(strip("Hello!","lo")) #He!
You could use the re module's regular expression replacement. Using the ^ expression allows you to pick exactly what you want from your string.
import re
text = "This is absurd!"
text = re.sub("[^a-zA-Z]","",text) # Keeps only Alphabets
print(text)
Output to this would be "Thisisabsurd". Only things specified after the ^ symbol will appear.
# for each file on a directory, rename filename
file_list = os.listdir (r"D:\Dev\Python")
for file_name in file_list:
os.rename(file_name, re.sub(r'\d+','',file_name))
Even the below approach works
line = "a,b,c,d,e"
alpha = list(line)
while ',' in alpha:
alpha.remove(',')
finalString = ''.join(alpha)
print(finalString)
output: abcde
The string method replace does not modify the original string. It leaves the original alone and returns a modified copy.
What you want is something like: line = line.replace(char,'')
def replace_all(line, )for char in line:
if char in " ?.!/;:":
line = line.replace(char,'')
return line
However, creating a new string each and every time that a character is removed is very inefficient. I recommend the following instead:
def replace_all(line, baddies, *):
"""
The following is documentation on how to use the class,
without reference to the implementation details:
For implementation notes, please see comments begining with `#`
in the source file.
[*crickets chirp*]
"""
is_bad = lambda ch, baddies=baddies: return ch in baddies
filter_baddies = lambda ch, *, is_bad=is_bad: "" if is_bad(ch) else ch
mahp = replace_all.map(filter_baddies, line)
return replace_all.join('', join(mahp))
# -------------------------------------------------
# WHY `baddies=baddies`?!?
# `is_bad=is_bad`
# -------------------------------------------------
# Default arguments to a lambda function are evaluated
# at the same time as when a lambda function is
# **defined**.
#
# global variables of a lambda function
# are evaluated when the lambda function is
# **called**
#
# The following prints "as yellow as snow"
#
# fleece_color = "white"
# little_lamb = lambda end: return "as " + fleece_color + end
#
# # sometime later...
#
# fleece_color = "yellow"
# print(little_lamb(" as snow"))
# --------------------------------------------------
replace_all.map = map
replace_all.join = str.join
If you want your string to be just allowed characters by using ASCII codes, you can use this piece of code:
for char in s:
if ord(char) < 96 or ord(char) > 123:
s = s.replace(char, "")
It will remove all the characters beyond a....z even upper cases.
I'm trying to traverse through a string using its indices and remove specific elements. Due to string length getting shorter as elements are removed, it always goes out of range by the time the final element is reached.
Here's some code to ilustrate what I'm trying to do. For example, going from "1.2.3.4" to "1234".
string = "1.2.3.4"
for i in range(len(string)):
if string[i] == ".":
string = string[:i] + string[i+1:]
I know there are alternate approaches like using string method called replace() and I can run string = string.replace(string[i], "", 1) OR I can traverse through individual elements (not indicies).
But how would I solve it using the approach above (traversing string indices)? What techniques can I use to halt the loop after it reaches the final element of the string? Without continuing to advance the index, which will go out of range as elements are removed earlier in the string.
Use this:
string = "1.2.3.4"
res = ""
for s in string:
if s != '.':
res += s
The result is of course '1234'.
you can use the re module:
import re
string = "1.2.3.4"
string = re.sub('\.','',string)
print(string)
If I understand correctly, you want to modify a string by its index while the length of it keep changing.
That's pretty dangerous.
The problem you ran into is caused by range(len(string)).See, once the range is fixed, it won't change.And in the loop, string changes, it gets shorter, and that's why you got out of range error.
So what you want to do is to track the string while looping, and use if-else to find the '.'s, here is an example:
string = '1.2.3.4'
i = 0
while i < len(string):
if string[i] == '.':
string = string[:i] + string[i+1:]
else:
i += 1
Still, there are plenty of ways to deal with your string, don't use this, this is not good.
it could be done like this (with a try/except block), but that's not really a great way to approach this problem (or any problem)
string = "1.2.3.4"
for i in range(len(string)):
try:
if string[i] == ".":
string= string[:i]+string[i+1:]
except:
IndexError
result is 1234
The only real change of course is that by adding a try/except around our loop, we save ourselves from the IndexError that would normally come up once we try to access an element in the string that is now out of bounds
Once that happens, the Exception is caught and we simply exit the loop with our finished string
I am trying to write some code that is given a string and reverses it, while capitalizing the first letter of the reversed string. for some, reason i get this message when running
Code:
Error:
In your capitalize() function you are calling upper() method without parenthesis. It returning the function object instead of string that's causing the problem.
Change this to
stringA[i] = stringA[i].upper
this
stringA[i] = stringA[i].upper()
Simple solution to your question using str.capitalize()
res = string[::-1].capitalize()
There's just one simple problem in your code. you forgot to put the () after the upper method.
here's how you can write the code:
def capitalize(stringA):
notAplha = [" ",".",",","/","?","1","2","3","4","5","6","7","8","9","0"]
for i in range(1):
if stringA[i] in notAplha:
continue
else:
stringA[i]=stringA[i].upper()
return stringA
def reverseit(string):
string = list(string)
string.reverse()
string = capitalize(string)
string = "".join(string)
return string
print(reverseit(str(input())))
there seems to be an issue with you code. it does't work properly if i insert a number at the end for input
So, try this capitalize(stringA[i+1:-1]) in the if condition. It'll look like this:
def capitalize(stringA):
notAplha = [" ",".",",","/","?","1","2","3","4","5","6","7","8","9","0"]
for i in range(len(stringA)):
if stringA[i] in notAplha:
capitalize(stringA[i+1:-1])
continue
else:
stringA[i]=stringA[i].upper()
return stringA
def reverseit(string):
string = list(string)
string.reverse()
string = capitalize(string)
string = "".join(string)
return string
print(reverseit("dsaddsa00000"))
output will be:
00000Asddasd
Here's the problem I'm trying to wrap my head around:
We can use the idea of bisection search to determine if a character
is in a string, so long as the string is sorted in alphabetical order.
First, test the middle character of a string against the character
you're looking for (the "test character"). If they are the same, we
are done - we've found the character we're looking for!
If they're not the same, check if the test character is "smaller" than
the middle character. If so, we need only consider the lower half of
the string; otherwise, we only consider the upper half of the string.
(Note that you can compare characters using Python's < function.)
Implement the function isIn(char, aStr) which implements the above
idea recursively to test if char is in aStr. char will be a single
character and aStr will be a string that is in alphabetical order. The
function should return a boolean value.
As you design the function, think very carefully about what the base
cases should be.
Here's the code I tried to do. I'm getting errors, but I'm falling behind in understanding the basics of how to do this problem.
def isIn(char, aStr):
'''
char: a single character
aStr: an alphabetized string
returns: True if char is in aStr; False otherwise
'''
# Your code here
middle_char = len(aStr)/2
if char == middle_char:
True
elif char == "" or char == 1:
False
elif char < aStr[:middle_char]:
return isIn(char,aStr(middle_char)
else:
return isIn(char, aStr(middle_char))
One reason you're falling behind is that you're trying to write a recursive function when you haven't yet mastered writing simple statements. You have about 10 lines of active code here, including at least four syntax errors and two semantic errors.
Back off and use incremental programming. Write a few lines of code, test them, and don't advance until you're sure they work as expected. Insert diagnostic print statements to check values as you go. For instance, start with force-fed values and no actual function call, like this:
# def isIn(char, aStr):
'''
char: a single character
aStr: an alphabetized string
returns: True if char is in aStr; False otherwise
'''
char = 'q'
aStr = "abcdefghijklmnopqrstuvwxyz"
print "parameters:", char, aStr
middle_char = len(aStr)/2
print len(aStr), middle_char
print "if", char, "==", middle_char, ":"
This gives you the output
parameters: q abcdefghijklmnopqrstuvwxyz
26 13
if q == 13 :
Obviously, a character is not going to equal the integer 13.
Fix this before you go any further. Then you can try actually writing your first if statement.
See how that works?
middle_char = len(aStr)/2
if char == middle_char:
Middle char is half the length (I.e. an integer value)
It's not going to be equal to your char value.
middle_index = len(aStr)//2
middle_char = aStr[middle_index]
to actually get the middle char value. Note the integer division (//). we want to make sure that the resulting index is a whole number.
elif char == "" or char == 1:
you've already tested (well tried to) the case where there is one char left, you dont need to handle that specifically. You also need to test for empty string before you try extracting values.
elif char < aStr[:middle_char]:
here you actually do try and index into the string. unfortunately, what you are actually doing is slicing it, and seeing if the secind hald of the string (middle character onwards) is equal to your char. this will only ever match if you are looking at a one character string. e.g. isin('d', 'd')
return isIn(char,aStr(middle_char)
else:
return isIn(char, aStr(middle_char))
- Missing parenthesis on the first return )
- aStr() is not a function. you need [ and ]
- you are trying to pass just a single char into the recursive call. you need to slice the string and pass the resulting sub-string into the recursive string
- both of these (ignoring the missing bracket) are identical calls. you need one to call with the first half of aStr and one with the second half.
Your task says to think about the base cases. They are (I'm listing them because you almost got them spot on):
- empty string (return False)
- mid char = search char (return True)
- mid char > search char (search left substring)
- mid char < search char (search right substring)
note that there is no need to explicitly check for a non matching string with a length of 1, as that will pass an empty string into the next call
something for you to think about: why does the string need to be sorted? what happens if the string isnt sorted?
a working implementation:
def isin (char, str):
if not str:
return False
mid_index = len(str)/2
mid_char = str[mid_index]
return True if mid_char == char else isin(char, str[:mid_index] if mid_char > char else str[mid_index+1:])
DO NOT just use this code. This code is just for your reference so you can understand what it is doing and rewrite you code once you understand. There is no point in just copying the code if you dont understand it. It wont help you in the future.
You do seem to have the general idea of what you need to do (I'm guessing you have gone over this in class), but are lacking knowlege in the how (syntax etc).
I recommend going through the python tutorial in your own time, doing the exercises it takes you through. It will introduce you to the features of the language in turn and this will really help you.
good luck!
This question already has answers here:
How can I invert (swap) the case of each letter in a string?
(8 answers)
Closed 7 years ago.
I'm rank new in Python, thus the question,
I'm trying to solve a simple problem, where the program takes in a simple string and swaps all the cases. Thus if we enter
SimPLE
We should get
sIMple
This is my code
def main():
oldStr = input()
for s in oldStr:
if s.islower():
s.upper()
elif s.isupper():
s.lower()
print(oldStr)
if __name__ == "__main__" : main()
It just returns the same string. Any help appreciated.
As a generator expression:
mystr = "SimPLE"
print("".join(c.upper() if c.islower() else c.lower() for c in mystr))
The breakdown of the above is:
c.upper() if c.islower() else c.lower()
is an conditional expression that will convert a character from upper to lower case and vice versa.
Then,
(... for c in mystr)
is a generator expression, which is somewhat like a list that is generated on-the-fly.
Finally:
".join(...)
will join any sequence of strings together with nothing ("") between them.
Do this in one fell swoop with a string join on a list comprehension of individual characters:
outstr = ''.join([s.upper() if s.islower() else s.lower() for s in oldStr])
print(outstr)
Input & Output:
sIMple
SimPLE
Strings are immutable. What this means is that when you use the function s.upper(), it is not setting that letter in str to be uppercase, it simply returns that letter in uppercase.
Here is some code that works:
def main():
oldStr = input()
newStr = ""
for s in oldStr:
if s.islower():
newStr+=s.upper()
elif s.isupper():
newStr+=s.lower()
print(newStr)
Notice now that we are creating a new string and simply adding the letters at each point in the forloop as opposed to changing those letters in str.
You are running each character through lower() and upper(), but these functions do not change the character.
Instead, they return the modified version of the character. The original character s will stay as it is.
You should build a new string based off the return values of lower() and upper(), and return that string.
1) you need to put the main() call on new line, as python relies on whitespace heavily for program structure
2) s is a temporary variable created for the purpose of the for statement. It doesn't actually reference the character in the string
Essentially what is going on is that s has the same value as the character in the string, but it IS NOT ACTUALLY the character in the string.