Traitement de texte avec Python

Contexte

Au travail, j'utilise souvent Perl pour le traitement de texte. Python n'a été utilisé que comme outil pour le traitement io de Raspberry Pi. Ainsi, afin de savoir comment Python est conçu en tant que langage de programmation, j'ai décidé de créer un script de traitement de texte à titre d'essai.

Ce qui a été fait

Traitez le fichier journal suivant ligne par ligne et sortez-le correctement.

log.txt


date Thu Apr 11 04:41:25 pm 2013
base hex  timestamps absolute
internal events logged
// version 8.0.0
Begin Triggerblock Thu Apr 11 04:41:25 pm 2013
   0.000000 Start of measurement
   0.001316 CAN 1 Status:chip status error active
   0.001399 1  1F3             Rx   d 3 00 10 00  Length = 146000 BitCount = 77 ID = 499
   0.002763 1  1E5             Rx   d 8 4C 00 21 10 00 00 00 B9  Length = 228000 BitCount = 118 ID = 485
   0.003009 1  710             Rx   d 8 00 5F 00 00 00 00 13 BE  Length = 238000 BitCount = 123 ID = 1808
   0.003175 1  C7              Rx   d 4 00 38 26 9B  Length = 158000 BitCount = 83 ID = 199
   0.003349 1  1CC             Rx   d 4 00 00 00 00  Length = 165883 BitCount = 87 ID = 460
   0.003586 1  F9              Rx   d 8 00 DA 40 33 D0 63 FF 1C  Length = 228000 BitCount = 118 ID = 249
   0.003738 1  1CF             Rx   d 3 00 00 05  Length = 144000 BitCount = 76 ID = 463
   0.003976 1  711             Rx   d 8 00 23 00 7E FF EB FC 6F  Length = 230000 BitCount = 119 ID = 1809
   0.004148 1  1D0             Rx   d 4 00 00 00 00  Length = 164000 BitCount = 86 ID = 464
   0.004382 1  C1              Rx   d 8 30 14 F6 08 32 B4 F7 70  Length = 226000 BitCount = 117 ID = 193
   0.004615 1  C5              Rx   d 8 31 27 F8 44 32 B0 F8 5C  Length = 224121 BitCount = 116 ID = 197
   0.004825 1  BE              Rx   d 6 00 00 4D 00 00 00  Length = 202242 BitCount = 105 ID = 190
   0.005051 1  D1              Rx   d 7 80 00 BF FE 00 FE 00  Length = 218121 BitCount = 113 ID = 209
   0.005292 1  C9              Rx   d 8 80 2C 5A 60 00 00 18 00  Length = 232242 BitCount = 120 ID = 201
   0.005538 1  1C8             Rx   d 8 80 00 00 00 FF FE 3F FE  Length = 238121 BitCount = 123 ID = 456
   0.005774 1  18E             Rx   d 8 00 00 00 84 78 46 08 45  Length = 228242 BitCount = 118 ID = 398
#Afficher uniquement les champs obligatoires
$python canlogfilter.py log.txt                                                                                                                               0.001399 1 1F3 Rx 3 00 10 00
0.002763 1 1E5 Rx 8 4C 00 21 10 00 00 00 B9
0.003009 1 710 Rx 8 00 5F 00 00 00 00 13 BE
0.003175 1 0C7 Rx 4 00 38 26 9B
0.003349 1 1CC Rx 4 00 00 00 00
0.003586 1 0F9 Rx 8 00 DA 40 33 D0 63 FF 1C
0.003738 1 1CF Rx 3 00 00 05
0.003976 1 711 Rx 8 00 23 00 7E FF EB FC 6F
0.004148 1 1D0 Rx 4 00 00 00 00
0.004382 1 0C1 Rx 8 30 14 F6 08 32 B4 F7 70
0.004615 1 0C5 Rx 8 31 27 F8 44 32 B0 F8 5C
0.004825 1 0BE Rx 6 00 00 4D 00 00 00
0.005051 1 0D1 Rx 7 80 00 BF FE 00 FE 00
0.005292 1 0C9 Rx 8 80 2C 5A 60 00 00 18 00
0.005538 1 1C8 Rx 8 80 00 00 00 FF FE 3F FE
0.005774 1 18E Rx 8 00 00 00 84 78 46 08 45

