One place for hosting & domains

      eBook Comment coder dans Go


      Télécharger le eBook complet

      eBook Comment coder dans Go au format EPUB

      eBook Comment coder dans Go au format PDF

      Présentation de l’eBook

      Cet eBook a été conçu pour vous introduire à l’écriture de programmes avec le langage de programmation Go. Vous apprendez à écrire des outils et des applications utiles exécutables sur des serveurs distants ou des systèmes locaux Windows, macOS et Linux dans le cadre du développement.

      Cet eBook s’inspire de la série de tutoriels Comment coder dans Go, que vous trouverez dans DigitalOcean Community. Les sujets traités sont les suivants :

      1. Installer et configurer un environnement de développement local Go sur les systèmes Windows, macOS et Linux

      2. Concevoir vos programmes avec une logique conditionnelle, et notamment les instructions de commutation permettant de contrôler le flux du programme

      3. Définir vos propres structures de données et créer des interfaces pour le code réutilisable

      4. Écrire des fonctions personnalisées de gestion des erreurs

      5. Développer et installer vos programmes Go afin qu’ils puissent fonctionner sur différents systèmes d’exploitation et sur différentes architectures de processeur

      6. Utiliser des balises pour transférer des arguments à vos programmes, réécrire les options par défaut

      Chaque chapitre peut être lu indépendamment des autres ou être utilisé à titre de référence. Vous pouvez également suivre l’intégralité des chapitres du début à la fin. N’hésitez pas à passer au(x) chapitre(s) le(s) plus adapté(s) à vos objectifs au cours de votre apprentissage de Go avec cet eBook.

      Télécharger le eBook

      Vous pouvez télécharger le eBook au format EPUB ou PDF en suivant les liens ci-dessous.

      Télécharger le eBook complet

      eBook Comment coder dans Go au format EPUB

      eBook Comment coder dans Go au format PDF

      Si, une fois que vous aurez atteint la fin de cet eBook, vous souhaitez en savoir plus sur la manière de construire des outils et des applications avec Go, consultez la section Go de DigitalOcean Community.



      Source link

      Comment utiliser le module des collections dans Python 3


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Python 3 possède un certain nombre de structures de données intégrées, notamment des tuples, des dictionnaires et des listes. Les structures de données nous fournissent un moyen d’organiser et de stocker les données. Le module de collections nous aide à remplir et à manipuler les structures de données de manière efficace.

      Dans ce tutoriel, nous allons passer en revue trois classes des collections pour vous aider à travailler avec des tuples, des dictionnaires et des listes. Nous utiliserons namedtuples pour créer des tuples avec des champs nommés, defaultdict pour regrouper de manière concise les informations dans les dictionnaires, et deque pour ajouter efficacement des éléments de chaque côté d’un objet de type liste. 

      Pour ce tutoriel, nous travaillerons principalement avec un inventaire de poissons que nous devons modifier au fur et à mesure que des poissons sont ajoutés ou retirés d’un aquarium fictif.

      Conditions préalables

      Pour tirer le meilleur parti de ce tutoriel, il est recommandé de se familiariser avec le tuple, le dictionnaire et les types de données de liste que ce soit en ce qui concerne leur syntaxe que de la manière d’en extraire des données. Vous pouvez consulter ces tutoriels pour obtenir les informations de base nécessaires :

      Ajouter des champs nominatifs aux tuples

      Les tuples de Python sont une séquence d’éléments immuables, ou inchangeables, ordonnés. Les tuples sont fréquemment utilisés pour représenter des données en colonnes ; par exemple, les lignes d’un fichier CSV ou les lignes d’une base de données SQL. Un aquarium peut garder une trace de son inventaire de poissons sous la forme d’une série de tuples.

      Un tuple de poissons individuel :

      ("Sammy", "shark", "tank-a")
      

      Ce tuple est composé de trois éléments de chaîne caractères.

      Bien qu’utile à certains égards, ce tuple n’indique pas clairement ce que représente chacun de ses champs. En réalité, l’élément 0 est un nom, l’élément 1 est une espèce, et l’élément 2 est le réservoir de stockage. 

      Explication des champs de tuples de poissons :

      name species tank
      Sammy shark tank-a

      Ce tableau montre clairement que chacun des trois éléments du tuple a une signification claire.

      namedtuple du module collections vous permet d’ajouter des noms explicites à chaque élément d’un tuple pour rendre ces significations claires dans votre programme Python.

      Utilisons namedtuple pour générer une classe qui nomme clairement chaque élément du tuple de poisson :

      from collections import namedtuple
      
      Fish = namedtuple("Fish", ["name", "species", "tank"])
      

      from collections import namedtuple donne à votre programme Python l’accès à la fonction d’usine namedtuple. L’appel de la fonction namedtuple() renvoie une classe qui est liée au nom Fish. Le namedtuple() a deux arguments : le nom souhaité de notre nouvelle classe "Fish" et une liste d’éléments nommés ["name", "species", "tank"]. 

      Nous pouvons utiliser la classe Fish pour représenter le tuple de poissons de tout à l’heure :

      sammy = Fish("Sammy", "shark", "tank-a")
      
      print(sammy)
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      Fish(name="Sammy", species="shark", tank='tank-a')

      sammy est instancié à l’aide de la classeFish. sammy est un tuple avec trois éléments clairement nommés. 

      Les champs de sammy sont accessibles par leur nom ou avec un index tuple traditionnel :

      print(sammy.species)
      print(sammy[1])
      

      Si nous lançons ces deux appels d’impression, nous obtiendrons le résultat suivant :

      Output

      shark shark

      L’accès à .species a la même valeur que l’accès au deuxième élément de sammy en utilisant [1]. 

      Utiliser namedtuple du module collections rend votre programme plus lisible tout en conservant les propriétés importantes d’un tuple (à savoir qu’ils sont immuables et ordonnés). 

      De plus, la fonction d’usine namedtuple ajoute plusieurs méthodes supplémentaires aux instances de Fish.

      Utilisez ._asdict() pour convertir une instance en dictionnaire :

      print(sammy._asdict())
      

      Si nous lançons print, vous verrez des résultats comme ceux qui suivent :

      Output

      {'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}

      Appeler .asdict() sur sammy renvoie un dictionnaire mettant en correspondance les noms de chacun des trois champs avec leurs valeurs correspondantes. 

      Les versions de Python plus anciennes que 3.8 pourraient produire cette ligne de manière légèrement différente. Vous pourriez, par exemple, voir un OrderedDict au lieu du simple dictionnaire présenté ici. 

      Note : Dans Python, les méthodes avec des traits de soulignement en tête sont généralement considérées comme « privées ». Les méthodes supplémentaires fournies par namedtuple (comme _asdict(), ._make(), ._replace(), etc.), sont toutefois publiques.

      Rassembler des données dans un dictionnaire

      Il est souvent utile de collecter des données dans les dictionnaires Python. defaulttdict du module de collections peut nous aider à rassembler les informations dans les dictionnaires de manière rapide et concise. 

      defaultdict ne soulève jamais une KeyError. Si une clé n’est pas présente, defaulttdict se contente d’insérer et de renvoyer une valeur de remplacement à la place : 

      from collections import defaultdict
      
      my_defaultdict = defaultdict(list)
      
      print(my_defaultdict["missing"])
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      []

      defaultdict insère et renvoie une valeur de remplacement au lieu de lancer une KeyError. Dans ce cas, nous avons spécifié la valeur de remplacement sous forme de liste.

      Les dictionnaires ordinaires, en revanche, lancent une KeyError sur les clés manquantes :

      my_regular_dict = {}
      
      my_regular_dict["missing"]
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'missing'

      Le dictionnaire habituel my_regular_dict soulève une KeyError lorsque nous essayons d’accéder à une clé qui n’est pas présente. 

      defaulttdict se comporte différemment d’un dictionnaire ordinaire. Au lieu de soulever une KeyError sur une clé manquante, defaultdict appelle la valeur de remplacement sans argument pour créer un nouvel objet. Dans ce cas, list() pour créer une liste vide. 

      Pour continuer avec notre exemple d’aquarium fictif, disons que nous avons une liste de tuples de poissons représentant l’inventaire d’un aquarium :

      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      

      Trois poissons existent dans l’aquarium – leur nom, leur espèce et leur bac de rétention sont notés dans ces trois tuples.

      Notre objectif est d’organiser notre inventaire par réservoir – nous voulons connaître la liste des poissons présents dans chaque réservoir. En d’autres termes, nous voulons un dictionnaire qui cartographie "tank-a". à ["Jamie", "Mary"] et "tank-b" à ["Jamie"].

      Nous pouvons utiliser defaulttdict pour regrouper les poissons par bassin :

      from collections import defaultdict
      
      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      fish_names_by_tank = defaultdict(list)
      for name, species, tank in fish_inventory:
          fish_names_by_tank[tank].append(name)
      
      print(fish_names_by_tank)
      

      En exécutant ce code, nous obtiendrons le résultat suivant :

      Output

      defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

      fish_names_by_tank est déclaré comme un paramètre par défaut qui consiste par défaut à insérer list() au lieu de lancer une KeyError.  Comme cela garantit que chaque clé dansfish_names_by_tank pointera sur une liste, nous pouvons librement appeler .append() pour ajouter des noms à la liste de chaque réservoir.

      defaultdict vous aide ici car il réduit le risque d’erreurs inattendues de KeyErrors. Réduire lesKeyErrors inattendues signifie que votre programme peut être écrit plus clairement et avec moins de lignes. Plus concrètement, l’idiome defaultdict permet d’éviter d’instancier manuellement une liste vide pour chaque réservoir.

      Sans défaultdict, le corps de la boucle for aurait pu ressembler davantage à ceci :

      More Verbose Example Without defaultdict

      ...
      
      fish_names_by_tank = {}
      for name, species, tank in fish_inventory:
          if tank not in fish_names_by_tank:
            fish_names_by_tank[tank] = []
          fish_names_by_tank[tank].append(name)
      

      L’utilisation d’un simple dictionnaire (au lieu d’un defaultdict) signifie que le corps de la boucle for doit toujours vérifier l’existence du tank donné dans fish_names_by_tank. Ce n’est qu’après avoir vérifié que tank est déjà présent dans fish_names_by_tank, ou vient d’être initialisé avec un [], qu’on peut ajouter le nom du poisson.

      defaultdict peut aider à réduire le nombre de codes passe-partout lors du remplissage des dictionnaires car il ne provoque jamais de KeyError.

      Utilisation de deque pour ajouter efficacement des éléments de chaque côté d’une collection

      Les listes Python sont une séquence d’éléments ordonnée, mutable ou modifiable. Python peut ajouter des listes en temps constant (la longueur de la liste n’a aucun effet sur le temps qu’il faut pour l’ajout), mais l’insertion au début d’une liste peut être plus lente (le temps nécessaire augmente à mesure que la liste s’agrandit).

      En termes de notation Big O, l’ajout à une liste est une opération O(1) à temps constant. L’insertion au début d’une liste, en revanche, est plus lente avec O(n) performance. 

      Note : Les informaticiens mesurent souvent la performance des procédures en utilisant ce qu’on appelle la notation « Big O ». Lorsque la taille d’une entrée n’a aucun effet sur le temps nécessaire pour exécuter une procédure, on dit qu’elle se déroule en temps constant ou O(1) (“Big O of 1”). Comme vous l’avez appris plus haut, Python peut s’ajouter aux listes à performance temporelle constante, autrement dit O(1). 

      Parfois, la taille d’une entrée a une incidence directe sur le temps nécessaire à l’exécution d’une procédure. L’insertion au début d’une liste Python, par exemple, est d’autant plus lente qu’il y a plus d’éléments dans la liste. La notation Big O utilise la lettre n pour représenter la taille de l’entrée. Cela signifie que l’ajout d’éléments au début d’une liste Python se fait en « temps linéaire » ou O(n) (“Big O of n”). 

      En général, les procédures O(1) sont plus rapides que les procédures O(n).

      On peut l’insérer au début d’une liste Python :

      favorite_fish_list = ["Sammy", "Jamie", "Mary"]
      
      # O(n) performance
      favorite_fish_list.insert(0, "Alice")
      
      print(favorite_fish_list)
      

      Si nous exécutons ce qui suit, nous obtiendrons le résultat suivant :

      Output

      ['Alice', 'Sammy', 'Jamie', 'Mary']

      La méthode .insert (index, objet) sur list nous permet d’insérer "Alice" au début de favorite_fish_list. Toutefois, il est à noter que l’insertion au début d’une liste a O(n) performance. Comme la durée de favorite_fish_list augmente, le temps nécessaire pour insérer un poisson au début de la liste augmentera proportionnellement et prendra de plus en plus de temps.

      deque (prononcé « deck ») du module collections est un objet de type liste qui nous permet d’insérer des éléments au début ou à la fin d’une séquence avec une performance à temps constant (O(1)).

      Insérez un élément au début d’un deque: 

      from collections import deque
      
      favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
      
      # O(1) performance
      favorite_fish_deque.appendleft("Alice")
      
      print(favorite_fish_deque)
      

      En exécutant ce code, nous obtiendrons le résultat suivant :

      Output

      deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

      Nous pouvons instancier un deque en utilisant un ensemble d’éléments préexistants, en l’occurrence une liste de trois noms de poissons favoris. L’appel de la méthode appendleft de favorite_fish_deque nous permet d’insérer un élément au début de notre collection avec la performance O (1). O(1) performance signifie que le temps nécessaire pour ajouter un élément au début de favorite_fish_deque n’augmentera pas, même si favorite_fish_deque a des milliers ou des millions d’éléments. 

      Note : Bien que deque ajoute des entrées au début d’une séquence plus efficacement qu’une liste, deque n’effectue pas toutes ses opérations plus efficacement qu’une liste. Par exemple, l’accès à un article aléatoire dans un deque a une performance O(n), mais l’accès à un élément aléatoire d’une liste a une performance O(1). Utilisez deque lorsqu’il est important d’insérer ou de retirer rapidement des éléments de chaque côté de votre collection. Une comparaison complète des performances temporelles est disponible sur le wiki de Python.

      Conclusion

      Le module des collections est une partie puissante de la bibliothèque standard Python qui vous permet de travailler avec des données de manière concise et efficace.  Ce tutoriel couvrait trois des cours fournis par le module des collections comprenant namedtuple, defaultdict, et deque. 

      D’ici, vous pouvez utiliser la documentation du module de collecte pour en savoir plus sur les autres classes et utilités disponibles. Pour en savoir plus sur Python en général, vous pouvez lire notre Série de tutoriels Comment coder en Python 3. 



      Source link

      Utiliser les buffers dans Node.js


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Un buffer est un espace en mémoire (généralement de la RAM) qui stocke des données binaires. Dans Node.js, nous pouvons accéder à ces espaces de mémoire grâce à la classe Buffer intégrée. Les buffers stockent une séquence d’entiers, comme un array en JavaScript. Contrairement aux arrays, vous ne pouvez pas modifier la taille d’un buffer une fois qu’il est créé.

      Vous avez peut-être utilisé des buffers sans vous en rendre compte si vous avez déjà écrit du code de Node.js. Par exemple, lorsque vous lisez à partir d’un fichier avec fs.readFile(), les données renvoyées au callback ou à la Promise sont un objet buffer. En outre, lorsque des requêtes HTTP sont effectuées dans Node.js, elles renvoient des flux de données qui sont temporairement stockés dans un buffer interne lorsque le client ne peut pas traiter le flux en une seule fois.

      Les buffers sont utiles lorsque vous interagissez avec des données binaires, généralement à des niveaux de réseau inférieurs. Ils vous donnent également la possibilité d’effectuer des manipulations fines de données dans Node.js.

      Dans ce tutoriel, vous utiliserez Node.js REPL pour parcourir divers exemples de buffers, comme la création de buffers, la lecture de buffers, l’écriture et la copie de buffers, et l’utilisation de buffers pour convertir des données binaires en données codées. À la fin du tutoriel, vous aurez appris comment utiliser la classe Buffer pour travailler avec des données binaires.

      Conditions préalables

      Étape 1 — Création d’un buffer

      Cette première étape vous présentera les deux façons principales de créer un objet buffer dans Node.js.

      Pour décider de la méthode à utiliser, vous devez répondre à cette question : voulez-vous créer un nouveau buffer ou extraire un buffer à partir de données existantes ? Si vous allez stocker en mémoire des données que vous n’avez pas encore reçues, vous voudrez créer un nouveau buffer. Dans Node.js, nous utilisons la fonction alloc() de la classe Buffer pour ce faire.

      Ouvrons le Node.js REPL pour voir par nous-mêmes. Dans votre terminal, entrez la commande node :

      Vous verrez que l’invite commence par >.

      La fonction alloc() prend la taille du buffer comme premier et seul argument requis. La taille est un nombre entier représentant le nombre d’octets de mémoire que l’objet buffer utilisera. Par exemple, si nous voulions créer un buffer de 1 Ko (kilooctet), équivalent à 1024 octets, nous entrerions ceci dans la console :

      • const firstBuf = Buffer.alloc(1024);

      Pour créer un nouveau buffer, nous avons utilisé la classe Buffer globalement disponible, qui contient la méthode alloc(). En fournissant 1024 comme argument pour alloc(), nous avons créé un buffer d’une taille de 1 Ko.

      Par défaut, lorsque vous initialisez un buffer avec alloc(), le buffer est rempli de zéros binaires en guise d’emplacement pour les données ultérieures. Cependant, nous pouvons modifier la valeur par défaut si nous le souhaitons. Si nous voulions créer un nouveau buffer avec des 1 au lieu des 0, nous définirions le deuxième paramètre de la fonction alloc()fill.

      Dans votre terminal, créez un nouveau buffer à l’invite REPL qui est rempli de 1 :

      • const filledBuf = Buffer.alloc(1024, 1);

      Nous venons de créer un nouvel objet buffer qui fait référence à un espace en mémoire qui stocke 1 Ko de 1. Bien que nous ayons saisi un nombre entier, toutes les données stockées dans un buffer sont des données binaires.

      Les données binaires peuvent se présenter sous de nombreux formats différents. Considérons par exemple une séquence binaire représentant un octet de données : 01110110. Si cette séquence binaire représentait une chaîne en anglais utilisant la norme de codage ASCII, il s’agirait de la lettre v. Cependant, si notre ordinateur traitait une image, cette séquence binaire pourrait contenir des informations sur la couleur d’un pixel.

      L’ordinateur sait les traiter différemment parce que les octets sont codés différemment. L’encodage des octets est le format de l’octet. Un buffer dans Node.js utilise le schéma d’encodage UTF-8 par défaut s’il est initialisé avec des données de chaîne. Un octet en UTF-8 représente un nombre, une lettre (en anglais et dans d’autres langues) ou un symbole. L’UTF-8 est un superset de l’ASCII, le code standard américain pour l’échange d’informations. L’ASCII peut coder des octets avec des lettres anglaises majuscules et minuscules, les chiffres 0-9, et quelques autres symboles comme le point d’exclamation (!) ou le signe esperluette (&).

      Si nous écrivions un programme qui ne pourrait fonctionner qu’avec des caractères ASCII, nous pourrions modifier l’encodage utilisé par notre buffer avec le troisième argument de la fonction alloc()encoding.

      Créons un nouveau buffer de cinq octets de long qui ne stocke que des caractères ASCII :

      • const asciiBuf = Buffer.alloc(5, 'a', 'ascii');

      Le buffer est initialisé avec cinq octets du caractère a, en utilisant la représentation ASCII.

      Remarque : par défaut, Node.js prend en charge les codages de caractères suivants :

      • ASCII, représenté sous le nom ascii
      • UTF-8, représenté sous le nom utf-8 ou utf8
      • UTF-16, représenté sous le nom utf-16le ou utf16le
      • UCS-2, représentée sous le nom ucs-2 ou ucs2
      • Base64, représentée sous le nom base64
      • Hexadecimal, représenté sous le nom hex
      • ISO/IEC 8859-1, représenté sous le nom latin1 ou binary

      Toutes ces valeurs peuvent être utilisées dans les fonctions de la classe Buffer qui acceptent un paramètre encoding. Par conséquent, ces valeurs sont toutes valables pour la méthode alloc().

      Jusqu’à présent, nous avons créé de nouveaux buffers avec la fonction alloc(). Mais nous pouvons parfois vouloir créer un buffer à partir de données qui existent déjà, comme une chaîne ou un tableau.

      Pour créer un buffer à partir de données pré-existantes, nous utilisons la méthode from(). Nous pouvons utiliser cette fonction pour créer des buffers à partir de :

      • Un tableau d’entiers : les valeurs des entiers peuvent être comprises entre 0 et 255.
      • Un ArrayBuffer : c’est un objet JavaScript qui stocke une longueur fixe d’octets.
      • Une chaîne.
      • Un autre buffer.
      • D’autres objets JavaScript qui ont une propriété Symbol.toPrimitive. Cette propriété indique à JavaScript comment convertir l’objet en un type de données primitives : boolean, null, undefined, number, string, or symbol. Vous pouvez en savoir plus sur les symboles en consultant la documentation JavaScript de Mozilla.

      Voyons comment nous pouvons créer un buffer à partir d’une chaîne. Dans l’invite Node.js, saisissez ceci :

      • const stringBuf = Buffer.from('My name is Paul');

      Nous avons maintenant un objet buffer créé à partir de la chaîne de caractères My name is Paul. Créons un nouveau buffer à partir d’un autre buffer que nous avons créé précédemment :

      • const asciiCopy = Buffer.from(asciiBuf);

      Nous avons maintenant créé un nouveau buffer asciiCopy qui contient les mêmes données que asciiBuf.

      Maintenant que nous avons fait l’expérience de la création de buffers, nous pouvons nous plonger dans des exemples de lecture de leurs données.

      Étape 2 — Lecture à partir d’un buffer

      Il existe de nombreuses façons d’accéder aux données dans un buffer. Nous pouvons accéder à un octet individuel dans un buffer ou nous pouvons extraire le contenu entier.

      Pour accéder à un octet d’un buffer, nous passons l’index ou l’emplacement de l’octet que nous voulons. Les buffers stockent les données de manière séquentielle comme des tableaux. Ils indexent également leurs données comme des tableaux, à partir de 0. Nous pouvons utiliser la notation de tableau sur l’objet buffer pour obtenir un octet individuel.

      Voyons à quoi cela ressemble en créant un buffer à partir d’une chaîne dans le REPL :

      • const hiBuf = Buffer.from('Hi!');

      Maintenant, lisons le premier octet du buffer :

      Lorsque vous appuyez sur ENTER, le REPL affiche :

      Output

      72

      L’entier 72 correspond à la représentation UTF-8 pour la lettre H.

      Remarque : les valeurs pour les octets peuvent être des nombres entre 0 et 255. Un octet est une séquence de 8 bits. Un bit est binaire, et ne peut donc avoir qu’une seule des deux valeurs : 0 ou 1. Si nous avons une séquence de 8 bits et deux valeurs possibles par bit, alors nous avons un maximum de 2⁸ valeurs possibles pour un octet. Cela correspond à un maximum de 256 valeurs. Comme nous commençons à compter à partir de zéro, cela signifie que notre nombre le plus élevé est 255.

      Faisons de même pour le deuxième octet. Entrez ce qui suit dans le REPL :

      Le REPL renvoie 105, qui représente le i minuscule.

      Enfin, obtenons le troisième caractère :

      Vous verrez 33 affiché dans le REPL, ce qui correspond à !

      Essayons de récupérer un octet d’un index non valide :

      Le REPL renverra :

      Output

      undefined

      C’est comme si nous essayions d’accéder à un élément d’un tableau avec un index incorrect.

      Maintenant que nous avons vu comment lire les octets individuels d’un buffer, voyons nos options pour récupérer en une seule fois toutes les données stockées dans un buffer. L’objet buffer est doté des méthodes toString() et toJSON(), qui renvoient l’intégralité du contenu d’un buffer dans deux formats différents.

      Comme son nom l’indique, la méthode toString() convertit les octets du buffer en une chaîne de caractères et la renvoie à l’utilisateur. Si nous utilisons cette méthode sur hiBuf, nous obtiendrons la chaîne Hi!. Essayons !

      Dans l’invite, saisissez :

      Le REPL renverra :

      Output

      'Hi!'

      Ce buffer a été créé à partir d’une chaîne. Voyons ce qui se passe si nous utilisons la fonction toString() sur un buffer qui n’a pas été créé à partir de données de chaîne.

      Créons un nouveau buffer vide d’une taille de 10 octets :

      • const tenZeroes = Buffer.alloc(10);

      Maintenant, utilisons la méthode toString() :

      Nous verrons le résultat suivant :

      'u0000u0000u0000u0000u0000u0000u0000u0000u0000u0000'
      

      La chaîne u0000 est le caractère Unicode pour NULL. Il correspond au nombre 0. Lorsque les données du buffer ne sont pas encodées sous forme de chaîne, la méthode toString() renvoie l’encodage UTF-8 des octets.

      La toString() a un paramètre optionnel, encoding. Nous pouvons utiliser ce paramètre pour modifier l’encodage des données du buffer qui sont renvoyées.

      Par exemple, si vous voulez l’encodage hexadecimal pour hiBuf, vous devez entrer ce qui suit à l’invite :

      Cette instruction évaluera :

      Output

      '486921'

      486921 est la représentation hexadécimale des octets qui représentent la chaîne Hi! Dans Node.js, lorsque les utilisateurs veulent convertir l’encodage des données d’un formulaire à un autre, ils mettent généralement la chaîne dans un buffer et appellent toString() avec l’encodage souhaité.

      La méthode toJSON() se comporte différemment. Que le buffer ait été fait à partir d’une chaîne ou non, il renvoie toujours les données sous forme de représentation entière de l’octet.

      Réutilisons les buffers hiBuf et tenZeroes pour nous entraîner à utiliser toJSON(). Dans l’invite, saisissez :

      Le REPL renverra :

      Output

      { type: 'Buffer', data: [ 72, 105, 33 ] }

      L’objet JSON a une propriété type qui sera toujours Buffer. C’est ainsi que les programmes peuvent distinguer ces objets JSON des autres objets JSON.

      La propriété data contient un tableau de la représentation entière des octets. Vous avez peut-être remarqué que 72, 105 et 33 correspondent aux valeurs que nous avons reçues lorsque nous avons tiré les octets individuellement.

      Essayons la méthode toJSON() avec tenZeroes :

      Dans le REPL, vous verrez ce qui suit :

      Output

      { type: 'Buffer', data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }

      Le type est le même que celui indiqué précédemment. Cependant, les données sont maintenant un tableau avec dix zéros.

      Maintenant que nous avons couvert les principales façons de lire à partir d’un buffer, voyons comment nous modifions le contenu d’un buffer.

      Étape 3 — Modification d’un buffer

      Il existe de nombreuses façons de modifier un objet buffer existant. Comme pour la lecture, nous pouvons modifier individuellement les octets du buffer en utilisant la syntaxe du tableau. Nous pouvons également écrire de nouveaux contenus dans un buffer, en remplacement des données existantes.

      Commençons par examiner comment nous pouvons modifier les octets individuels d’un buffer. Rappelons notre variable tampon hiBuf, qui contient la chaîne de caractères Hi!. Changeons chaque octet pour qu’elle contienne plutôt Hey.

      Dans le REPL, essayons d’abord de remplacer le deuxième élément de hiBuf par e :

      Voyons maintenant ce buffer comme une chaîne de caractères pour confirmer qu’il stocke les bonnes données. Poursuivez en appelant la méthode toString() :

      Il sera évalué comme :

      Output

      'Hu0000!'

      Nous avons reçu cette étrange sortie parce que le buffer ne peut accepter qu’une valeur entière. On ne peut pas l’attribuer à la lettre e ; il faut plutôt lui attribuer le nombre dont l’équivalent binaire représente e :

      Maintenant, lorsque nous appelons la méthode toString() :

      Nous obtenons cette sortie dans le REPL :

      Output

      'He!'

      Pour modifier le dernier caractère du buffer, nous devons fixer le troisième élément au nombre entier qui correspond à l’octet pour y :

      Confirmez en utilisant à nouveau la méthode toString() :

      Votre REPL affichera :

      Output

      'Hey'

      Si nous essayons d’écrire un octet qui est en dehors de la plage du buffer, il sera ignoré et le contenu du buffer ne changera pas. Essayons, par exemple, de régler le quatrième élément inexistant du buffer sur o :

      Nous pouvons confirmer que le buffer est inchangé avec la méthode toString() :

      La sortie est toujours :

      Output

      'Hey'

      Si nous voulons modifier le contenu de l’ensemble du buffer, nous pouvons utiliser la méthode write(). La méthode write() accepte une chaîne de caractères qui remplacera le contenu d’un buffer.

      Utilisons la méthode write() pour modifier le contenu de hiBuf et revenir à Hi!. Dans votre shell Node.js, tapez la commande suivante à l’invite :

      La méthode write() a renvoyé 3 dans le REPL. C’est parce qu’il a écrit trois octets de données. Chaque lettre a la taille d’un octet, puisque ce buffer utilise le codage UTF-8, qui utilise un octet pour chaque caractère. Si le buffer avait utilisé le codage UTF-16, qui comporte un minimum de deux octets par caractère, la fonction write() aurait renvoyé 6.

      Maintenant, vérifiez le contenu du buffer en utilisant toString() :

      Le REPL produira :

      Output

      'Hi!'

      Cette technique est plus rapide que de devoir modifier chaque élément octet par octet.

      Si vous essayez d’écrire plus d’octets que la taille d’un buffer, l’objet buffer n’acceptera que le nombre d’octets qu’il peut recevoir. Pour illustrer cela, créons un buffer qui stocke trois octets :

      • const petBuf = Buffer.alloc(3);

      Essayons maintenant d’y écrire Cats :

      Lorsque l’appel write() est évalué, le REPL renvoie 3, indiquant que seuls trois octets ont été écrits dans le buffer. Confirmez maintenant que le buffer contient les trois premiers octets :

      Le REPL renvoie :

      Output

      'Cat'

      La fonction write() ajoute les octets dans un ordre séquentiel, de sorte que seuls les trois premiers octets ont été placés dans le buffer.

      En revanche, faisons un Buffer qui stocke quatre octets :

      • const petBuf2 = Buffer.alloc(4);

      Ecrivez-lui le même contenu :

      Ensuite, ajoutez un nouveau contenu qui occupe moins d’espace que le contenu original :

      Puisque les buffers s’écrivent séquentiellement en partant de 0, si nous imprimions le contenu du buffer :

      Nous verrions :

      Output

      'Hits'

      Les deux premiers caractères sont écrasés, mais le reste du buffer est intact.

      Parfois, les données que nous voulons dans notre buffer préexistant ne sont pas dans une chaîne mais résident dans un autre objet buffer. Dans ces cas, nous pouvons utiliser la fonction copy() pour modifier ce que notre buffer stocke.

      Créons deux nouveaux buffers :

      • const wordsBuf = Buffer.from('Banana Nananana');
      • const catchphraseBuf = Buffer.from('Not sure Turtle!');

      Les buffers wordsBuf et catchphraseBuf contiennent tous deux des données de chaîne. Nous voulons modifier catchphraseBuf pour qu’il stocke Nananana Turtle! au lieu de Not sure Turtle! . Nous utiliserons copy() pour faire passer Nananana de wordsBuf à catchphraseBuf.

      Pour copier des données d’un buffer à l’autre, nous utiliserons la méthode copy() sur le buffer qui est la source de l’information. Par conséquent, comme wordsBuf possède les données des chaînes de caractères que nous voulons copier, nous devons copier comme ceci :

      • wordsBuf.copy(catchphraseBuf);

      Dans ce cas, le paramètre target est le buffer catchphraseBuf.

      Lorsque nous entrons cela dans le REPL, il renvoie 15 indiquant que 15 octets ont été écrits. La chaîne Nananana n’utilise que 8 octets de données, nous savons donc immédiatement que notre copie ne s’est pas déroulée comme prévu. Utilisez la méthode toString() pour voir le contenu de catchphraseBuf :

      • catchphraseBuf.toString();

      Le REPL renvoie :

      Output

      'Banana Nananana!'

      Par défaut, copy() a pris tout le contenu de wordsBuf et l’a placé dans catchphraseBuf. Nous devons être plus sélectifs dans notre objectif et ne copier que Nananana. Réécrivons le contenu original de catchphraseBuf avant de continuer :

      • catchphraseBuf.write('Not sure Turtle!');

      La fonction copy() dispose de quelques paramètres supplémentaires qui nous permettent de personnaliser les données qui sont copiées dans l’autre buffer. Voici une liste de tous les paramètres de cette fonction :

      • target – C’est le seul paramètre requis de copy(). Comme nous l’avons vu lors de notre précédente utilisation, il s’agit du buffer vers lequel nous voulons copier.
      • targetStart – Il s’agit de l’index des octets du buffer cible vers lequel nous devons commencer la copie. Par défaut, il est de 0, ce qui signifie qu’il copie les données à partir du début du buffer.
      • sourceStart – C’est l’index des octets du buffer source où nous devons copier.
      • sourceEnd – C’est l’index des octets dans le buffer source où nous devons arrêter la copie. Par défaut, il s’agit de la longueur du buffer.

      Donc, pour copier Nananana de wordsBuf à catchphraseBuf, notre target devrait être catchphraseBuf comme avant. targetStart serait 0, car nous voulons que Nananana apparaisse au début de catchphraseBuf. Le sourceStart devrait être 7, car c’est l’indice où commence Nananana dans wordsBuf. Le sourceEnd continuerait à être la longueur des buffers.

      À l’invite du REPL, copiez le contenu de wordsBuf comme ceci :

      • wordsBuf.copy(catchphraseBuf, 0, 7, wordsBuf.length);

      Le REPL confirme que 8 octets ont été écrits. Notez comment wordsBuf.length est utilisé comme valeur pour le paramètre sourceEnd. Comme pour les tableaux, la propriété length nous donne la taille du buffer.

      Voyons maintenant le contenu de catchphraseBuf :

      • catchphraseBuf.toString();

      Le REPL renvoie :

      Output

      'Nananana Turtle!'

      Bravo! Nous avons pu modifier les données de catchphraseBuf en copiant le contenu de wordsBuf.

      Vous pouvez quitter le Node.js REPL si vous le souhaitez. Notez que toutes les variables qui ont été créées ne seront plus disponibles lorsque vous le ferez :

      Conclusion

      Dans ce tutoriel, vous avez appris que les buffers sont des allocations de longueur fixe en mémoire qui stockent des données binaires. Vous avez d’abord créé des buffers en définissant leur taille en mémoire et en les initialisant avec des données pré-existantes. Vous avez ensuite lu les données d’un buffer en examinant leurs octets individuels et en utilisant les méthodes toString() et toJSON(). Enfin, vous avez modifié les données stockées par un buffer en changeant ses octets individuels et en utilisant les méthodes write() et copy().

      Les buffers vous donnent un bon aperçu de la façon dont les données binaires sont manipulées par Node.js. Maintenant que vous pouvez interagir avec les buffers, vous pouvez observer les différentes façons dont l’encodage des caractères affecte la façon dont les données sont stockées. Par exemple, vous pouvez créer des buffers à partir de données de chaînes qui ne sont pas codées en UTF-8 ou ASCII et observer leur différence de taille. Vous pouvez également choisir un buffer avec UTF-8 et utiliser toString() pour le convertir en d’autres schémas d’encodage.

      Pour en savoir plus sur les buffers dans Node.js, vous pouvez lire la documentation de Node.js sur l’objet Buffer. Si vous souhaitez continuer à apprendre Node.js, vous pouvez revenir à la série Comment coder dans Node.js, ou parcourir les projets de programmation et les configurations sur notre page thématique Node.



      Source link