IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Script pour trouver des blocs dans un listing


Sujet :

Python

  1. #1
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut Script pour trouver des blocs dans un listing
    Bonjour,
    j'aimerais avoir vos avis sur le code suivant qui pour le moment cherche des groupes du type docstring """....""" ou du type commentaire à la C de la forme /*....*/ . Pour chaque type de groupe, son contenu ne doit pas avoir de signification particulière.

    Trouvez-vous ma méthode trop naïve ?

    Je suis conscient que pour le moment ma méthode ne fonctionne que pour des blocs ouverts sur une ligne puis fermés sur une autre. Ce n'est pas ce problème qui m'intéresse dans ce post !


    scriptTest.py
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    #! /usr/bin/env python3
     
    class findBlocks():
        """
        Looking for docstring using triple quotes "
        or C-like comments one several lines.
        """
     
        def __init__(self, pathOfTheFile):
            self.pathOfTheFile = pathOfTheFile
     
        def analyse(self):
            blocksToFind = {
                'docString': {
                    'open': '"""',
                    'close': '"""'
                },
                'multiComment': {
                    'open': '/*',
                    'close': '*/'
                }
            }
     
            lineNo = 0
            lastBlockOpened = ''
     
            with open(
                self.pathOfTheFile,
                mode = 'r',
                encoding = 'utf8'
            ) as fileToAnalyse:
                for oneLine in fileToAnalyse.readlines():
                    print(
                        oneLine.rstrip(),
                        sep="\n"
                    )
     
                    lineNo += 1
     
    # One block has been found.
                    if lastBlockOpened:
                        blockInfo = blocksToFind[lastBlockOpened]
     
                        position = self.lookFor(
                            stringToFind = blockInfo['close'],
                            stringToAnalyse = oneLine
                        )
     
                        if position > -1:
                            print(
                                '\t===> CLOSE {0} AT {1} IN LINE {2} : '.format(
                                    lastBlockOpened, position, lineNo
                                )
                            )
     
                            lastBlockOpened = ''
     
     
    # No block has been found.
                    else:
                        lastPosition = -1
                        lastBlockName = ''
     
                        for blockName, blockInfo in blocksToFind.items():
                            position = self.lookFor(
                                stringToFind = blockInfo['open'],
                                stringToAnalyse = oneLine
                            )
     
                            if position > -1 \
                            and (lastPosition == -1 or lastPosition > position):
                                lastPosition = position
                                lastBlockName = blockName
     
                        if lastBlockName:
                            lastBlockOpened = lastBlockName
     
                            print(
                                '\t===> OPEN {0} AT {1} IN LINE {2} : '.format(
                                    lastBlockName, lastPosition, lineNo
                                )
                            )
     
     
        def lookFor(self, stringToFind,
                          stringToAnalyse,
                          start = 0):
            return stringToAnalyse.find(stringToFind, start)
     
     
    ##############
    # SOME TESTS #
    ##############
     
    if __name__ == '__main__':
        pathForTest = "test_1.txt"
     
        findBlocks(pathForTest).analyse()

    test_1.txt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Try one doc string : """ Bla, bla
    Bla, bla, ...
     
    /* This not
    one comment ! */
    Bla, bla, ..."""
     
    Let's see one multiline comment  /* Comment """
    This is not one docstring !
    """
     
    Comment Comment Comment
    Comment */ This is the end.

  2. #2
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    Bonjour

    je trouve que, pour ce genre de problème, l'usage de classe(s) est complètement artificiel et, personnellement, j'ai un mal de chien à lire, suivre et comprendre le code. Comme on est en Python (et non pas en Java ou Ruby), je préfère passer par une/des fonctions.

    Sinon, au moins au niveau affichage, tu démarres les numéros de lignes à 1 et les numéros de colonnes à 0. Je pense que ce serait bien de choisir l'une ou l'autre des conventions.

    Ton script ramasse aussi, sur une ligne où il y a du commentaire, tout ce qu'il y a, y compris le texte hors commentaire ('Try one doc string' sur la première ligne ou 'This is the end' sur la dernière)

    En passant par une fonction, je préfère un truc dans le genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    # -*- coding:utf-8 -*-
     
    # (clé,valeur) = (début,fin) des blocs/commentaires recherchés
    PATTERNS = {'"""':'"""', '/*':'*/'}
     
    def search_blocks(pathname):
     
        blocks = []         # liste des blocs/commentaires détectés
        current  = None     # état courant (None ou dictionnaire)
     
        for l,line in enumerate(open(pathname).readlines()):
     
            # on N'est PAS dans un bloc/commentaire
            if current is None:
     
                # débuts de bloc/commentaire présents dans la ligne
                begins = filter(lambda p:p in line,PATTERNS)
     
                # s'il y en a, on s'intéresse au premier
                if begins:
                    index,begin = min(map(lambda b:(line.index(b),b),begins))
                    current = {'begin':begin,'start_char':index,'start_line':l,'end':PATTERNS[begin],'buffer':line[index:]}
     
            # on EST dans un bloc/commentaire ET la fin du bloc/commentaire est présente dans la ligne courante
            elif current['end'] in line:
     
                current['end_char']  = line.index(current['end'])+len(current['end'])
                current['end_line']  = l
                current['buffer']   += line[:current['end_char']]
                blocks.append(current)
                current = None
     
            # on EST dans un bloc/commentaire ET la fin du bloc/commentaire N'est PAS présente
            else:
     
                current['buffer'] += line
     
        return blocks
     
     
    if __name__ == '__main__':
        import sys
        from pprint import pprint as pp
        comments = search_blocks(sys.argv[1])
        for c,comment in enumerate(comments):
            print "==== DEBUT COMMENTAIRE No %d =========" % (c+1)
            pp(comment)
            print "==== soit :\n%s" % comment['buffer']
            print "==== FIN COMMENTAIRE No %d =========" % (c+1)
    Sur ton fichier exemple, ça donne ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    ==== DEBUT COMMENTAIRE No 1 =========
    {'begin': '"""',
     'buffer': '""" Bla, bla\nBla, bla, ...\n \n/* This not\none comment ! */\nBla, bla, ..."""',
     'end': '"""',
     'end_char': 16,
     'end_line': 5,
     'start_char': 21,
     'start_line': 0}
    ==== soit :
    """ Bla, bla
    Bla, bla, ...
     
    /* This not
    one comment ! */
    Bla, bla, ..."""
    ==== FIN COMMENTAIRE No 1 =========
    ==== DEBUT COMMENTAIRE No 2 =========
    {'begin': '/*',
     'buffer': '/* Comment """\nThis is not one docstring !\n"""\n \nComment Comment Comment\nComment */',
     'end': '*/',
     'end_char': 10,
     'end_line': 12,
     'start_char': 33,
     'start_line': 7}
    ==== soit :
    /* Comment """
    This is not one docstring !
    """
     
    Comment Comment Comment
    Comment */
    ==== FIN COMMENTAIRE No 2 =========

  3. #3
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 049
    Par défaut
    je trouve que, pour ce genre de problème, l'usage de classe(s) est complètement artificiel
    Je ne suis pas tout à fait d'accord, les classes permettent une bonne organisation et un code maintenable.

    Ne connaissant pas l'objectif final de rambc, on pourrait très bien penser qu'une suite reste possible, une modification ou autres...

    Par contre j'aurais appelé la classe Block et la méthode find

  4. #4
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Bonsoir.

    Merci plxpy pour ton code. Pour le faire fonctionner sous Python3, il faut remplacer filter(lambda p: p in line,PATTERNS) par list(filter(lambda p: p in line,PATTERNS)).

    Citation Envoyé par fred1599 Voir le message
    Je ne suis pas tout à fait d'accord, les classes permettent une bonne organisation et un code maintenable.

    Ne connaissant pas l'objectif final de rambc, on pourrait très bien penser qu'une suite reste possible, une modification ou autres...
    Effectivement, l'idée est d'avoir une classe qui sera surclassée pour différents types de blocs.

    Citation Envoyé par fred1599 Voir le message
    Par contre j'aurais appelé la classe Block et la méthode find
    C'est mieux effectivement...

  5. #5
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    Effectivement, l'idée est d'avoir une classe qui sera surclassée pour différents types de blocs
    Si ce n'est que ça, plutôt que d'utiliser la variable globale PATTERNS, un paramètre supplémentaire dans la fonction peut faire l'affaire (là j'ai fait vite). Et je continue de penser que, pour traiter ça, une fonction est plus naturelle. Mais c'est complètement subjectif.

  6. #6
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    vraie question ,pas là pour prolonger le débat fonction VS classe

    @rambc : comment comptes-tu t'y prendre pour faire donc une classe "générique" Block et la dériver pour gérer tantôt les commentaires C, tantôt les commentaires XML, etc ... ?

    Une méthode de classe (@classmethod) dans le classe Block qui s'appuyerait sur une variable de classe définissant début et fin des blocs recherchés, toujours avec le même nom et définie dans chaque classe dérivée (j'ai déjà "commis" un truc dans le genre) ?

    Ou plus simple (quand c'est possible, là je pense que c'est bon), une variable de classe dans chaque classe dérivée donnant ces début et fin de blocs ?

    Autre chose ?

  7. #7
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    certainement plus clair et pour fixer les idées

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    >>> class Block(object):
    ...     def __init__(self):
    ...             assert hasattr(self,'START_END')
    ...             # ...
    ...     def find(self):
    ...             print "je travaillerai avec %s" % str(self.START_END)
    ...             # ...
    ... 
    >>> class C_Comment(Block):
    ...     START_END = ('/*','*/')
    ... 
    >>> class XML_Comment(Block):
    ...     START_END = ('<!--','-->')
    ... 
    >>> class Other(Block):
    ...     pass
    ... 
    >>> 
    >>> c = C_Comment()
    >>> c.find()
    je travaillerai avec ('/*', '*/')
    >>> 
    >>> x = XML_Comment()
    >>> x.find()
    je travaillerai avec ('<!--', '-->')
    >>> 
    >>> o = Other()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in __init__
    AssertionError
    >>>

  8. #8
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Bonjour,
    ton exemple ci-dessus est le bon.

    Les classes dérivées seront donc toutes courtes. J'ai commis un code "test" un peu amélioré qui est le suivant. J'ai aussi mis un autre fichier "test".

    J'utilise un dictionnaire pour définir les blocs pour plus de flexibilité. Ce weekend, quand ma santé ira mieux, je vais m'occuper de groupe du type parenthèses pour faire un classe qui gèrera deux types de blocs.

    Code Python
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    #! /usr/bin/env python3
     
    class Block():
        """
        BLOCKS_TO_FIND is one dictionnary of the following kind.
     
            {
                'name': {
                    'open': '...',
                    'close': '...'
                },
                ...
            }
        """
        BLOCKS_TO_FIND = {}
     
        def __init__(self, pathOfTheFile):
            self.pathOfTheFile = pathOfTheFile
     
        def find(self):
            lastState = ''
            closeToFind = ''
     
            with open(
                self.pathOfTheFile,
                mode = 'r',
                encoding = 'utf8'
            ) as fileTofind:
                for lineNo, oneLine in enumerate(fileTofind.readlines()):
                    positionShift = 0
     
                    print(oneLine.rstrip())
     
    # The search must be done inside the line for cases like
    #
    #    One /* Comment */ and one """docstring""" in the same line... /*
                    while oneLine:
     
    # No block has been found.
                        if not lastState:
                            lastPosition = -1
                            lastBlockName = ''
     
                            for blockName, blockInfo in self.BLOCKS_TO_FIND.items():
    # We only analyse text before one eventually block already found.
                                position = self.search(
                                    stringToAnalyse = oneLine[:lastPosition],
                                    stringToFind = blockInfo['open']
                                )
     
                                if position > -1:
                                    lastPosition = position
                                    lastBlockName = blockName
                                    closeToFind = blockInfo['close']
     
                            if lastBlockName:
                                lenShift = len(
                                    self.BLOCKS_TO_FIND[lastBlockName]['open']
                                )
                                oneLine = oneLine[lastPosition + lenShift:]
                                lastState = lastBlockName
     
                                print(
                                    '\t===> OPEN {0} AT {1} IN LINE {2} : '.format(
                                        lastBlockName, 
                                        lastPosition + positionShift + 1, 
                                        lineNo + 1
                                    )
                                )
     
                                positionShift += lastPosition + lenShift
     
                            else:
                                oneLine = ''
     
    # One block has been found.
                        else:
                            position = self.search(
                                stringToAnalyse = oneLine,
                                stringToFind = closeToFind
                            )
     
                            if position == -1:
                                oneLine = ''
     
                            else:
                                lenShift = len(
                                    self.BLOCKS_TO_FIND[lastBlockName]['close']
                                )
                                position += lenShift
     
                                print(
                                    '\t===> CLOSE {0} AT {1} IN LINE {2} : '.format(
                                        lastState, 
                                        position + positionShift + 1, 
                                        lineNo + 1
                                    )
                                )
     
                                oneLine = oneLine[position:]
                                positionShift += position
     
                                lastState = ''
                                closeToFind = ''
     
        def search(self, stringToAnalyse,
                         stringToFind):
            return stringToAnalyse.find(stringToFind)
     
     
    class TestBlock(Block):
        """
        Looking for docstring using triple quotes
        and C-like comments on several lines.
        """
        BLOCKS_TO_FIND = {
            'docString': {
                'open': '"""',
                'close': '"""'
            },
            'multiComment': {
                'open': "/*",
                'close': "*/"
            }
        }
     
     
    ##############
    # SOME TESTS #
    ##############
     
    if __name__ == '__main__':
        pathForTest = "test.txt"
     
        TestBlock(pathForTest).find()
    test.txt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    One /* Comment */ and one """docstring""" in the same line... /*
    Let's start ne comment on two lines */
     
    /* Comment opened and closed in the same line ! */
     
     
     
     
    Try one doc string : """ Blooo, bla
    Bla, bla, ...
     
    /* This not
    one comment ! */
    Bla, bla, ..."""
     
    Let's see one multiline comment  /* Comment """
    This is not one docstring !
    """
     
    Comment Comment Comment
    Comment */ This is the end.
     
    Bla, bla, bla, bla,...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [KSH] Script pour trier des fichier dans des répertoires par mois
    Par MsieurDams dans le forum Shell et commandes GNU
    Réponses: 4
    Dernier message: 08/08/2014, 14h32
  2. Réponses: 7
    Dernier message: 24/01/2012, 18h55
  3. Script pour exporter des données dans fichier texte
    Par dionmaxime dans le forum MS SQL Server
    Réponses: 1
    Dernier message: 15/03/2011, 05h18
  4. Requete pour trouver des trous dans une suite
    Par Ben_Le_Cool dans le forum Langage SQL
    Réponses: 11
    Dernier message: 28/08/2009, 18h17
  5. [BATCH]script pour encoder des fichiers dans une arborescence
    Par ashgan44 dans le forum Scripts/Batch
    Réponses: 4
    Dernier message: 11/05/2009, 15h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo