129

There are two ways to open a text file in Python:

f = open(filename)

And

import codecs
f = codecs.open(filename, encoding="utf-8")

When is codecs.open preferable to open?

5
  • 58
    Note that codecs.open() is obsolete in 3.x, since open() gains an encoding argument. Commented Mar 9, 2011 at 19:05
  • 1
    There's also a 3rd way (in Python 2.x at least): `f = file(filename)' Commented Nov 1, 2012 at 15:53
  • 1
    @IgnacioVazquez-Abrams Is there any link that codecs.open() is obsolete? I don't think this in python3 docs: docs.python.org/3.7/library/codecs.html Commented Apr 17, 2019 at 12:25
  • 1
    @varela: the Python documentation page you mentioned says: "the builtin open() and the associated io module are the recommended approach for working with encoded text files" Commented May 10, 2019 at 2:10
  • What about import io; io.open(...) ? Commented Jan 10, 2023 at 13:47

7 Answers 7

95

Since Python 2.6, a good practice is to use io.open(), which also takes an encoding argument, like the now obsolete codecs.open(). In Python 3, io.open is an alias for the open() built-in. So io.open() works in Python 2.6 and all later versions, including Python 3.4. See docs: http://docs.python.org/3.4/library/io.html

Now, for the original question: when reading text (including "plain text", HTML, XML and JSON) in Python 2 you should always use io.open() with an explicit encoding, or open() with an explicit encoding in Python 3. Doing so means you get correctly decoded Unicode, or get an error right off the bat, making it much easier to debug.

Pure ASCII "plain text" is a myth from the distant past. Proper English text uses curly quotes, em-dashes, bullets, € (euro signs) and even diaeresis (¨). Don't be naïve! (And let's not forget the Façade design pattern!)

Because pure ASCII is not a real option, open() without an explicit encoding is only useful to read binary files.

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

4 Comments

@ForeverWintr The answer is pretty clearly in there: use io.open() for text, and open() only for binary. The implication is that codecs.open() is not preferred at all.
@Bdoserror, There is an answer in there, clearly, but it's not an answer to the question that was asked. The question was about the difference between open and codecs.open, and specifically when the latter is preferable to the former. An answer that doesn't so much as mention codecs.open can't answer that question.
@ForeverWintr If the the OP asked the wrong question (i.e. with the assumption that codecs.open() was correct to use) then there is no "correct" answer about when to use it. The answer is to use io.open() instead. It's like if I ask "when should I use a wrench to drive a nail into a wall?". The right answer is "use a hammer".
sometimes there is an unspoken question. In this one it is, "I don't know what codecs.open is for if open seems to do the same thing! What is it?" -- many thanks for explaining both!
23

Personally, I always use codecs.open unless there's a clear identified need to use open**. The reason is that there's been so many times when I've been bitten by having utf-8 input sneak into my programs. "Oh, I just know it'll always be ascii" tends to be an assumption that gets broken often.

Assuming 'utf-8' as the default encoding tends to be a safer default choice in my experience, since ASCII can be treated as UTF-8, but the converse is not true. And in those cases when I truly do know that the input is ASCII, then I still do codecs.open as I'm a firm believer in "explicit is better than implicit".

** - in Python 2.x, as the comment on the question states in Python 3 open replaces codecs.open

3 Comments

what I don't really get is why open sometimes can handle very well the UTF-8 encoded non-latin characters of the unicode set, and sometimes it fails miserabily ...
This makes sense to me. io.open does not take an encoding param from what I can see in python 2.7.5
@radtek, you are right that this is undocumented; however (at least in 2.7.12) io.open accepts encoding and newline parameters and interprets them as Python 3 does. Unlike codecs.open, a file opened with io.open will raise TypeError: write() argument 1 must be unicode, not str even in Python 2.7 if you attempt to write str (bytes) to it. A file opened with codecs.open will instead attempt implicit conversion to unicode, often leading to confusing UnicodeDecodeErrors.
12

In Python 2 there are unicode strings and bytestrings. If you just use bytestrings, you can read/write to a file opened with open() just fine. After all, the strings are just bytes.

The problem comes when, say, you have a unicode string and you do the following:

>>> example = u'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

So here obviously you either explicitly encode your unicode string in utf-8 or you use codecs.open to do it for you transparently.

If you're only ever using bytestrings then no problems:

>>> example = 'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
>>>

It gets more involved than this because when you concatenate a unicode and bytestring string with the + operator you get a unicode string. Easy to get bitten by that one.

Also codecs.open doesn't like bytestrings with non-ASCII chars being passed in:

codecs.open('test', 'w', encoding='utf-8').write('Μου αρέσει')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/codecs.py", line 691, in write
    return self.writer.write(data)
  File "/usr/lib/python2.7/codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xce in position 0: ordinal not in range(128)

The advice about strings for input/ouput is normally "convert to unicode as early as possible and back to bytestrings as late as possible". Using codecs.open allows you to do the latter very easily.

Just be careful that you are giving it unicode strings and not bytestrings that may have non-ASCII characters.

2 Comments

Can you explain your second example? It appears to be identical to your first example, so why would the result be any different?
Note the use of the u'' in the first example. This means I created a unicode string, not a bytestring. This is the difference between the two examples. In the second example I am creating a bytestring and writing out one of those to a file is just fine. A unicode string is not fine if you're using characters outside of ASCII.
7

codecs.open, i suppose, is just a remnant from the Python 2 days when the built-in open had a much simpler interface and fewer capabilities. In Python 2, built-in open doesn't take an encoding argument, so if you want to use something other than binary mode or the default encoding, codecs.open was supposed to be used.

In Python 2.6, the io module came to the aid to make things a bit simpler. According to the official documentation

New in version 2.6.

The io module provides the Python interfaces to stream handling.
Under Python 2.x, this is proposed as an alternative to the
built-in file object, but in Python 3.x it is the default
interface to access files and streams.

Having said that, the only use i can think of codecs.open in the current scenario is for the backward compatibility. In all other scenarios (unless you are using Python < 2.6) it is preferable to use io.open. Also in Python 3.x io.open is the same as built-in open

Note:

There is a syntactical difference between codecs.open and io.open as well.

codecs.open:

open(filename, mode='rb', encoding=None, errors='strict', buffering=1)

io.open:

open(file, mode='r', buffering=-1, encoding=None,
     errors=None, newline=None, closefd=True, opener=None)

1 Comment

Not only codecs.open and io.open differ in terms of syntax, they return objects of different type. Also codecs.open always works with files in binary mode.
5

When you need to open a file that has a certain encoding, you would use the codecs module.

1 Comment

I guess all the text files have a certain encoding, somehow (:
5
  • When you want to load a binary file, use f = io.open(filename, 'b').

  • For opening a text file, always use f = io.open(filename, encoding='utf-8') with explicit encoding.

In python 3 however open does the same thing as io.open and can be used instead.

Note: codecs.open is planned to become deprecated and replaced by io.open after its introduction in python 2.6. I would only use it if code needs to be compatible with earlier python versions. For more information on codecs and unicode in python see the Unicode HOWTO.

2 Comments

1. Why can't I open a file in binary mode with io.open or codecs.open? 2. codecs.open is not deprecated yet, read the discussion on the page you linked to.
Good points! 1. You can use either, but I would again advice against codecs.open unless you're on python 2.5 or older. 2. I updated my answer to reflect that the deprecation did not take place immediately, but rather in the future.
2

When you're working with text files and want transparent encoding and decoding into Unicode objects.

Comments

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.