Debian-facile

Bienvenue sur Debian-Facile, site d'aide pour les nouveaux utilisateurs de Debian.

Vous n'êtes pas identifié(e).

#1 19-12-2020 16:10:24

carlus
Membre
Inscription : 19-12-2020

[python] Problème de petits carrés dans des noms de fichiers

J'avais un problème dans des noms de fichiers qui s'affichaient dans Thunar avec des petits carrés à la place de la plupart des caractères accentués.
Les fichiers avaient été récupérés d'on ne sait où et impossible d'en connaître l'encodage d'origine vu qu'ils étaient déjà en UTF8.

Grâce au génie de Elzen, j'ai pu résoudre complètement le problème.
Il a fait un script qui corrige tous les caractères de façon récursive, nom de répertoire compris.
Je poste donc tout ça car j'ai beaucoup cherché avant et ce problème ne semble pas courant.

Il a fallu d'abord trouver le code que python3 trouvait pour chaque fichier qui posait problème.
Pour cela j'ai isolé chaque exemple de fichier dans un répertoire seul et passé la commande suivante qui affiche dans le retour le code en question à la place du caractère accenté :

os.listdir(".")[0].encode()



Ensuite, il faut enrichir le dictionnaire en début de script avec les bons codes et les bons caractères.

Il suffit enfin d'appeler le script avec la commande suivante :

python3 [nom du script] [chemin du repertoire racine à traiter]



Voici le script :

#! /usr/bin/python3
# coding: Utf-8

import os
import sys
import subprocess

converts = { b"\xc2\x82": "é",
             b"\xc2\x87": "ç",
             b"\xc2\x8a": "è",
             b"\xc2\x85": "à",
             b"\xc2\x83": "â" }

def rename(origin):
    target = origin.encode()
    for weird, valid in converts.items():
        target = subprocess.run(("tr", weird, valid),
            input=target, capture_output=True).stdout
    if target != origin.encode():
        subprocess.run(("mv", origin, target))
    target = target.decode()
    if os.path.isdir(target):
        for file in os.listdir(target):
            rename(os.path.join(target, file))

if __name__ == "__main__":
    for arg in sys.argv[1:]:
        rename(arg)



Le problème est donc résolu, en espérant qu'il pourra servir à quelqu'un d'autre.
Joyeux Noël à tous !

Hors ligne

#2 19-12-2020 16:18:05

Elzen
Modérateur
Distrib. : Debian Sid GNU/Linux
Noyau : amd64 (à jour le vendredi)
(G)UI : Touhy
Inscription : 01-07-2014

Re : [python] Problème de petits carrés dans des noms de fichiers

Génie, 'faut pas exagérer, j'ai surtout des restes de soucis divers et variés qui ont appréciablement pu se combiner ici ^^'

Une difficulté ici a été d'identifier l'encodage utilisé, ce que captnfab et moi avons tenté sans succès. Mais bon, vu qu'on a pu faire sans…


Un point important: la commande utilisée pour le remplacement de caractères est tr, qui fait du remplacement caractère par caractère. Ça marche donc parce qu'il accepte le « b"\xc2\x82" » comme étant un caractère unique, ce qui n'était pas forcément gagné d'avance et pourrait donc gêner une prochaine fois. C'est pour ça que j'ai préféré mettre plusieurs coups de tr de suite pour les différents caractères concernés plutôt que de tout faire en une seule passe, même si ça aurait sans doute marché aussi bien.

À la réflexion, je me sens même assez bête de ne pas avoir pensé à essayer directement la méthode « replace » des chaînes de caractères en Python, qui aurait sans doute fait le taff aussi bien tout en étant largement moins lourde. Pour ma défense, je venais d'essayer de récupérer le caractère d'origine en shell uniquement pendant un certain temps sans succès, et donc je n'ai pensé à passer par python que pour essayer de continuer à faire ce que je faisais jusque là ^^'

Si vous avez d'autres retours sur pourquoi mon script est atroce, n'hésitez pas tongue

Hors ligne

#3 19-12-2020 20:00:06

David5647
Membre
Distrib. : Debian Bullseye/Sid
Noyau : 5.7.0-2-amd64
(G)UI : KDE/i3wm
Inscription : 27-08-2017

Re : [python] Problème de petits carrés dans des noms de fichiers

Juste quelques questions (par curiosité maladive...):

Je trouve ce mélange byte/utf-8 très perturbant.
Je ne vois pas pourquoi tu encodes target = origin.encode() , puisque python doit le décoder aussitôt dans subprocess(... input=target)
Pareil pour le contenu de target, python doit le décoder implicitement lorsque tu lances la commande tr ....

