Traitement de code de caractère assimilé à "Attacher à une plaie" ~ Opération de nom de fichier déformé avec python3 ~

introduction

Je veux traiter un cas où un bogue de nom de fichier se produit.

図3.png 図1.png

Même si le nom de fichier est le même en japonais, il est complètement différent selon que le code de caractère est utf-8 ou shift-jis. Grâce à l'interprétation intelligente de l'interface utilisateur qui affiche le nom du fichier, vous pouvez généralement travailler sans utiliser votre tête, mais si vous devez le faire avec python, le code de caractère est mélangé (le code de caractère diffère selon le fichier à traiter) Situation) devient très gênant. Donc, cette fois, tout en comprenant l'affichage et la conversion de chaque code de caractère, je vise à finalement convertir le nom de fichier japonais de shift-jis en utf-8, qui est la valeur par défaut de python3.

L'hypothèse est de 3 séries. ** 3 séries ** (important car lié au code de caractère). La plupart des connaissances requises pour le code de caractère dans cet article sont ** Site officiel ** (y compris les citations), et certaines Il a été obtenu sur ** ce site **.

Code de caractère assimilé au nom "Doivent être attachés à une blessure"

J'ai téléchargé l'image de «Sont à mettre sur la plaie» de Irasutoya. Ce nom semble différent selon la région (?). Dans cet article, nous traiterons la relation entre ces noms comme suit.

図8.png