#Sortie avec temps de différence supplémentaire
$python canlogfilter.py log.txt -d(-option d)                                                                                                                            0.001399 0.001399 1 1F3 Rx 3 00 10 00
0.001364 0.002763 1 1E5 Rx 8 4C 00 21 10 00 00 00 B9
0.000246 0.003009 1 710 Rx 8 00 5F 00 00 00 00 13 BE
0.000166 0.003175 1 0C7 Rx 4 00 38 26 9B
0.000174 0.003349 1 1CC Rx 4 00 00 00 00
0.000237 0.003586 1 0F9 Rx 8 00 DA 40 33 D0 63 FF 1C
0.000152 0.003738 1 1CF Rx 3 00 00 05
0.000238 0.003976 1 711 Rx 8 00 23 00 7E FF EB FC 6F
0.000172 0.004148 1 1D0 Rx 4 00 00 00 00
0.000234 0.004382 1 0C1 Rx 8 30 14 F6 08 32 B4 F7 70
0.000233 0.004615 1 0C5 Rx 8 31 27 F8 44 32 B0 F8 5C
0.000210 0.004825 1 0BE Rx 6 00 00 4D 00 00 00
0.000226 0.005051 1 0D1 Rx 7 80 00 BF FE 00 FE 00
0.000241 0.005292 1 0C9 Rx 8 80 2C 5A 60 00 00 18 00
0.000246 0.005538 1 1C8 Rx 8 80 00 00 00 FF FE 3F FE
0.000236 0.005774 1 18E Rx 8 00 00 00 84 78 46 08 45

#Sortie en réduisant les enregistrements en fonction d'une valeur de champ spécifique(-option u)
$python canlogfilter.py log.txt -u 710 0C9 18E                                                                                                                
0.003009 1 710 Rx 8 00 5F 00 00 00 00 13 BE
0.005292 1 0C9 Rx 8 80 2C 5A 60 00 00 18 00
0.005774 1 18E Rx 8 00 00 00 84 78 46 08 45

#Supprimer l'enregistrement et la sortie en fonction de la valeur de champ spécifique(-option o)
$python canlogfilter.py log.txt -o 710 0C9 18E                                                                                                                
0.001399 1 1F3 Rx 3 00 10 00
0.002763 1 1E5 Rx 8 4C 00 21 10 00 00 00 B9
0.003175 1 0C7 Rx 4 00 38 26 9B
0.003349 1 1CC Rx 4 00 00 00 00
0.003586 1 0F9 Rx 8 00 DA 40 33 D0 63 FF 1C
0.003738 1 1CF Rx 3 00 00 05
0.003976 1 711 Rx 8 00 23 00 7E FF EB FC 6F
0.004148 1 1D0 Rx 4 00 00 00 00
0.004382 1 0C1 Rx 8 30 14 F6 08 32 B4 F7 70
0.004615 1 0C5 Rx 8 31 27 F8 44 32 B0 F8 5C
0.004825 1 0BE Rx 6 00 00 4D 00 00 00
0.005051 1 0D1 Rx 7 80 00 BF FE 00 FE 00
0.005538 1 1C8 Rx 8 80 00 00 00 FF FE 3F FE

#Combinaison d'options(-u, -d)
$python canlogfilter.py log.txt -u 710 0C9 18E -d                                                                                                             
0.003009 0.003009 1 710 Rx 8 00 5F 00 00 00 00 13 BE
0.002283 0.005292 1 0C9 Rx 8 80 2C 5A 60 00 00 18 00
0.000482 0.005774 1 18E Rx 8 00 00 00 84 78 46 08 45

Code source

canlogfilter.py


import re
import argparse

class Record:
    def __init__(self):
        self.crtime   = 0.00000
        self.ch       = 1
        self.hexid    = 0x000
        self.dir      = "Rx"
        self.stat     = "d"
        self.dlc      = 0
        self.data     = []
        self.length   = 0
        self.bitcount = 0
        self.decid    = 0

