[*]

awk est une sorte de couteau suisse pour les fichiers texte. Cependant, certaines de ses limitations sont souvent un peu ennuyeuses. J’ai utilisé un ensemble simple de fonctions pour créer awk un peu mieux, bien que je vous prévienne: il nécessite des extensions GNU pour awk. Autrement dit, vous devez utiliser gawk et pas d’autres versions. Votre système mappe probablement /usr/bin/awk à quelque chose et que quelque chose pourrait être gawk. Mais ça pourrait aussi être mawk ou une autre saveur. Si vous utilisez une distribution basée sur Debian, update-alternatives est votre ami ici. Mais aux fins de cet article, je vais supposer que vous utilisez gawk.

À la fin de l’article, vous verrez comment utiliser mon awk fonctions complémentaires pour diviser une ligne en champs même s’il n’y a pas de caractère unique pour séparer tous les champs. De plus, vous pourrez vous référer aux champs en utilisant les noms que vous décidez. Vous n’aurez pas à vous rappeler que $ 2 est le champ de temps. Tu diras Fields_fields["time"] au lieu.

Le problème

awk fait beaucoup de travail courant pour vous lorsque vous l’utilisez pour traiter des fichiers texte. Il lit les fichiers un enregistrement à la fois. Normalement, un enregistrement est une seule ligne. Ensuite, il divise la ligne sur les champs en utilisant des espaces ou un autre choix de séparateurs de champs. Vous pouvez écrire du code qui manipule la ligne ou des champs individuels. Ce comportement par défaut est excellent, d’autant plus que vous pouvez modifier le caractère de fin d’enregistrement et le séparateur de champ. Un nombre surprenant de fichiers correspond à ce type de format.

Jusqu’à ce que, bien sûr, ils ne le font pas. Si vous avez des données provenant d’un instrument d’enregistrement de données ou d’une base de données, elles peuvent être formatées de différentes manières. Certains champs peuvent avoir des données structurées avec une variété de séparateurs. Ce n’est pas un facteur décisif. Puisque vous pouvez accéder à toute la ligne, vous pouvez faire presque tout ce que vous voulez, mais la logique est plus difficile et tout l’intérêt d’utiliser awk est de rendre les choses plus faciles.

Par exemple, supposons que vous ayez un fichier d’un enregistreur de données comportant un numéro de série à huit chiffres, suivi d’une balise à six caractères, puis de deux nombres à virgule flottante séparés par des deux-points. Le motif pourrait ressembler à


^([0-9]{8})([a-zA-Z0-9]{6})([-+.0-9]+),([-+.0-9]+)$

Ce serait difficile à gérer avec le fractionnement de champ conventionnel et vous écririez normalement du code pour tout séparer.

Si vous avez des champs réguliers, mais que vous ne savez pas combien, vous voudrez probablement définir FS ou FPAT, au lieu. Nous avons parlé de FPAT un peu avant quand on abusait awk pour lire les fichiers hexadécimaux. Cette bibliothèque est un peu différente. Vous pouvez l’utiliser pour séparer totalement une ligne. Par exemple, vous pouvez avoir une partie de la ligne avec une longueur de champ fixe, puis plusieurs types de séparateurs. Cela peut être difficile à gérer avec les autres méthodes.

Expressions régulières

Pour faciliter les choses, je vais conclure le gawk match une fonction. Cette fonction existe en awk, bien sûr, mais gawk ajoute une extension qui rend les choses beaucoup plus faciles. Normalement, la fonction effectue une correspondance d’expression régulière sur une chaîne et vous indique où commence la correspondance, s’il y a une correspondance et combien de caractères correspondent.

Avec les extensions GNU dans gawk, vous pouvez fournir un argument de tableau supplémentaire. Ce tableau obtiendra des informations sur la correspondance. En particulier, l’élément zéro du tableau contiendra toute la correspondance. Si l’expression régulière contient des sous-expressions entre parenthèses, le tableau les contiendra, numérotées dans l’ordre des parenthèses. Il contiendra également des informations de début et de longueur.

Par exemple, si votre expression régulière était "^([0-9]+)([a-z]+)$" et votre chaîne d’entrée est 123abc, le tableau ressemblerait à ceci:



array[0] - 123abc
array[1] - 123
array[2] - abc
array[0start] - 1
array[0length] - 6
array[1start] - 1
array[1length] - 3
array[2start] - 4
array[2length] - 3


Vous pouvez même avoir des expressions imbriquées, donc "^(([xyz])[0-9]+)([a-z]+)$" avec une entrée de z1x donne array[1]=z1, array[2]=z, et array[3]=x.

Théorie vs pratique

