tags:

views:

215

answers:

5

I need to change some characters that are not ASCII to '_'. For example,

Tannh‰user -> Tannh_user
  • If I use regular expression with Python, how can I do this?
  • Is there better way to do this not using RE?
+4  A: 

How to do it using built-in str.decode method:

>>> 'Tannh‰user'.decode('ascii', 'replace').replace(u'\ufffd', '_')
u'Tannh___user'

(You get unicode string, so convert it to str if you need.)

You can also convert unicode to str, so one non-ASCII character is replaced by ASCII one. But the problem is that unicode.encode with replace translates non-ASCII characters into '?', so you don't know if the question mark was there already before; see solution from Ignacio Vazquez-Abrams.

Another way, using ord() and comparing value of each character if it fits in ASCII range (0-127) - this works for unicode strings and for str in utf-8, latin and some other encodings:

>>> s = u'Tannh‰user'
>>> "".join((c if ord(c) < 128 else '_' for c in s))
u'Tannh_user'
Messa
it is not percent % but ‰ . Not sure it is still ascii. Is it?
joaquin
It's U+2030 character, per mille sign (not in ASCII).
interjay
Oh, sorry; I have edited my response.
Messa
+1  A: 

if you know which characters you want to replace, you can apply string methods

mystring.replace('oldchar', 'newchar')
joaquin
-1: first, it should be `ord(item)>127`. Then, think what your code does for this string: `'\xa0'*1000`.
ΤΖΩΤΖΙΟΥ
@ΤΖΩΤΖΙΟΥ The downvoted part has been eliminated. Note that although you are right and it could not manage the '\xa0' type representation, it worked perfectly with any one-char printable symbols that I understood was the type of strings the OP was fighting with. Also note that another post suggested the very same approach.
joaquin
I removed my downvote, but I can't find any other answer that: for *every applicable* character in the input string, replace the *whole input string*. In my `'\xa0'*1000` example, your code performed the whole `mystring` substitution a thousand times, 999 times of which unnecessarily. If you disagree with this, then given `astr='hello'; c=0`, what is the value of c after this loop: `for char in astr: c+= 1; astr=''`? I say it would be 5, you might think it would be 1.
ΤΖΩΤΖΙΟΥ
Brian's answer proposes the same approach: to check the value of ord() for each char in the string and replacing it if it is beyond 127. I understand your point now. After your first comment I realized that my code was not working with mystring = '\xa0' because it was checking '\', then 'x'... So I though this was your point. Now I understantd you refered to another thing I missed: the string in the for loop is unaltered during the search process so that repeated characters are checked even after they have been already replaced in the variable inside the loop. Not very efficient. Thanks!
joaquin
+1  A: 
re.sub(r'[^\x00-\x7F]', '_', theString)

This will work if theString is unicode, or a string in an encoding where ASCII occupies values 0 to 0x7F (latin-1, UTF-8, etc.).

interjay
+2  A: 

I'd rather just call ord on every character in the string, 1 by 1. If ord([char]) >= 128 the character is not an ascii character and should be replaced.

Brian
+2  A: 

Using Python's support for character encodings:

# coding: utf8
import codecs

def underscorereplace_errors(exc):
  return (u'_', exc.end)

codecs.register_error('underscorereplace', underscorereplace_errors)

print u'Tannh‰user'.encode('ascii', 'underscorereplace')
Ignacio Vazquez-Abrams