Cet article a un prédécesseur et un successeur.
Implorer le pardon des gens vraiment compétents
Dans un article précédent je racontais mes aventures de débutant avec le langage de programmation TypeScript, et comment ChatGPT m’avait aidé (je suis loin d’être le seul). Bon, il ne manque pas de gens sur le réseau pour expliquer que cette démarche est mauvaise, et j’ai moi-même rendu compte d’une étude très sérieuse qui explique qu’elle ne saurait que faire perdre du temps à un programmeur expérimenté et expert de son domaine. Je ne parle ici que pour les débutants, comme moi en l’occurrence avec TypeScript.
Pour aborder un nouveau langage de programmation, il faut en apprendre la syntaxe, et cela peut être pénible. Or la syntaxe ne présente aucun intérêt, et justement, au contraire des langages humains, la syntaxe des langages de programmation est rigide et univoque : cela convient bien à un système d’IA. Il faut aussi découvrir les noms de toutes les fonctions des bibliothèques de calcul, d’entrée-sortie, de manipulation de chaînes de caractères et de structures de données diverses et variées, et les créateurs de langages sont dotés pour cela d’une imagination débordante : un débutant humain pourrait passer des heures à fouiller la documentation, mais un agent IA fait cela bien plus vite. On aurait tort de se priver de son aide.
Justement pour ce second épisode je me suis attaqué aux entrées-sorties, un domaine dépourvu de noblesse intellectuelle, mais néanmoins plein de complication, puisqu’il faut établir des communications entre le programme et des objets matériels, par l’intermédiaire du système d’exploitation, des objets d’une grande complexité, et là aussi ChatGPT m’a aidé, sans que pour autant je cesse de maîtriser mon code, parce que cela reste concis et intelligible.
Alors allons-y.
Lire des données dans un fichier
Pour continuer la lignée de mes langages précédents, je lirai des extraits de la banque de données de protéines SwissProt, un fichier texte séquentiel, et j’en extrairai le texte de la séquence proprement dite, débarrassée des informations descriptives destinées au lecteur humain, pour la réduire au format dit FASTA, accepté par tous les logiciels d’analyse.
Voici une entrée de la banque SwissProt ; elle donne la séquence du gène YWHAB chez le Xénope [1], une grande grenouille africaine griffue. Les deux premières colonnes de chaque ligne identifient un type de ligne. Les lignes qui nous intéresseront sont la ligne "AC", qui donne le ou les Accession Numbers qui identifient la séquence de façon unique par une nomenclature internationale, et, à la fin, les lignes dont les deux premières colonnes sont vides, et qui donnent la séquence d’acides aminés de la protéine, codés selon le code génétique :
Et voici le fichier FASTA que je veux en extraire, après suppression des espaces et des commentaires :
Premier dialogue avec ChatGPT
En TypeScript, pour NodeJs, comment déclare-t-on un fichier ? Quelle est la méthode pour l’ouvrir ? lire une ligne ?
En Node.js avec TypeScript, tu travailles avec les modules intégrés de Node, notamment fs (file system).
Contrairement à certains langages (C, Pascal, etc.), on ne "déclare" pas un fichier avec un mot-clé particulier : on importe simplement le module fs et on manipule les fichiers via ses fonctions.
1. Déclarer un fichier (ouvrir/écrire) :
En TypeScript, tu importes fs ainsi :
Pour écrire dans un fichier :
2. Lire un fichier ligne par ligne :
En Node, on utilise le module readline avec un ReadableStream :
✅ Résumé :
Pas de "déclaration" spéciale, juste import * as fs.
fs.openSync ou fs.open pour ouvrir.
fs.readFileSync / fs.readFile pour lire en bloc.
readline + fs.createReadStream pour lire ligne par ligne.
Abandonner la distribution Ubuntu des paquets NodeJS et Npm
Pour réparer NodeJS et Npm (packages Ubuntu pas terribles) :
Installe Node via NodeSource ou nvm.
Option 1 : avec nvm (recommandé)
L’organisation du code a changé : leçons modernes
La façon d’organiser un projet informatique a pas mal changé depuis mes débuts au siècle dernier. Jadis, on installait un compilateur dans l’arborescence du système, et tous les programmes à construire l’utilisaient de la même façon. Aujourd’hui, on crée un répertoire par projet, et c’est à partir de ce répertoire que l’on effectue les opérations d’installation énumérées ci-dessus, parce que la configuration de la chaîne de compilation peut être plus compliquée qu’un unique compilateur, et qu’elle peut varier d’un projet à un autre.
Puis on place les fichiers sources dans un sous-répertoire src, ce n’est pas obligatoire mais usuel, c’est écrit dans le fichier de configuration tsconfig.json. Je place les fichiers de données dans un sous-répertoire Data.
C’est ce que nous avons vu ci-dessus, avec les conseils judicieux de ChatGPT.
Lire le fichier de séquence et le filtrer pour obtenir un fichier Fasta
Nous allons ajouter au programme ci-dessus une fonction filtrerFasta pour omettre les lignes de commentaires à usage humain, ajouter la ligne de commentaire du format Fasta avec les données de la ligne AC, retirer les espaces blancs des lignes de séquence. Par la même occasion nous allons lui ajouter de quoi l’invoquer depuis la ligne de commande en important la classe Command :
On observe que l’argument de la méthode replace(/\s/g, "") contient une expression régulière :
– /\s/ indique le motif qui sera la cible de la méthode replace, le motif est encadré par des barres obliques /, il est noté \s, ce qui désigne un espace ou caractère équivalent, tel que tabulation ou espace insécable ;
– le caractère g indique que le remplacement sera global, c’est-à-dire que si le motif apparaît plusieurs fois dans la même ligne de texte, il sera remplacée à chaque fois ;
– la chaîne "" indique ce qui remplacera le motif cible, en l’occurrence la chaîne vide.
Passer le nom du fichier en argument de la ligne de commande
Coder « en dur » dans le programme le nom du fichier à traiter est singulièrement malcommode. Voici comment passer le nom de fichier en argument à l’appel du programme depuis la ligne de commande ; la classe Command permet d’instancier notre programme program en commande du shell, capable de recevoir des arguments et des options :
On observera, au début du programme, la série de déclarations de méthodes qui suivent l’invocation de program.
La méthode .parse déclenche l’analyse des arguments et donc l’exécution du programme.
Lire plusieurs fichiers à la suite
Nous aimerions aussi que notre programme puisse lire des séquences dans plusieurs fichiers, ce qui suppose qu’il puisse itérer sur une liste de noms de fichiers, ainsi :
On observe, dans l’invocation de program, des méthodes déclaratives, comme .argument, qui permet d’ajouter une définition d’argument à la commande (explication fournie par ChatGPT).
Ici les arguments peuvent être plusieurs, d’où la présence de l’opérateur ..., ou opérateur de reste, qui permet à la méthode d’accepter un nombre indéfini d’arguments. Ce sera le rôle de la méthode .action, à la ligne suivante, d’itérer sur la liste d’arguments et d’en détecter la fin.
Écrire le résultat dans un fichier
Enfin ce serait bien d’écrire le résultat dans un fichier avec une option -o (même si on peut toujours utiliser les possibilités de redirection du shell). Voici :
On notera, dans la déclaration de type du paramètre outStream des fonctions lireLignes et filtrerFasta ainsi que de la méthode .action la notation :
C’est une particularité de TypeScript/JavaScript : ces fonctions et méthodes peuvent accepter, comme argument outStream, soit des objets de type fs.WriteStream, si l’option -o est présente et suivie d’un nom de fichier de sortie, soit des objets de type NodeJS.WriteStream dans le cas contraire (résultat sur la sortie standard).
Voici un exemple de résultat :
Cet article a un prédécesseur et un successeur.