def main():
    parser = argparse.ArgumentParser(description = 'CanlogFilter')

    parser.add_argument('inputFile',        help = 'Input file path')
    parser.add_argument('--difftime', '-d', action = 'store_const', const = True, default = False,  help = 'Print with difftime')
    parser.add_argument('--pickup',   '-u', nargs = '*', help = 'pick up records')
    parser.add_argument('--dropoff',  '-o', nargs = '*', help = 'drop off records')
    args = parser.parse_args()

    canlog = []
    canlog = parse(args.inputFile)

    if args.pickup != None and args.dropoff != None:
        print "--pickup and --dropoff, both provide"
        return -1
    elif args.pickup != None:
        canlog = pick_log(canlog, map(lambda x:int(x, 16), args.pickup))
    elif args.dropoff != None:
        canlog = drop_log(canlog, map(lambda x:int(x, 16), args.dropoff))

    if args.difftime == True:
        printlog_with_diff_time(canlog)
    else:
        printlog(canlog)

def parse(filename):
    canlog = []
    for line in open(filename, 'r'):
        fields = line.split()
        if re.match("1|2", fields[1]):
            rec = Record()
            rec.crtime   = float(fields[0])
            rec.ch       = int(fields[1], 10)
            rec.hexid    = int(fields[2], 16)
            rec.dir      = fields[3]
            rec.stat     = fields[4]
            rec.dlc      = int(fields[5], 10)
            rec.data     = map(lambda x:int(x, 16), fields[6:rec.dlc+6])
            rec.length   = int(fields[rec.dlc+8], 10)
            rec.bitcount = int(fields[rec.dlc+11], 10)
            rec.decid    = int(fields[rec.dlc+14], 10)
            canlog.append(rec)
    return canlog

def pick_log(canlog, ids):
    ret = []
    for rec in canlog:
        if rec.hexid in ids:
            ret.append(rec)
    return ret

def drop_log(canlog, ids):
    ret = []
    for rec in canlog:
        if not rec.hexid in ids:
            ret.append(rec)
    return ret

def printlog(canlog):
    for rec in canlog:
        print '%f %d %03X %s %d' % (rec.crtime, rec.ch, rec.hexid, rec.dir, rec.dlc),
        for byte in rec.data:
            print '%02X' % byte,
        print

def printlog_with_diff_time(canlog):
    prevtime = 0
    difftime = 0
    for rec in canlog:
        difftime = rec.crtime - prevtime
        print '%f %f %d %03X %s %d' % (difftime, rec.crtime, rec.ch, rec.hexid, rec.dir, rec.dlc),
        for byte in rec.data:
            print '%02X' % byte,
        print
        prevtime = rec.crtime

if __name__ == '__main__' : main()

Impression qu'il était facile à utiliser uniquement avec des produits standards. Les personnes qui écrivent plus peuvent en utiliser d'autres.

Résumé ・ TODO

Code source reçu des commentaires sur TODO

De shiracamus

Il semble que TODO envisage de revoir la définition de la classe, mais je l'ai implémentée à ma manière. J'espère que cela vous sera utile pour votre avis.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import argparse

def hexint(x):
    return int(x, 16)


class Record:

    @staticmethod
    def create(line):
        fields = line.split()
        if len(fields) < 2 or fields[1] not in ('1', '2'):
            return None

        record = Record()
        record.crtime = float(fields[0])
        record.ch = int(fields[1])
        record.hexid = hexint(fields[2])
        record.dir = fields[3]
        record.stat = fields[4]
        record.dlc = int(fields[5])
        record.data = map(hexint, fields[6:record.dlc + 6])
        record.length = int(fields[record.dlc + 8])
        record.bitcount = int(fields[record.dlc + 11])
        record.decid = int(fields[record.dlc + 14])
        return record

    def __str__(self):
        return ('{crtime} {ch} {hexid:03X} {dir} {dlc}'.format(**vars(self))
                + ' '.join('%02X' % byte for byte in self.data))


class Canlog:

    def __init__(self, records):
        self.records = list(records)

    @staticmethod
    def create(lines):
        return Canlog(record
                      for record in map(Record.create, lines)
                      if record != None)

    def pickup(this, ids):
        return Canlog(record
                      for record in this.records
                      if record.hexid in ids)

    def dropoff(canlog, ids):
        return Canlog(record
                      for record in self.records
                      if record.hexid not in ids)

    def print_without_diff_time(self):
        for record in self.records:
            print record

    def print_with_diff_time(self):
        prevtime = 0
        for record in self.records:
            difftime = record.crtime - prevtime
            print difftime, record
            prevtime = record.crtime


