132

I'm really confused. I tried to encode but the error said can't decode....

>>> "你好".encode("utf8")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

I know how to avoid the error with "u" prefix on the string. I'm just wondering why the error is "can't decode" when encode was called. What is Python doing under the hood?


See also: unicode().decode('utf-8', 'ignore') raising UnicodeEncodeError , the other way around.

1
  • 1
    Note that this issue is fixed in 3.x: strings are simply Unicode and bytes objects are simply raw bytes, and never the twain shall meet. Strings don't do any implicit decoding when .encode is called; in fact, .encode is not supported at all, as it makes no sense. The weird behaviour was only present in 2.x as a compatibility hack in the first place, because of the way that Unicode was introduced into the language. Commented Jan 4, 2023 at 6:46

7 Answers 7

171
"你好".encode('utf-8')

encode converts a unicode object to a string object. But here you have invoked it on a string object (because you don't have the u). So python has to convert the string to a unicode object first. So it does the equivalent of

"你好".decode().encode('utf-8')

But the decode fails because the string isn't valid ascii. That's why you get a complaint about not being able to decode.

Sign up to request clarification or add additional context in comments.

7 Comments

So what is the solution? Especially if I don't have a string literal, I just have a string object.
@JonTirsen, you should not be encoding a string object. A string object is already encoded. If you need to change the encoding, you need to decode it into a unicode string and then encode it as the desired encoding.
So to state it clearly from above you can "你好".decode('utf-8').encode('utf-8')
@WinstonEwert I guess I was confused. The encoding business tend to leave me eternally confused. I guess my confusion came from my own problem of not knowing the if the input is a string or unicode string and what encoding it may have.
@deinonychusaur, yeah... I get that.
|
56
+250

Always encode from Unicode to bytes. In this direction, you choose the encoding.

>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print _
你好

The other way is to decode from bytes to Unicode.
In this direction, you have to know what the encoding is.

>>> bytes = '\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print bytes
你好
>>> bytes.decode("utf-8")
u'\u4f60\u597d'
>>> print _
你好

This point can't be stressed enough. If you want to avoid playing Unicode "whack-a-mole", it's important to understand what's happening at the data level. Here it is explained another way:

  • A unicode object (type unicode) is decoded already; you never want to call decode on it.
  • A bytestring object (type str) is encoded already; you never want to call encode on it.

When .encode is called on a bytestring, Python 2 first tries to implicitly convert it to text (a unicode object). Similarly, on seeing .decode on a Unicode string, Python 2 implicitly tries to convert it to bytes (a str object).

These implicit conversions are why you can get UnicodeDecodeError when you've called encode. Encoding usually accepts an object of type unicode; when called on a str object, there's an implicit decoding into an object of type unicode before re-encoding. The implicit decoding chooses a default 'ascii' codec, resulting in a decoding error from an encoding call.

In Python 3, the methods str.decode and bytes.encode were removed, as part of the changes to define separate, unambiguous types for text and raw "bytes" data.

...or whatever coding sys.getdefaultencoding() mentions; usually this is 'ascii'

4 Comments

So do you mean that Python decodes the bytestring before encoding?
@thoslin exactly, I added more details.
What is _, and why are your print statements missing parenthesis?
@NoBugs 1. in the REPL, _ refers to the previous value 2. because this is a python-2.x question.
40

You can try this

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

Or

You can also try following

Add following line at top of your .py file.

# -*- coding: utf-8 -*- 

2 Comments

This must be accepted answer!
Why? It's completely incorrect. It explains nothing, and does not solve the described problem, but instead gives two ideas for things to try, both of which are for different kinds of Unicode-related problem, and which don't correspond to each other either.
7

If you're using Python < 3, you'll need to tell the interpreter that your string literal is Unicode by prefixing it with a u:

Python 2.7.2 (default, Jan 14 2012, 23:14:09) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> "你好".encode("utf8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'

Further reading: Unicode HOWTO.

2 Comments

If you're encoding a string, why does it throw a decode error?
@MxLDevs because you can't get a decode error on an encode action.
4

You use u"你好".encode('utf8') to encode an unicode string. But if you want to represent "你好", you should decode it. Just like:

"你好".decode("utf8")

You will get what you want. Maybe you should learn more about encode & decode.

Comments

3

In case you're dealing with Unicode, sometimes instead of encode('utf-8'), you can also try to ignore the special characters, e.g.

"你好".encode('ascii','ignore')

or as something.decode('unicode_escape').encode('ascii','ignore') as suggested here.

Not particularly useful in this example, but can work better in other scenarios when it's not possible to convert some special characters.

Alternatively you can consider replacing particular character using replace().

1 Comment

This is wrong. The reported problem in Python 2.x is a decode error which happens in code that runs implicitly, before the encode takes effect. Specifying 'ignore' in the code has no effect on that implicit code. The underlying problem is that, in Python 2.x, "你好" does not contain those characters in the first place; Python 2.x's str type only contains bytes, and pretends to contain characters by attempting implicit conversions on the fly. The unicode_escape proposal here would return an empty string.
1

If you are starting the python interpreter from a shell on Linux or similar systems (BSD, not sure about Mac), you should also check the default encoding for the shell.

Call locale charmap from the shell (not the python interpreter) and you should see

[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

If this is not the case, and you see something else, e.g.

[user@host dir] $ locale charmap
ANSI_X3.4-1968
[user@host dir] $ 

Python will (at least in some cases such as in mine) inherit the shell's encoding and will not be able to print (some? all?) unicode characters. Python's own default encoding that you see and control via sys.getdefaultencoding() and sys.setdefaultencoding() is in this case ignored.

If you find that you have this problem, you can fix that by

[user@host dir] $ export LC_CTYPE="en_EN.UTF-8"
[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

(Or alternatively choose whichever keymap you want instead of en_EN.) You can also edit /etc/locale.conf (or whichever file governs the locale definition in your system) to correct this.

1 Comment

This has nothing to do with the described problem, which can be reproduced in Python 2.x without a terminal or even a shell (for example, by writing to a file instead of using print). And it is the terminal which has to deal with the encoding, not the shell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.