b"\xc2\x82" est une représentation ascii d'un type byte, que python interprète en utf-8,
alors que ce qui est envoyé via origin est une chaine (string) contenant une représentation ascii d'un byte mais d'encodage inconnu.
Ce que je veux dire, c'est que:

>>> a = "\xc2\x83".encode()
>>> b = b"\xc2\x83"
>>> a == b
False


Désolé c'est un peu confus, finalement, ma question, c'est :
pourquoi passer par du byte? Alors qu'on manipule simplement du texte, non?

Hors ligne

#4 20-12-2020 16:40:21

Elzen
Modérateur
Distrib. : Debian Sid GNU/Linux
Noyau : amd64 (à jour le vendredi)
(G)UI : Touhy
Inscription : 01-07-2014

Re : [python] Problème de petits carrés dans des noms de fichiers

Parce qu'au début de la discussion, on a passé un bon moment à réfléchir à la question de l'encodage qui avait produit ces machins, et à essayer des outils du type iconv. Donc dans le doute, potentiels soucis d'encodage → bytes plutôt que str. Même si effectivement, dans les faits, c'était un réflexe pas tellement utile ici.
(Pour le paramètre input, étant encore loin d'être habitué à subprocess, j'ai mis le paramètre en str au départ, ça a râlé parce que ça voulait du bytes, donc j'ai converti sans vérifier si une autre option existait)

Hors ligne

#5 20-12-2020 20:49:52

David5647
Membre
Distrib. : Debian Bullseye/Sid
Noyau : 5.7.0-2-amd64
(G)UI : KDE/i3wm
Inscription : 27-08-2017

Re : [python] Problème de petits carrés dans des noms de fichiers

Ok!
A y réfléchir, ce serait plutôt l'inverse de mon idée de départ,
j'imagine que la communication python <=> bash passe par du byte, l'objet "string" n'y ayant pas de sens.
Finalement, peut-être est-ce plus naturel de réfléchir en bytes tongue

J'ai dans mes rares utilisation de subprocess, plutôt fait quelque chose comme ça :

cmd = subprocess.Popen(f'echo "{variable}"', shell=True, stdout=subprocess.PIPE)
result = subprocess.check_output(f"tr {weird} {valid}", stdin=cmd.stdout, shell=True).decode("utf-8")

mais, à priori input permet de faire la même chose plus directement.
Et je m’aperçois en reprenant ce bout de code, qu'effectivement, c'est du byte qui est renvoyé... tongue

Là, ou un .replace() aurait exigé le même type,

converts = { b"\xc2\x82": "é".encode(),
             ...

subprocess ne bronche pas.

Bref, en revenant là, ça me parait plus clair, mais difficile à dire à son cerveau que bytes != str

Hors ligne

#6 21-12-2020 14:16:12

Elzen
Modérateur
Distrib. : Debian Sid GNU/Linux
Noyau : amd64 (à jour le vendredi)
(G)UI : Touhy
Inscription : 01-07-2014

Re : [python] Problème de petits carrés dans des noms de fichiers

Ça paraît logique, oui, le bytes est la suite de bits tel qu'on le rencontre à l'extérieur, et le str est la chaîne de caractères correctement préparée pour son usage dans Python (mais c'est très probablement perturbant pour les gens encore trop habitués à Python 2 où le nom str était utilisé pour l'équivalent du bytes actuel, et où l'équivalent du str actuel s'appelait unicode tongue C'est perturbant, mais ils ont bien fait de changer, les noms actuels sont plus clairs, je trouve).

Une partie du souci vient, je pense, du fait que Python est « trop » sympathique en convertissant automatiquement entre bytes et str chaque fois qu'il y arrive, ce qui fait qu'on perd de vue le fait que, dès qu'on doit communiquer avec l'extérieur, c'est dans la pratique du bytes qu'on est censé utiliser, mais que la plupart du temps, c'est du str qu'on a entre les doigts à ce moment-là. Comme les conversions à l'intérieur du programme doivent être explicites (contrairement à pas mal d'autres langages où on peut concaténer n'importe quel type à une chaîne de caractères, par exemple), on ne pense pas forcément au fait que celles vers/depuis l'extérieur sont automatiques.

Encore une fois, pour le coup, ce n'était pas franchement grave, puisque les deux types fonctionnaient en interne, mais ce n'était effectivement pas forcément clair, surtout pour un code pas commenté parce que fait en vitesse pour un usage immédiat et jetable. Si je re-rencontre un problème de ce style, je tâcherai de me souvenir de ça et de faire un script un peu plus compréhensible ^^

Hors ligne

Pied de page des forums