def main():
    parser = argparse.ArgumentParser(description='CanlogFilter')
    parser.add_argument('inputFile', help='Input file path')
    parser.add_argument('--difftime', '-d', action='store_const', const=True, default=False, help='Print with difftime')
    parser.add_argument('--pickup', '-u', nargs='*', help='pick up records')
    parser.add_argument('--dropoff', '-o', nargs='*', help='drop off records')

    args = parser.parse_args()
    if args.pickup != None and args.dropoff != None:
        print "--pickup and --dropoff, both provide"
        return -1

    with open(args.inputFile) as lines:
        canlog = Canlog.create(lines)

    if args.pickup != None:
        canlog = canlog.pickup(map(hexint, args.pickup))
    elif args.dropoff != None:
        canlog = canlog.dropoff(map(hexint, args.dropoff))

    if args.difftime == True:
        canlog.print_with_diff_time()
    else:
        canlog.print_without_diff_time()


if __name__ == '__main__':
    main()

Il a montré un exemple de la façon d'organiser une classe pour TODO. Non seulement cela, il était également très utile en tant que retrait détaillé et petite méthode de codage.

De knoguchi

J'utilise Python depuis longtemps. J'ai essayé de le réécrire un peu avec les fonctions de Python. Ce code est sur-conçu, mais pour votre référence. https://gist.github.com/knoguchi/4fc486a0cc39c1fd256d2fb6f619ee98

Options exclusives groupées avec argparse. Vous n'avez plus à vérifier avec if. Modifié pour convertir le type d'argument hexadécimal avec argparse. La valeur par défaut de Record est maintenant définie comme argument de mot-clé. Créer un objet Record avec une méthode de classe Puisque dlc est la longueur des données, je l'ai changé en propriété Remplacement des fonctions transmises par liste par des générateurs. La consommation de mémoire est réduite lors du traitement de gros volumes de journaux. Remplacement du traitement des champs par namedtuple. Vous pouvez désormais y accéder par nom, vous n'avez donc pas à réécrire les indices lorsque le champ> est ajouté au journal. Remplacement du filtre en utilisant la boucle for avec le filtre. Déplacement de l'affichage du contenu de l'enregistrement effectué à deux endroits vers la méthode str d'enregistrement. Le fichier est automatiquement fermé une fois le processus terminé à l'aide du gestionnaire de contexte. if x! = None peut être écrit comme si x. Idem pour si x == True. Utilisez is> pour vérifier explicitement None, True. Addendum: j'ai commenté sans remarquer le post de @ shiracamus, donc j'ai beaucoup souffert.

import re
import argparse
from collections import namedtuple


class Record:
    def __init__(self, crtime=0.00000, ch=1, hexid=0x000, dir="Rx", stat="d", data=None, length=0, bitcount=0, decid=0):
        self.crtime = crtime
        self.ch = ch
        self.hexid = hexid
        self.dir = dir
        self.stat = stat
        self.data = data or []
        self.length = length
        self.bitcount = bitcount
        self.decid = decid

    @property
    def dlc(self):
        return len(self.data)

    @classmethod
    def parse_from_file(cls, input_file):
        """
        The log format is fixed header fields, variable length data, ordered key-value pairs
        header fields: crtime, ch, hexid, dir, stat, dlc
        variable data: byte * dlc
        """
        HEADER_TYPES = (
            ('crtime', float),
            ('ch', int),
            ('hexid', lambda s: int(s, 16)),
            ('dir', str),
            ('stat', str),
            ('dlc', int),
        )
        HEADER_LENGTH = len(HEADER_TYPES)
        Header = namedtuple("Header", [field for field, _ in HEADER_TYPES])

        for line in input_file:
            fields = line.split()
            if not re.match("1|2", fields[1]):
                # ignore non-data rows
                continue

            # extract header
            header_values = fields[:HEADER_LENGTH]
            header_values = [func(value) for (field, func), value in zip(HEADER_TYPES, header_values)]
            header = Header(*header_values)

            # extract data
            data = map(lambda x: int(x, 16), fields[HEADER_LENGTH:][:header.dlc])

            # extract trailer
            length = fields[-7]
            bitcount = fields[-4]
            decid = fields[-1]

            yield cls(
                crtime=header.crtime,
                ch=header.ch,
                hexid=header.hexid,
                dir=header.dir,
                stat=header.stat,
                data=data,
                length=length,
                bitcount=bitcount,
                decid=decid
            )

    def __str__(self):
        return '%f %d %03X %s %d %s' % (
            self.crtime, self.ch, self.hexid, self.dir, self.dlc,
            ' '.join(["%02X" % byte for byte in self.data])
        )