Le nom «Bansokou» est [Daijirin](https://www.weblio.jp/content/%E3%81%B0%E3%82%93%E3%81%9D%E3%81%86% Il s'agit d'une nomenclature courante publiée dans E3% 81% 93% E3% 81% 86), tandis que "Cut Bang ([Yutoku Yakuhin Kogyo Co., Ltd.](https://www.yutokuyakuhin.co.jp/product/1-" 1.html)) »et« Band Aid (Johnson End Johnson Co., Ltd.) »sont des noms de produits. Je sais qu'il y a des différences de performances et de matériaux selon les sociétés de vente (je ne les ai pas comparées), mais par souci de simplicité, dans cet article, tous ces noms sont les mêmes, «sont à mettre sur la plaie». Supposons que ce soit un mot précis __. En d'autres termes, "celui à attacher à la plaie" est représenté par l'unique <nom (nom du produit) et la notation> selon les quatre types de règles de verbalisation (** code de caractère **) dans la figure. L'application de cette règle de verbalisation pour traduire s'appelle ** encode **. En python3, le <objet (sont)> à exprimer est défini comme ** type de chaîne **, et le <nom (nom du produit) ou la notation> qui diffère selon les règles de verbalisation est défini comme ** type d'octets **.

Une chaîne Unicode est une chaîne de points de code, qui est un nombre compris entre 0 et 0x10FFFF (1 114 111 en notation décimale). Cette chaîne de points de code est représentée en mémoire sous la forme d'une colonne d'unité de code, qui correspond à une chaîne d'octets de 8 bits. Les règles de traduction d'une chaîne Unicode sous forme de chaîne d'octets sont appelées encodage de caractères ou simplement encodage. (Omis) Le codage par défaut du code source Python est UTF-8, vous pouvez donc inclure des caractères Unicode tels quels dans les littéraux de chaîne.

Dans le code python, le type de chaîne "Doivent être attachés à la plaie" est exprimé en utilisant la ** chaîne de caractères unicode ** (et le ** point de code ** correspondant). Ensuite, lorsque le codage est requis dans le traitement des chaînes de caractères, il est codé comme "bansoko" en utilisant le code de caractère par défaut (hirakana dans l'exemple, UTF-8 en python).

Le code de caractère par défaut peut être confirmé avec le code suivant.

Vérifiez le code de caractère par défaut


import sys
print(sys.getdefaultencoding()) # → utf-8

Inversement, afficher l ' à l'aide du s'appelle ** décoder **.

Vous pouvez également utiliser la méthode decode () de la classe> bytes pour créer une chaîne. Cette méthode prend une valeur de type UTF-8 comme argument de codage et éventuellement un argument d'erreurs.

Puisque la correspondance entre et peut ne pas fonctionner correctement, les arguments <str> .encode () et <bytes> .decode () peuvent prendre des arguments ʻerreurs`. Le traitement des exceptions est effectué selon les règles spécifiées dans cette variable.

Encodage et décodage de "Akasatana"

Immédiatement, la chaîne de caractères "Akasatana" est encodée à l'aide de quatre types de codes de caractères.

utf-8 et décalage-Comparaison de jis


str_src = 'Akasa Tana'
enc_utf = str_src.encode() # default: encoding="utf-8"
enc_u16 = str_src.encode('utf-16')
enc_euc = str_src.encode('euc_jp')
enc_jis = str_src.encode('shift-jis')

print('--- ENCODE ---')
print('encoded by utf-8:', enc_utf, '(length={})'.format(len(enc_utf)))
print('encoded by utf-16:', enc_u16, '(length={})'.format(len(enc_u16)))
print('encoded by euc_jp:', enc_euc, '(length={})'.format(len(enc_euc)))
print('encoded by shift-jis:', enc_jis, '(length={})'.format(len(enc_jis)))
print('--- DECODE ---')
print(enc_utf.decode(), enc_u16.decode('utf-16'), enc_euc.decode('euc_jp'), enc_jis.decode('shift-jis'))
'''
--- ENCODE ---
encoded by utf-8: b'\xe3\x81\x82\xe3\x81\x8b\xe3\x81\x95\xe3\x81\x9f\xe3\x81\xaa' (length=15)
encoded by utf-16: b'\xff\xfeB0K0U0_0j0' (length=12)
encoded by euc_jp: b'\xa4\xa2\xa4\xab\xa4\xb5\xa4\xbf\xa4\xca' (length=10)
encoded by shift-jis: b'\x82\xa0\x82\xa9\x82\xb3\x82\xbd\x82\xc8' (length=10)
--- DECODE ---
Akasata Akasata Akasata Akasata
'''

La sortie est tracée comme suit (utf-16 est également affiché en notation hexadécimale).

図9.png

Les deux octets qui apparaissent au début de l'encodage utilisant utf-16 sont un code appelé BOM.

Le caractère Unicode U + FEFF est utilisé comme marque d'ordre des octets (BOM) et est écrit comme premier caractère du fichier pour aider à la détermination automatique de l'ordre des octets du fichier. Certains encodages, comme UTF-16, nécessitent une nomenclature au début du fichier; lorsqu'un tel encodage est utilisé, la nomenclature est automatiquement écrite comme premier caractère et lors de la lecture du fichier Est implicitement supprimé.

Créer un fichier qui peut être comparé à "Joindre à une plaie"

Pensez maintenant à créer un fichier en spécifiant le nom du fichier dans le code python. Comme d'habitude, on réfléchit au principe en "le collant sur la plaie". Pour réitérer, ** Ici, nous allons nous concentrer sur le "nom du fichier" et poursuivre la discussion sans trop nous soucier du contenu du fichier créé **.

図10.png

Lors de la fabrication de "Doivent être attachés à une plaie", le nom "Appel" est enregistré sur le disque. Ensuite, quand «cela» est nécessaire, il sera traduit (décodé) à chaque fois de «l'appel» à «celui à attacher à la plaie» dans le cerveau de la règle de verbalisation attendue. Considérons ici le cas où la règle de verbalisation attendue n'est que celle de Yutoku Yakuhin. Si le de "bansoukou" est passé à cette règle, il sera traduit incompréhensible en disant "" bansoko "? Seulement" cut bang "" (** = caractères). Il sera brouillé **). Par conséquent, il est nécessaire de sélectionner un approprié lors de la fabrication de "celui à attacher à la plaie". Comment puis-je fabriquer avec le "cut van" stipulé dans les règles de verbalisation de Yutoku Yakuhin? Premièrement, traduisez "celui qui colle à la blessure" par le "nom" de Yutoku Yakuhin. Ensuite, en faisant une demande de fabrication à l'aide de ce , le du produit fini peut également être traduit selon les règles de Yutoku Yakuhin. Il n'y a rien de mal à cela, mais ** si vous êtes habitué au type str qui est traduit (décodé) en pseudonyme simple **, vous voudrez faire une demande de fabrication en utilisant le type str. Dans ce cas, la chaîne de caractères utilisée pour la requête est ** str (= "'\ u ou \ u \ u et \ uba \ un'") **, qui est une traduction de de Yutoku Yakuhin en pseudonyme simple. Il doit y avoir.

Si vous travaillez sans penser à rien, le code de caractère d'encodage spécifié par défaut dans python3 sera appliqué au nom du fichier lors de la génération du fichier. En outre, la valeur par défaut de l'argument «erreurs» est également déterminée. Ceux-ci peuvent également être confirmés par le code suivant.

python


import sys
print(sys.getfilesystemencoding())
print(sys.getfilesystemencodeerrors())
'''
utf-8
surrogateescape
'''

«Surrogate» lui-même a été nouvellement introduit en tant que code pour étendre davantage la zone d'expression d'Unicode. Dans ʻerrors = surrogate escape au moment du décodage de type octets de Python3, la partie correspondante des octets qui ne peut pas être décodée est remplacée par la chaîne de caractères unicode utilisée dans le surrogate. En conséquence, bien que le décodage lui-même soit incomplet, des erreurs peuvent être évitées sans perdre les informations de type d'octets d'origine. Lorsque vous souhaitez recoder le même code, la chaîne de caractères Unicode insérée en spécifiant ʻerrors = substitate escape est supprimée afin que le formulaire d'origine puisse être restauré.

Le gestionnaire d'erreurs de substitution décode tous les octets non ASCII en tant que points de code dans la zone privée Unicode de U + DC80 à U + DCFF. Ces points de code privés sont renvoyés aux mêmes octets lors de l'utilisation de surrogateescape, un gestionnaire d'erreurs lors du codage et de la réécriture des données.

Création d'un fichier de répertoire japonais

Maintenant, envisagez de sauvegarder le fichier avec le nom japonais comme utf-8 et shift-jis, en faisant attention à ce qui précède.

Préparation

Importez le module à utiliser et créez un répertoire de travail. Cet article n'utilise pas de modules de traitement de code de caractères tels que les codecs (je ne suis pas sûr).

Préparation: Importez le module utilisé, créez un répertoire de travail


import os
import glob

testdir = './testdir'
if not os.path.exists(testdir):
    os.mkdir(testdir)

#Rechercher et afficher le chemin du répertoire courant, en se limitant au répertoire
print([p for p in glob.glob('./*') if os.path.isdir(p)])
'''
[..., './testdir', ...]
''' 

Créer un fichier avec utf-8

Comme mentionné ci-dessus, lorsque le code de caractère est utf-8, il est spécifié comme valeur par défaut de python3, il n'y a donc rien à faire attention.

Créer un répertoire pour utf-8

Utilisez ʻos.mkdir () `pour créer le répertoire utf-8" Aiueo "dans le répertoire de travail.

Créer un répertoire (utf-8)


utf_dirname = os.path.join(testdir, 'AIUEO')
os.mkdir(utf_dirname)

Enregistrer le fichier txt avec utf-8

De plus, créez "Kakikukeko.txt" dans "Aiueo". Bien qu'elle ne soit pas utilisée dans cet article, la chaîne de caractères par utf-8 est sortie dans le fichier.

Création de fichier (utf-8)


utf_filename = os.path.join(utf_dirname, 'Kakikukeko.txt')
with open(utf_filename, 'w') as f:
    f.write('SA Shi Su Se So\n')

Créer un fichier avec shift-jis

Enfin, créez un nouveau fichier dont le nom de fichier est défini par shift-jis.

図11.png

Création de répertoire Shift-jis (par type d'octets)

Créez un répertoire shift-jis "Akasatana" dans le répertoire de travail. Jusqu'à présent, le type str était donné à ʻos.mkdir () `, mais cela fonctionne normalement même si ** type bytes est inséré **.

python


jis_dirname = os.path.join(testdir, 'Akasa Tana').encode('shift-jis')
print(jis_dirname)
os.mkdir(jis_dirname)
'''
b'./testdir/\x82\xa0\x82\xa9\x82\xb3\x82\xbd\x82\xc8'
'''

Créer un répertoire shift-jis (par type str)

Plus tôt, nous avons montré qu'un nouveau répertoire peut être créé en utilisant le nom de chemin de type bytes, mais cette fois nous allons créer le répertoire shift-jis "Akasatana" en utilisant le type str. Comme mentionné ci-dessus, il est connu que ʻencoding = 'utf-8' et ʻerrors =' surrogate escape' sont utilisés lors de l'encodage du nom de fichier par défaut, utilisez donc ces options pour changer le type d'octets en type str. Convertir (décoder) en. En spécifiant «erreurs», «dc» est ajouté au début de chaque chaîne de caractères Unicode en sortie. Lorsque la chaîne de caractères unicode de type str générée est placée dans print () par ce décodage spécial, une erreur (ʻUnicodeDecodeError) est renvoyée. Par conséquent, en l'enfermant dans repr ()`, la chaîne de caractères unicode est sortie telle quelle.

Créer un répertoire (shift-jis)


jis_dirname_bytes = os.path.join(testdir, 'Akasa Tana'.encode('shift-jis')
jis_dirname_str = jis_dirname_bytes.decode(errors='surrogateescape'))
print(repr(jis_dirname_str))
os.mkdir(jis_dirname_str)
'''
'./testdir/\udc82\udca0\udc82\udca9\udc82\udcb3\udc82\udcbd\udc82\udcc8'
'''

Création d'un nom de fichier txt avec shift-jis

Créez "Hamayarawa.txt" dans le répertoire "Akasatana" avec shift-jis. Le code de caractère du texte du fichier est également défini sur shift-jis par ʻencoding = 'shift-jis'`.

Création de fichier (shift-jis)


jis_filename = os.path.join(jis_dirname, 'Hamayarawa.txt'.encode('shift-jis'))
# encode()Après.decode(errors='surrogateescape')Peut être joint
with open(jis_filename, 'w', encoding='shift-jis') as f:
    f.write('Tout à coup\n Himiri')

Vérifiez le fichier enregistré une fois

Jusqu'à présent, nous avons défini le nom du fichier avec deux types de codes de caractères, mais affichons-le avec l'interface utilisateur et glob.glob (). Dans la figure ci-dessous, le carré bleu au milieu est le nom du fichier enregistré (octets). Le côté inférieur montre le résultat de l'interface utilisateur traduisant le nom du fichier en spécifiant le code de caractère, et le côté supérieur montre le résultat de l'obtention de la liste des noms de fichier en type str ou en type octets en python.

図12.png

Affichage de l'interface utilisateur par utf-8

J'ai utilisé la première page de Jupyter Notebook. Bien que le répertoire "aiueo" créé par utf-8 soit affiché normalement. Le répertoire "Akasatana" créé par shift-jis est déformé et ne peut pas être sélectionné.

図1.png

"Kakikukeko.txt" dans le répertoire "Aiueo" est également affiché sans aucun problème.

図2.png

Affichage de l'interface utilisateur par shift-jis

J'ai utilisé WinSCP. Bien que le répertoire "Akasatana" créé par shift-jis s'affiche normalement. Le répertoire "aiueo" créé par utf-8 est déformé, et une erreur se produira s'il est sélectionné.

図3.png

"Hamayarawa.txt" dans le répertoire "Akasatana" est également affiché sans aucun problème.

図4.png

Affichage de la liste par glob.glob ()

Si vous essayez d'obtenir une liste de fichiers dans le répertoire de travail normalement, le nom du fichier sera décodé par l'utf-8 par défaut, donc le répertoire ** "Akasatana" sera de type str avec surrogate escape **.

utf-8 pour afficher le nom du chemin


print(glob.glob(os.path.join(testdir, '*')))
print(glob.glob(os.path.join(testdir, 'AIUEO', '*')))
'''
['./testdir/AIUEO',
 './testdir/\udc82\udca0\udc82\udca9\udc82\udcb3\udc82\udcbd\udc82\udcc8']
['./testdir/AIUEO/Kakikukeko.txt']
'''

Ici, le type d'octets est attribué à ** glob.glob (), et le nom de fichier est acquis par la ** méthode de décodage de la sortie obtenue avec un code arbitraire. Si vous décodez avec shift-jis, le nom du répertoire "Akasatana" sera affiché normalement, et le répertoire "Aiueo" sera brouillé.

Liste des noms de chemins utilisant des octets


#Obtenir le nom du fichier avec le type d'octets
bytes_paths = glob.glob(os.path.join(testdir, '*').encode())
print(bytes_paths)
# utf-Décoder à 8
print([f.decode(errors='surrogateescape') for f in bytes_paths])
# shift-Décoder avec jis
print([f.decode('shift-jis', errors='surrogateescape') for f in bytes_paths])
'''
[b'./testdir/\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a',
 b'./testdir/\x82\xa0\x82\xa9\x82\xb3\x82\xbd\x82\xc8']
['./testdir/AIUEO',
 './testdir/\udc82\udca0\udc82\udca9\udc82\udcb3\udc82\udcbd\udc82\udcc8']
['./testdir/縺 > 縺\udc86 Docteur ♀',
 './testdir/Akasa Tana']
'''

Conversion de code de caractère du nom de fichier (shift-jis → utf-8)

Enfin, remplacez le nom de fichier enregistré par shift-jis par le nom de fichier défini par utf-8.

Conversion du nom du répertoire (shift-jis)

Récupérez directement le chemin du répertoire défini par shift-jis et remplacez-le par la chaîne de utf-8 par ʻos.rename () `.

Conversion de code de caractère de répertoire unique


target = glob.glob(os.path.join(testdir, '*').encode())[1]
print(repr(target))
print(target.decode('shift-jis'))
os.rename(target, target.decode('shift-jis'))
'''
b'./testdir/\x82\xa0\x82\xa9\x82\xb3\x82\xbd\x82\xc8'
./testdir/Akasa Tana
'''

Le résultat est présenté ci-dessous. Le code du caractère a été converti avec succès et "Akasatana" est visible.

図5.png

Conversion par lots de noms de fichiers (shift-jis)

Entrez dans le répertoire "Akasatana".

図6.png

Dans cet article, il n'existe qu'un seul fichier, mais en supposant le cas où la conversion de code de caractère de plusieurs fichiers est requise, le code avec traitement en boucle est utilisé. En se concentrant sur la partie japonaise, la partie "Akasatana" est la chaîne de caractères utf-8, et le "Hamayarawa.txt" est la chaîne de caractères shift-jis. ** Ce sont des décodages de type mixte et octets Renvoie une erreur ** (Puisque la partie "./testdir" est incluse dans la chaîne de caractères ascii, utf-8 et shift-jis fonctionnent avec des octets communs, vous n'avez donc pas à y penser). Il est nécessaire de séparer ces deux avant le traitement.

Conversion de code de caractère de tous les fichiers du répertoire


for f in glob.glob(os.path.join(testdir, 'Akasa Tana', '*').encode()):
    #Nom du répertoire (utf-Décoder avec 8)
    d_str = os.path.dirname(f).decode()
    #Nom de fichier (shift-Décoder avec jis)
    b_str = os.path.basename(f).decode('shift-jis')
    #Conversion de code de caractère
    os.rename(f, os.path.join(d_str, b_str))

Avec cela, "Hamayarawa.txt" a également été défini en toute sécurité avec le code de caractère de utf-8.

図7.png

À la fin

Jusqu'à présent, je pense avoir enfin compris le talent pour manipuler les chaînes de caractères. En particulier, j'ai trouvé que le travail devient beaucoup plus facile si je fais bon usage du type bytes, auquel j'étais habitué et dont je ne me souciais pas du tout. En plus de cela, si vous pouvez choisir la gestion des «erreurs» au moment de l'encodage / décodage, la plupart du travail sera possible.

Après tout, je pense que le document officiel est le plus simple à comprendre.

Recommended Posts