Backporting Python 3 ouvert (encoding = "utf-8") vers Python 2

152

J'ai une base de code Python, construite pour Python 3, qui utilise le style Python 3 open () avec le paramètre d'encodage:

https://github.com/miohtama/vvv/blob/master/vvv/textlineplugin.py#L47

    with open(fname, "rt", encoding="utf-8") as f:

Maintenant, j'aimerais rétroporter ce code vers Python 2.x, afin d'avoir une base de code qui fonctionne avec Python 2 et Python 3.

Quelle est la stratégie recommandée pour contourner les open()différences et l'absence de paramètre d'encodage?

Puis-je avoir un open()gestionnaire de fichiers de style Python 3 qui diffuse des chaînes d'octets, pour qu'il agisse comme Python 2 open()?

Mikko Ohtamaa
la source

Réponses:

177

1. Pour obtenir un paramètre d'encodage dans Python 2:

Si vous avez uniquement besoin de prendre en charge Python 2.6 et 2.7, vous pouvez utiliser à la io.openplace de open. ioest le nouveau sous-système io pour Python 3, et il existe également en Python 2,6 et 2.7. Veuillez noter que dans Python 2.6 (ainsi que 3.0), il est implémenté purement en python et très lent, donc si vous avez besoin de vitesse pour lire des fichiers, ce n'est pas une bonne option.

Si vous avez besoin de vitesse et que vous devez prendre en charge Python 2.6 ou une version antérieure, vous pouvez utiliser à la codecs.openplace. Il a également un paramètre d'encodage et est assez similaire à, io.opensauf qu'il gère différemment les fins de ligne.

2. Pour obtenir un open()gestionnaire de fichiers de style Python 3 qui diffuse des chaînes d'octets:

open(filename, 'rb')

Notez le «b», qui signifie «binaire».

Lennart Regebro
la source
11
Le «b» signifie en fait le mode binaire, pas les octets. Voir docs.python.org/3/library/functions.html#open .
pmdarrow
7
@pmdarrow Même chose dans ce cas, mais à proprement parler, oui.
Lennart Regebro
J'ai rencontré le problème que vous ne pouvez pas exécuter regex sur un flux d'octets pour l'option 2;)
Jonathan Komar
3
@ macmadness86 Vous devez utiliser une expression d'expression régulière d'octet.
Lennart Regebro
4
Une note du howto de portage: "Ne vous embêtez pas avec la pratique obsolète d'utiliser codecs.open () car c'est uniquement nécessaire pour maintenir la compatibilité avec Python 2.5." docs.python.org/3/howto/pyporting.html
Al Sweigart
65

je pense

from io import open

devrait faire.

mfussenegger
la source
7
Je pense que la réponse de Lennart ci-dessous est bien meilleure car elle fournit plus d'explications et la mise en garde sur la lenteur du module io dans 2.x ainsi que la suggestion d'utiliser codecs.open.
gps le
2
Que se passe-t-il si j'utilise from io import openPython 3? Je n'aime pas les performances actuellement.
samedi
8
@matth En python3 open from io est un alias pour l'open intégré. Voir docs.python.org/3/library/io.html?highlight=io#io.open
mfussenegger
21

Voici une façon:

with open("filename.txt", "rb") as f:
    contents = f.read().decode("UTF-8")
Flimm
la source
4
cela ne fonctionne évidemment pas si vous aviez des plans différents pourf
user5359531
8

Cela peut faire l'affaire:

import sys
if sys.version_info[0] > 2:
    # py3k
    pass
else:
    # py2
    import codecs
    import warnings
    def open(file, mode='r', buffering=-1, encoding=None,
             errors=None, newline=None, closefd=True, opener=None):
        if newline is not None:
            warnings.warn('newline is not supported in py2')
        if not closefd:
            warnings.warn('closefd is not supported in py2')
        if opener is not None:
            warnings.warn('opener is not supported in py2')
        return codecs.open(filename=file, mode=mode, encoding=encoding,
                    errors=errors, buffering=buffering)

Ensuite, vous pouvez garder votre code de la manière python3.

Notez que certaines API aiment newline, closefd, openerne fonctionnent pas

user2395922
la source
1
vous pouvez inverser la condition pour éviter cela pass.
bfontaine
2

Si vous utilisez six, vous pouvez essayer ceci, en utilisant la dernière API Python 3 et en exécutant à la fois Python 2/3:

import six

if six.PY2:
    # FileNotFoundError is only available since Python 3.3
    FileNotFoundError = IOError
    from io import open

fname = 'index.rst'
try:
    with open(fname, "rt", encoding="utf-8") as f:
        pass
        # do_something_with_f ...
except FileNotFoundError:
    print('Oops.')

Et, l'abandon de la prise en charge de Python 2 consiste simplement à supprimer tout ce qui est lié à six.

YaOzI
la source