def main():
    parser = argparse.ArgumentParser(description='CanlogFilter')

    parser.add_argument('inputFile', help='Input file path')
    parser.add_argument('--difftime', '-d', action='store_const', const=True, default=False, help='Print with difftime')

    group = parser.add_mutually_exclusive_group()
    group.add_argument('--pickup', '-u', nargs='*', type=lambda x: int(x, 16), help='pick up records')
    group.add_argument('--dropoff', '-o', nargs='*', type=lambda x: int(x, 16), help='drop off records')

    args = parser.parse_args()

    with open(args.inputFile) as input_file:
        canlog = Record.parse_from_file(input_file)

        if args.pickup:
            canlog = pick_log(canlog, args.pickup)
        elif args.dropoff:
            canlog = drop_log(canlog, args.dropoff)

        if args.difftime:
            printlog_with_diff_time(canlog)
        else:
            printlog(canlog)


def pick_log(canlog, ids):
    return filter(lambda rec: rec.hexid in ids, canlog)


def drop_log(canlog, ids):
    return filter(lambda rec: rec.hexid not in ids, canlog)


def printlog(canlog):
    for rec in canlog:
        print rec


def printlog_with_diff_time(canlog):
    prevtime = 0
    for rec in canlog:
        difftime = rec.crtime - prevtime
        print '%f %s' % (difftime, rec)
        prevtime = rec.crtime


if __name__ == '__main__': main()

La partie qui a été refactorisée est celle que vous avez commentée. L'utilisation d'argparse a également été corrigée.

Ceci est commun aux deux sources, mais j'ai écrit dans une instruction de type Perl comme for line in open (filename) ~ avec l'instruction with ~ as ~ Pour être honnête, je l'écris pour la première fois, et j'en suis venu à vouloir pouvoir écrire comme Python, en disant que Python a une syntaxe que je ne connais pas encore.

Je pensais que Python avait une manière d'écrire qui est utilisée par diverses personnes même pour le traitement de texte. Écrivons plus.

Recommended Posts

Traitement de texte avec Python
Traitement de texte UTF8 avec python
Traitement de fichiers en Python
Traitement multithread en python
Traitement des requêtes en Python
Traitement asynchrone (threading) en python
Parler avec Python [synthèse vocale]
Collection de traitement d'image en Python
Utilisation du mode Python dans le traitement
GOTO en Python avec Sublime Text 3
100 Language Processing Knock Chapitre 1 en Python
Extraire du texte d'images avec Python
Trier les gros fichiers texte en Python
Lire et écrire du texte en Python
Quadtree en Python --2
Python en optimisation
Métaprogrammation avec Python
Python 3.3 avec Anaconda
Géocodage en python
SendKeys en Python
traitement d'image python
Méta-analyse en Python
Unittest en Python
Époque en Python
Discord en Python
Allemand en Python
Traitement de fichiers Python
nCr en python
N-Gram en Python
Programmation avec Python
Plink en Python
Constante en Python
FizzBuzz en Python
Sqlite en Python
Étape AIC en Python
LINE-Bot [0] en Python
CSV en Python
Assemblage inversé avec Python
Réflexion en Python
Constante en Python
nCr en Python.
format en python
Scons en Python 3
Puyopuyo en python
python dans virtualenv
PPAP en Python
Quad-tree en Python
Réflexion en Python
Chimie avec Python
Hashable en Python
DirectLiNGAM en Python
LiNGAM en Python
Aplatir en Python
Aplatir en python
Traitez facilement des images en Python avec Pillow
Essayez de fouiller votre journal avec Python
Traitement des interdictions en double dans la banque de données GAE / Python
Lire des caractères dans des images avec Python OCR
Statut de chaque système de traitement Python en 2020