En théorie, c’est tout ce dont vous avez besoin. Vous pouvez écrire une expression régulière pour séparer une ligne, l’analyser, puis accéder aux pièces à l’aide du tableau. En pratique, il est beaucoup plus agréable de tout faire pour que vous puissiez utiliser des noms simples pour accéder aux données.

Comme exemple de format de données, considérez une ligne comme celle-ci:



11/10/2020 07:00 The Best of Bradbury, 14.95 *****


Il y a une date au format américain, une heure au format 24 heures, un nom d’article, un prix et une note de 1 à 5 étoiles qui peuvent ne pas être présentes. Ecrire une expression régulière pour saisir chaque champ est un peu complexe, mais pas très difficile. Voici une façon de le faire:



"^(([01][0-9])/([0-3][0-9])/(2[01][0-9][0-9]))[[:space:]]*(([0-2][0-9]):([0-5][0-9]))[[:space:]]+([^,]+),[[:space:]]*([0-9.]+)[[:space:]]*([*]{1,5})?[[:space:]]*$"


C’est une bouchée, mais ça marche. Notez que chaque élément est entre parenthèses et certains d’entre eux sont imbriqués. La date est donc un champ, mais le mois, le jour et l’année sont également des champs.

La bibliothèque

Une fois que vous avez récupéré les fichiers sur GitHub, vous pouvez mettre les fonctions fields_ * dans votre code. Vous devez effectuer une configuration dans la balise BEGIN. Ensuite, vous traitez chaque ligne à l’aide de fields_process. Voici un petit exemple (avec les fonctions omises):



BEGIN {
fields_setup("^(([01][0-9])/([0-3][0-9])/(2[01][0-9][0-9]))[[:space:]]*(([0-2][0-9]):([0-5][0-9]))[[:space:]]+([^,]+),     [[:space:]]*([0-9.]+)[[:space:]]*([*]{1,5})?[[:space:]]*$")
fields_setupN(1,"date")
fields_setupN(2,"month")
fields_setupN(3,"day")
fields_setupN(4,"year")
fields_setupN(5,"time")
fields_setupN(6,"hours")
fields_setupN(7,"minutes")
fields_setupN(8,"item")
fields_setupN(9,"price")
fields_setupN(10,"star")

}

{
v=fields_process()

... your code here...

}

Dans votre code, vous pouvez écrire quelque chose comme:



cost=Fields_fields["price"] * 3


Simple, non? le fields_process La fonction renvoie false s’il n’y a pas de correspondance. Vous pouvez toujours accéder à la normale awk des champs comme 0 $ ou 2 $ si vous le souhaitez.

À l’intérieur

Les fonctions supplémentaires reposent sur deux choses: les extensions du gawk match fonction et awkmécanisme de tableau associatif de. Dans le passé, j’ai ajouté les clés nommées au tableau de correspondance existant afin que vous puissiez obtenir des données dans les deux sens. Cependant, je l’ai modifié pour que le tableau de correspondance soit local car je ne veux presque jamais vraiment de cette capacité et vous devez ensuite filtrer les champs supplémentaires si vous voulez vider le tableau entier.

Il est souvent utile de démarrer l’expression régulière avec ^ et terminer avec $ pour ancrer toute la chaîne. N’oubliez pas que l’expression régulière doit gérer la consommation d’espace blanc, comme le fait l’exemple. C’est souvent un avantage lorsque vous avez des champs qui peuvent contenir des espaces, mais si vous vouliez quand même que les espaces cassent les champs, vous feriez probablement mieux d’utiliser le schéma d’analyse d’origine.

Une autre astuce consiste à obtenir «le reste de la ligne» après avoir analysé les premiers champs. Vous pouvez le faire en ajoutant "(.*)$" à la fin de l’expression régulière. N’oubliez pas de configurer une balise pour cela en utilisant fields_setupN afin que vous puissiez récupérer la valeur plus tard.

Une extension simple de cette bibliothèque serait de faire du motif un tableau. La fonction de traitement pourrait essayer chaque motif à son tour jusqu’à ce qu’il corresponde. Ensuite, il renverrait l’index du modèle correspondant ou false s’il n’y avait pas de correspondance. Cela vous permettrait de définir plusieurs types de lignes si vous aviez un format de fichier complexe. Vous voudrez probablement également avoir différents ensembles de balises de champ pour chacun d’eux.

J’ai une longue histoire d’abus d’outils comme awk pour faire des choses, comme créer des assembleurs croisés. Même ainsi, je ne suis probablement pas le pire délinquant.

LAISSER UN COMMENTAIRE

Rédigez votre commentaire !
Entrez votre nom ici