Je souhaite écrire un programme Python qui lit les fichiers contenant du texte Unicode. Ces fichiers sont normalement codés avec UTF-8, mais ne peuvent pas être; S'ils ne le sont pas, le codage alternatif sera explicitement déclaré au début du fichier. Plus précisément, il sera déclaré en utilisant exactement les mêmes règles que Python lui-même utilisées pour permettre à Python Source Code d'avoir un codage explicitement déclaré (comme dans Pep 0263, voir https://www.python.org/dev/peps/pep-0263/ pour plus de détails). Juste pour être clair, les fichiers en cours de traitement ne sont pas réellement de la source python, mais ils déclarent leurs codages (lorsqu'ils ne sont pas dans UTF-8) en utilisant les mêmes règles.
Si on connaît le codage d'un fichier avant que l'on ne l'ouvre avant l'ouverture Python offre un moyen très facile de lire le fichier avec décodage automatique: la commande et chaque ligne Une pensée est d'ouvrir Le fichier à l'aide de l'habituel Une autre pensée consiste à regarder dans les sources Python. Est-ce que quelqu'un sait où dans la source Python Le traitement du codage de code source est effectué? P> p> codecs.open code>; Par exemple, on pourrait faire: p> code> nous entrons dans la boucle sera une chaîne unicode. Existe-t-il une bibliothèque Python qui fait une chose similaire, mais choisissant le codage selon les règles ci-dessus (qui sont les règles de Python 3.0, je pense)? (par exemple, Python expose le «fichier de lecture avec codage auto-déclaré» qu'il utilise pour lire la source à la langue?) Sinon, quel est le moyen le plus simple d'atteindre l'effet souhaité? P> Ouvrir code>, lisez les deux premières lignes, interprétez-les sous UTF-8, recherchez une déclaration de codage à l'aide du REGEXP dans le PEP, et si l'on trouve un démarrage décodant toutes les lignes suivantes à l'aide de le codage déclaré. Pour que cela soit sûr de travailler, nous devons savoir que pour tous les codages que Python permet à Python Source, le python habituel readline code> divisera correctement le fichier en lignes - c'est-à-dire que nous devons savoir Pour que tous les codages Python permettent à Python Source, la chaîne d'octets '\ N' signifie toujours vraiment nouvelle ligne et ne fait pas partie de certaines séquences multi-octets codant pour un autre caractère. (En fait, j'ai aussi besoin de vous inquiéter de "\ r \ n" aussi.) Est-ce que quelqu'un sait-il si cela est vrai? Les documents n'étaient pas très précis. P>
5 Réponses :
à partir de ladite PEP (0268) : p>
Le combo Tokenizer / compilateur de Python sera Besoin d'être mis à jour pour fonctionner comme suit: p>
lisez le fichier p> li>
décode dans unicode en supposant un codage par fichier fixe p> li>
Convertissez-le en une chaîne d'octets UTF-8 P> li>
TOKENISEZ le contenu UTF-8 P> LI>
Compilez-le, créant des objets Unicode à partir des données Unicode données et créer des objets de chaîne à partir des données littérales Unicode en réenregistrant d'abord les données UTF-8 dans des données de chaîne de 8 bits Utilisation du fichier de fichier donné codant p> li> ol> blockQuote>
En effet, si vous vérifiez
parser / tokenizer.c code> dans la source Python, vous trouverez des fonctionsget_coding_spec code>etcheck_coding_spec code>qui sont responsables de la recherche de ces informations sur une ligne étant examinée dansdécodage_fgets code>. P>Il ne semble pas que cette capacité soit exposée n'importe où en tant qu'API Python (au moins ces fonctions spécifiques ne soit pas
py code> préfixée -, de sorte que vos options sont donc une bibliothèque tierce et / / ou réaffirmer ces fonctions comme une extension. Je ne connais personnellement aucune bibliothèque tierce partie - je ne peux pas voir cette fonctionnalité dans la bibliothèque standard non plus. P>
Merci pour le lien avec les sources. Je suis d'accord avec vous que cela ressemble à la fonctionnalité n'est exposé nulle part où j'avais peur. Je pense que je prévois d'analyser l'algorithme dans les sources Python, puis de le recoder en python ...
Vous devriez être capable de rouler votre propre décodeur en Python. Si vous ne supportez que les codages 8 bits qui sont des supersets d'ASCII, le code ci-dessous doit fonctionner comme.
Si vous avez besoin de support 2-byte Encodings comme UTF-16 Vous auriez besoin d'augmenter le modèle pour correspondre à puis écrivez le décodeur: p> sortie: < / p> \ x00c \ x00o .. code> ou l'inverse, en fonction de Le point d'ordre d'octet .
Tout d'abord, générez quelques fichiers de test qui annoncent leur codage: p>
Merci! C'est bien et simple et fonctionnera plutôt bien dans des exemples pratiques. C'est probablement la meilleure solution dans la plupart des cas! Pour mes propres exigences, la fidélité exacte à ce que Python est important, alors je finirai donc de faire quelque chose de plus élaboré, comme décrit dans ma réponse ci-dessous, que je comprends au cas où quelqu'un d'autre qui se soucie de la fidélité exacte trouve cette question sur Stackoverflow .
J'ai examiné les sources de Maintenant les règles de quoi faire quand ' J'ai trouvé une déclaration de codage forte>': p>
Pour ce que je fais, la fidélité au comportement de Python est importante. Mon plan est de faire valoir une mise en œuvre de l'algorithme ci-dessus en Python et d'utiliser ceci. Merci pour tous ceux qui ont répondu! P> tokenizer.c code> (grâce à @nineFingers pour le suggérer dans une autre réponse et donnant un lien vers le navigateur source). Il semble que l'algorithme exact utilisé par Python soit (équivalent à) ce qui suit. Dans divers endroits, je vais décrire l'algorithme en tant qu' octet de lecture par octet - - évidemment, on veut faire quelque chose de tamponné dans la pratique, mais il est plus facile de décrire de cette façon. La partie initiale du fichier est traitée comme suit: p>
' utf-8 ' code>, soit quelque chose en commençant par 'utf-8 -' code>.) li>
codecs code>. En particulier, la division du reste des octets dans le fichier dans les lignes est le travail du nouveau codage. Li>
'iso-latin-1' code> ou 'iso-8859-1' code> ou n'importe quelle chaîne commençant par l'une des < Code> 'Latin-1 -' code>, 'iso-latin-1 -' code> ou 'iso-8859-1 -' code>. li>.
ol>
Cela signifie que si vous voyez "\ r", vous devez être lu de manière spéculativement le caractère suivant, et si ce n'est pas un "\ n", placez-le i> pourquoi ne traitez-vous pas \ r code> comme un caractère EOL valide? Décode de cette chaîne à l'aide du codec UTF-8. Sauf si vous avez vu le nom de l'UTF-8, génère un message d'erreur si vous voyez des caractères non-ASCII (...) i> ne devriez-vous pas être inversé?
@PIODRDOBROGOST: Vous traitez \ r code> en tant que caractère EOL valide. Le point est que si le caractère suivant immédiatement le \ r code> est un \ n code>, alors que \ n code> doit être "mangé" dans le cadre du ligne se terminant par le \ r code> et pas i> traité comme faisant partie de la ligne suivante. Si le personnage suivant immédiatement le \ r code> est autre chose que \ n code>, il doit alors être laissé dans le flux pour être le premier caractère de la ligne suivante. C'est pourquoi vous avez besoin de la logique «remise» décrite.
@PIOLDOBROGROGOST: "Sauf si vous avez vu le nom de l'UTF-8, génère un message d'erreur si vous voyez des caractères non-ASCII". Je pense que cela est correct comme écrit. La règle est la suivante: Si vous (a) Voir un caractère non-ASCII et (B), vous n'avez pas vu de naissance UTF-8, vous devez générer une erreur. (Si vous voyez un personnage non ASCII, mais vous avez vu un nom d'erreur UTF-8, vous ne faites pas de message d'erreur; et évidemment si tous les caractères sont ASCII, vous ne faites pas de message d'erreur.) J'espère que cela clarifie les choses. S'il vous plaît demander plus de questions si je suis toujours pas clair et / ou si je ressemble vraiment à ce que je me trompe vraiment ici.
En ce qui concerne \ r code> - j'ai lu si ce n'est pas un "\ n", le remettez-le i> pour signifier mettre juste lire \ r code> retour pendant que cela ne s'applique pas à \ r code> mais le prochain caractère. Ma faute. Pour voir l'UTF-8 BOM ce que je veux dire, c'est la première chose à faire de vérifier s'il y a une chômée ou non et seulement s'il est présent, essayez de décoder à l'aide du codec UTF-8. Merci pour la clarification.
@PIOLDOBROGROGOST pour la chose de décodage UTF8, vous avez raison. Il est plus logique de penser à cela comme vous dites (erreur si vous voyez des octets ci-dessus \ x7f code> sauf si vous avez vu le bom, O'wise Decoder), mais d'autre part je pense Que ce que j'ai dit (premier décodage, puis Erreur si vous obtenez des erreurs de décodage ou si le résultat contient des caractères unicode ci-dessus \ u7f code>) est équivalent et était comme cela se produit comme je pensais à des choses. BTW, voudriez-vous voir mon code Python pour cela? (Ce n'est en fait pas 100% fidèle à l'algo ci-dessus en ce que mon code reconnaît les boms UTF16, contrairement à l'interprète de Python).
@ circulaire-ruine, avez-vous déjà fait une bibliothèque Python?
@Leorochael j'ai mis en œuvre ce qui précède comme module Python; Ce n'est pas publié nulle part mais je pourrais la mettre sur GitHub si cela vous sera utile --- Seriez-vous?
@Leorochael j'ai fait un gist: gist.github.com/anonymous/e7e8a33dbfd9CB645D74 , que je pense a le code pertinent dedans. J'ai bien peur que ce ne soit pas bien commenté. NB que le projet ceci a été utilisé pour finalement décider de faire une pause de Pep 0263: il sera i> reconnaître et honorer une marque d'ordre d'octets UTF-16, contrairement à Python. Donc, le code dans l'essentiel peut avoir besoin de modification si vous avez besoin de la fidélité exacte de Python.
À partir de Python 3.4 Il existe une fonction qui vous permet de faire ce que vous demandez - Selon Documentation : p>
Brett cannon Talks À propos de cette fonction dans sa conversation de la source au code: comment Le compilateur de CPPHON fonctionne em>. P> importatlib.util.decode_source code> p>
importateurLib.util.decode_source (source_bytes) code>
Décodez les octets donnés représentant le code source et renvoyez-la sous la forme d'une chaîne avec des nouvelles lignes universelles (comme requis par ImportLib.abc.infectLoader.get_source () code>). P>
blockQuote>
Merci! Il est utile que cela soit apparu. Mieux vaut tard que jamais :)
Il y a une prise en charge dans la bibliothèque standard, même en Python 2. Voici le code que vous pouvez utiliser:
def read_source_file(filename):
from lib2to3.pgen2.tokenize import cookie_re
with open_with_encoding_check(filename) as f:
return ''.join([
'\n' if i < 2 and cookie_re.match(line)
else line
for i, line in enumerate(f)
])
Merci - - C'est une solution soignée pour Python 2. En outre, votre débogueur a l'air vraiment cool :)
Il semble que Python 3.4 apporte une réponse à votre question - voir ma réponse.