Parser un fichier de log de simulation
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 21 oct. 2024 à 13:23
- Parser un fichier de log de simulation
- Fichier rar - Guide
- Comment ouvrir un fichier epub ? - Guide
- Comment réduire la taille d'un fichier - Guide
- Ouvrir un fichier .bin - Guide
- Fichier host - Guide
1 réponse
Modifié le 18 oct. 2024 à 15:19
Bonjour,
Que veux-tu extraire ? Et à quelle fin ? En outre, n'as tu pas meilleur temps de faire ce que tu veux faire directement dans ton simulateur et en C++ (je suspecte qu'il s'agit d'un log produit avec ns3) ?
Voici un exemple de parseur de log que tu peux adapter. L'idée est d'utiliser des expressions régulières afin de détecter les lignes qui contiennent des données à extraire et peupler une structure de données.
#!/usr/bin/env python3 import argparse import re import sys from pathlib import Path from pprint import pprint RE_INT = "[0-9]+" RE_FLOAT = f"{RE_INT}.{RE_INT}" MAP_REGEXPS = { "duration": re.compile(rf"Duration: ({RE_FLOAT})ms"), "real time factor": re.compile(rf"Real time factor: ({RE_FLOAT})"), "inserted vehicles": re.compile(rf"Inserted: ({RE_INT}) \(Loaded: ({RE_INT})\)"), } def smart_cast(x, types = None): if types is None: types = (int, float) for cast in types: try: return cast(x) except ValueError: pass return x def parse_line(line, r): m = r.match(line) if m: num_values = r.groups if num_values > 1: return tuple( smart_cast(m.group(i + 1)) for i in range(r.groups) ) elif num_values == 1: return smart_cast(m.group(1)) return None def parse_log(f): data = dict() for line in f.readlines(): line = line.strip() for (k, r) in MAP_REGEXPS.items(): values = parse_line(line, r) if values is not None: data[k] = values return data def main(): parser = argparse.ArgumentParser( description="Log parser", epilog=f"Example: {sys.argv[0]} -i file.log" ) g = parser.add_argument_group("Parser arguments") g.add_argument( "-i", "--input-filename", type=Path, required=True, help="Path to the input log" ) args = parser.parse_args() with open(args.input_filename) as f: data = parse_log(f) pprint(data) if __name__ == "__main__": main()
Comme tu peux le voir dans MAP_REGEXPS, je me suis contenté de traiter ces trois lignes :
... Duration: 938ms Real time factor: 5.33049 ... Inserted: 10 (Loaded: 20)
Libre à toi de traiter d'autres lignes.
Exécution :
python3 parse_log.py fichier.log
Résultat :
{'duration': 938, 'inserted vehicles': (10, 20), 'real time factor': 5.33049}
Quelques explications sur les expressions régulières
Pour comprendre ce qui se passe, il faut déjà maîtriser ce qu'est une expression régulière et comment s'utilise le module python "re", qui les prend en charge.
Une expression régulière permet d'extraire une ou plusieurs sous chaînes (appelés matches) étant donnée une chaîne de caractère (par exemple une ligne de fichier de log). Dans une expression régulière, les sous-chaines en questions sont délimités par des parenthèses. Si la chaîne de caractère comporte de "vraies parenthèses" (comme dans "inserted vehicles"), il faut veiller à échapper ces "vraies parenthèses" en les précédant d'un "\".
Par exemple regardons quelques-unes d'entre elles :
- RE_INT :
- [0-9] signifie n'importe quel caractère compris entre 0 et 9 au sens ASCII, ce qui concerne 0, 1, 2, ... 9. Dans la même veine [A-Za-z] signifie n'importe quelle lettre minuscule ou majuscule.
- + signifie au moins une répétition. Donc [0-9]+ signifie qu'on veut une suite non vide de caractères consécutifs entre 0 et 9. Cela définit donc du point de vue d'une chaîne de caractère bien n'importe quel entier.
- Il existe d'autres opérateurs de répétitions :
- ? : 0-1 répétitions
- + : 1 ou plus répétitions
- * : 0 ou plus répétitions
- {m} : exactement m répétitions
- {m,} : au moins m répétitions
- {,n} : au plus n répétitions
- {m, n}: entre m et n répétitions
- Comme {, }, +, *, ? mais aussi (, ), et . sont des métacaractère (~opérateurs) des expressions régulières, lorsqu'une expression régulière met en jeu un tel caractère qui ne doit pas être vu comme métacaractère doit être échappé, soit en le précédent de \, soit en l'entourant avec des crochet. Exemple :
- \. et [.] désignent le caractère "."
- Comme on est en train de définir une expressions régulières, on a intérêt à utiliser r"..."
- MAP_REGEXPS["duration"] :
- Comme on cherche ici à capturer un entier, on va réutiliser l'expression régulière RE_INT. Les f-strings f"..." permettent d'injecter la valeur d'une variable dans une chaîne, comme par exemple dans f"...{RE_INT}...".
- Comme c'est une expression régulière, on définit cette chaîne avec rf"...".
- Et comme on veut capturer l'entier en question, l'expression régulière est de la forme rf"...({RE_INT})...".
- MAP_REGEXPS["inserted vehicles"] :
- C'est le même principe, mais on a deux entiers à extraire donc l'expression régulière est de la forme l'expression régulière est de la forme rf"...({RE_INT})...({RE_INT})..."
- Comme notre expression régulière implique de "vraies parenthèses" il ne faut pas oublier de les échapper.
Quelques explications sur l'architecture du code
La fonction parse_log regarde pour chaque ligne et pour chaque expression régulière si l'une d'elle est capable d'extraire des valeurs. Si c'est le cas, les valeurs extraites sont mémorisés. Comme une expression régulière peut capturer plusieurs valeurs, il faut distinguer si on en extrait une ou plusieurs. Dans le premier cas, on retourne directement la valeur, dans l'autre, on stocke les valeurs dans un tuple. On observe cette différence de traitement dans le cas de "duration" et de "inserted vehicles".
Ces éventuelles sous-chaines sont par définition du texte. Et donc, si l'on souhaite avoir un type numérique (par exemple en vue de tracer des courbes), il faut les convertir dans le type adéquat (nombre entier, nombre flottant, etc). C'est le rôle de la fonction smart_cast.
Ensuite, toutes ces fonctions sont orchestrées par un programme principal (fonction main) qui s'occupe de récupérer les arguments passés en ligne de commande et d'appeler la fonction parse_log.
Bonne chance
Modifié le 18 oct. 2024 à 14:55
Oui c'est un log produit par ns-3 .
Je veux extraire les temps d'envoi des intérêts (Interest) et les temps de réception de chaque data équivalent (data) , ainsi que les IDs des stations (car) qui ont transmis ces messages , afin de calculer le délai de transmission pour chaque message.
21 oct. 2024 à 13:23
Peux-tu clarifier ? Donne un exemple de ligne à considérer et ce que tu veux en extraire. As-tu essayé par toi-même sur l'extrait de code que je t'ai proposé ?