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 :

Repérer la dernière ligne d'un fichier


Sujet :

Python

  1. #21
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 906
    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 : 3 906
    Points : 7 273
    Points
    7 273
    Par défaut
    Utiliser le module fileinput

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    import fileinput
    for lines in fileinput.FileInput("fichier.txt", inplace=1): 	
     	lines = lines.strip()
     	if lines == '': continue
     	print lines
    ou une lists comprehension

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    f = open('test.txt')
    l = [l for l in f.readlines() if l.strip()]
    f.close()
    print l

  2. #22
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Je suis assez d’accord avec le mouvement d’humeur de Antoine935.
    Parmi les différentes manières de mal poser un problème, rambc pratique celle de ne pas prendre le temps nécessaire pour décanter et préciser son objectif. C’est frappant dans cette file où tu modifies plusieurs fois ton objectif, rambc

    Après discussion, on finit par avoir un énoncé mûri:
    - Je dois parcourir ligne par ligne mon fichier texte pour l'analyser
    - Je veux le nettoyer en ne gardant pas les lignes vides au début et celles tout à la fin,
    J’ai pris le mot ’nettoyer’ comme signifiant que tu voulais recopier le fichier sans les lignes vides initiales et finales : j'ai écrit un code qui fait cela.

    C’est uniquement une fois parvenu au but, et après avoir comparé les vitesses de ton code et du mien, ce qui m’a obligé à comprendre ton code dérivé de celui de DelphiManiac, que j’ai réalisé que tu demandais moins: simplement afficher à l’écran un fichier sans les lignes de début ni de fin.






    Mais mon code de recopiage-“ébarbage“ n’est pas superflu, parce que, même pour afficher, je trouve incongru de passer son temps au sein d’un fichier à faire des tests qui ne sont requis que pour éliminer les lignes vides situées en périphérie du fichier

    Il vaut mieux, je pense, recopier un fichier en l’ébavurant avec mon code avant de l’afficher, c’est plus rapide et fait une fois pour toutes.






    aller voir les lignes à partir de la fin ne semble pas faisable sans traiter le fichier en un seul bloc.
    Assurément que si, c'est possible,
    à condition de ne pas se persuader a priori que ça ne l’est pas:
    avec getsize() , qui permet de calculer où sauter dans le fichier avec seek()


    -----------

    Éliminer des lignes vides d’un fichier, c’est en réalité éliminer des caractères ’\n’ et '\r' , c’est tout.

    Pour la fin du fichier, un simple déplacement du EOF (end of file) en mode ’r+’ et le tour est joué sans avoir à réécrire tout le fichier. Ce déplacement de EOF se fait avec truncate()

    -----------------

    Pour moi le vrai problème est en début de fichier, pas à la fin. Car pour les premières lignes vides, on est obligé de faire un recopiage du fichier en oubliant les premières lignes vides.
    On se sert du fait qu’ on peut ouvrir deux pointeurs à la fois dans un même fichier afin de faire un recopiage par morceaux, du fichier sur lui-même.

    On ne fait donc ni une itération sur les lignes (c’est long), ni une seule passe en chargeant la totalité du fichier en mémoire pour le traiter avant recopiage (c’est lourd pour la mémoire): on va itérer sur des morceaux de quelques hecto ou kilo ou méga-octets (c’est au choix en fonction de la taille du fichier et de la taille de la mémoire vive), sans se préoccuper de la position des fins de lignes dans les morceaux

    C’est autant plus intéressant que le fichier est gros.

    -----------------

    Voici le code pour faire le recopiage par morceaux:

    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
    from time import clock
    from os.path import getsize
     
    fich = raw_input('Entrer le nom du fichier a recopier-ebarber : ')
    print 'Recopiage-ebarbage du fichier'+fich+' avec seek()'
     
    te = clock()
     
    ########## Elimination des lignes vides terminales ##################
    nob = getsize(fich) # nob = number of bytes
    nob1 = nob
    f = open(fich,'rb+')
    x = -1
    recul = 1
    while x==-1 and nob-recul:
        recul += min(5,nob-recul)
        f.seek(-recul,2)
        ch = f.read()
        chcut = ch.rstrip('\n\r')
        x = max(chcut.rfind('\n'),chcut.rfind('\r'))
    if not chcut:
        print '\nLe fichier ne contient que des caracteres \\n et \\r'\
              +'Verifiez.'
    else:
        f.seek(-recul+len(chcut),1)
        f.truncate()
        f.close()
    #####################################################################
     
     
    ########## Elimination des lignes vides initiales ###################
    nob = getsize(fich) # nob = number of bytes
    nob2 = nob
    pLire = open(fich,'rb')
    x = 0
    av = 0
    while x==av and nob-av:
        av += min(5,nob-av)
        pLire.seek(0)
        ch = pLire.read(av)
        chcut = ch.lstrip('\n\r')
        x = av - len(chcut)
        pLire.seek(x)
    pEcrire = open(fich,'rb+')
    ch = 'go'
    while ch:
        ch = pLire.read(70000)
        pEcrire.write(ch)
    pEcrire.truncate()
    pEcrire.close()
    pLire.close()
    #####################################################################
     
    tf = clock()
    print 'nob1 =',nob1
    print 'nob2 =',nob2
    print 'nob3 =',getsize(fich)
    print tf-te,' secondes'


    Le code suivant, avec explications, crée un fichier puis le recopie en l’ébavurant en montrant le processus:

    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
    if 1:
    from os.path import getsize
     
    fich = 'essai fruits.txt'
     
    if fich=='essai fruits.txt':
        ch = '\n\r\r\n\r\r\n\r\r\n\r\n\n'\
             '\rarbouse\nkiwi\ngroseille\n\r\ncoing\r\rcanneberge\n\r\nlitchi'\
             '\n\r\r\r\n\n\n\r\r\n\n\r\r\n'
        f = open(fich,'wb')
        f.write(ch)
        f.close()
     
     
    print '* Le fichier initial'
    print 'getsize 1 :',getsize(fich)
    f = open(fich,'rb')
    a = f.read()
    f.close()
    print repr(a)
    print 'len du fichier lu :',len(a)
    print 'AFFICHAGE DU FICHIER ENTRE LES DEUX LIGNES ----------etc'
    print '--------------------------------------------------------'
    print a
    print '--------------------------------------------------------'
     
     
     
    ########## Elimination des lignes vides terminales ##################
    nob = getsize(fich) # nob = number of bytes du fichier
    f = open(fich,'rb+')
    x = -1 # x va recueillir la position de la fin de la ligne de l'avant-derniere ligne non vide
    recul = 1 # recul est un offset pour retrogader dans le fichier a partir de sa fin
    while x==-1 and nob-recul:
        # tant que x vaut -1, le pointeur de fichier n'a pas atteint l'avant derniere ligne non vide
        # quand il n'y a qu'une ligne non vide, le pointeur recule jusqu'au debut du fichier et x devient egal a 0
        recul += min(4,nob-recul)
        # il faut eviter que recul prenne une nouvelle valeur plus grande que la longueur de fichier
        f.seek(-recul,2)
        # le pointeur repart de la fin du fichier et retrograde de recul caracteres
        ch = f.read()
        # lecture des val(recul) derniers caracteres du fichier
        chcut = ch.rstrip('\n\r')
        # on elimine toutes les lignes vides en fin de fichier
        # si ch n'etait constitue que de lignes vides, alors chcut est vide
        x = max(chcut.rfind('\n'),chcut.rfind('\r'))
        # si chcut n'est pas vide , on cherche la position de fin de ligne la plus a droite, si elle existe
     
    # en sortie, x est la position de la fin de l'avant derniere ligne non vide; ou vaut 0
    if not chcut:
        print '\nLe fichier ne contient que des caracteres \\n et \\r'\
              +'Verifiez.'
    else:
        f.seek(-recul+len(chcut),1)
        # on positionne le pointeur du fichier au tout debut de toutes les lignes vides finales
        f.truncate()
        # on inscrit un EOF juste apres la derniere ligne non vide
        f.close()
    #####################################################################
     
     
     
    print '\n\n\n\n\n* Le fichier apres elimination des lignes vides terminales'
    print 'getsize 2 :',getsize(fich)
    f = open(fich,'rb')
    a = f.read()
    f.close()
    print repr(a)
    print 'len du fichier lu :',len(a)
    print 'AFFICHAGE DU FICHIER ENTRE LES DEUX LIGNES ----------etc'
    print '--------------------------------------------------------'
    print a
    print '--------------------------------------------------------'
     
     
     
    ########## Elimination des lignes vides initiales ###################
    nob = getsize(fich) # nob = number of bytes
    pLire = open(fich,'rb')
    x = 0
    av = 0
    while x==av and nob-av: # x==av signifie que chcut==[]
        av += min(5,nob-av) # il faut eviter que av devienne plus grande que la longueur du fichier
        pLire.seek(0)
        ch = pLire.read(av) # on lit les val(av) premiers caracteres du debut du fichier
        chcut = ch.lstrip('\n\r') # chcut==[] qund ch ne contient que des lignes vides, c'est a dire pas des \r et \n
        x = av - len(chcut) # x<av seulement si ch ne contient pas que des lignes vides
                            # x est la position du premier caractere non \r et non \n
    # en sortie, x est la position du premier caractere de la premiere ligne non vide
    pLire.seek(x) # on positionne le pointeur pLire au debut des lignes non vides
    pEcrire = open(fich,'rb+') # en ouvrant, le pointeur pEcrire est a la position 0
    ch = 'go'
    while ch:
        ch = pLire.read(7) # on lit dans le fichier
        pEcrire.write(ch) # on ecrit en amont de ce qu'on a lu, donc sans ecraser ce qui reste a lire
    pEcrire.close()
    pLire.close()
    #####################################################################
     
     
     
    print '\n\n\n\n\n* Le fichier apres elimination des lignes vides initiales'
    print 'getsize 3 :',getsize(fich)
    f = open(fich,'rb')
    a = f.read()
    f.close()
    print repr(a)
    print 'len du fichier lu :',len(a)
    print 'AFFICHAGE DU FICHIER ENTRE LES DEUX LIGNES ----------etc'
    print '--------------------------------------------------------'
    print a
    print '--------------------------------------------------------'









    J'ai pas mal fignolé le code: a priori il ne devrait pas produire de surprises. Mais si quelqu’un veut l’utiliser pour ses fichiers, je ne saurais trop lui recommander de rajouter des instructions pour créer une sauvegarde avec un autre nom (le fichier raccourci gardant le même nom et le fichier original devenant la sauvegarde), au cas où il y aurait un problème sur le fichier raccourci.

    C’est en affet assez chirurgical, et bien que j’ai envisagé des cas divers, on ne sait jamais ce qu’on peut oublier.

    Les seuls séparateurs envisagés sont ’\r’ , ’\n’ , ’\r\n’ . Si un texte contient d’autres types de séparateurs, il faut adapter le code pour les faire reconnaître comme séparateurs par le code.

    Mine de rien, ça m’a fait suer. J’ai mis pas mal de temps à comprendre l’importance du deuxième truncate() pour terminer l’élimination des lignes initiales, alors qu’il est évident qu’il est nécessaire, sinon on garde en queue de fichier le même nombres de caractères qu’il y a de lignes vides initiales, la séquence de ces caractères en queue étant en double.




    ---------------------------------------------------




    Tout ceci est bien beau, et je me suis bien amusé, mais il ne doit pas être fréquent d’avoir besoin de passer par ce code pour recopier-ébarber un fichier.

    En effet, comme l’a dit quelqu’un dans je ne sais plus quel post, les capacités des mémoires vives sont de nos jours très importantes et permettent d’envisager traiter des fichiers très volumineux avec la méthode suivante sans problème sérieux:

    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
    ch = '\r\n\n\n\r\n\r\n\nathaualpa yupanqui\norangina\nendeavour\n\n\r\r\n\r\n'
    f = open('essai fru.txt','wb')
    f.write(ch)
    f.close()
     
    f = open('essai fru.txt','rb')
    print 'Le fichier qui a ete enregistre:'
    print repr(f.read())
    f.close()
     
    f = open('essai fru.txt','rb+')
    ch = f.read().strip('\r\n')
    f.seek(0)
    f.write(ch)
    f.truncate()
    f.close
     
    f = open('essai fru.txt','rb')
    print '\nLe fichier apres recopiage sans lignes vides devant et apres:'
    print repr(f.read())
    f.close()
    Le fichier qui a ete enregistre:
    '\r\n\n\n\r\n\r\n\nathaualpa yupanqui\norangina\nendeavour\n\n\r\r\n\r\n'

    Le fichier apres recopiage sans lignes vides devant et apres:
    'athaualpa yupanqui\norangina\nendeavour'

    ----------------------------------------------


    Nota Bene


    Il est absolument impératif de faire les ouvertures de fichiers en mode binaire : ’rb’ et ’wb’ pour que les caractères soient lus et écrits tels qu’ils apparraissent dans les chaînes.

    Sinon il se passe des choses bizarres, que je ne comprends pas toutes. L’une des raisons est que Windows écrit toujours des ’\r\n’ , même si on lui spécifie ’\n’. En sens inverse, Python traduit toutes les fins de ligne en un seul caractère: ’\n’ . Si on n’est pas en mode binaire, c’est ingérable.

    ’rb+’ permet de lire ET d’écrire

    ’a’ ne permet que de lire et d’écrire à la fin d’un fichier, exclusivement: à chaque intsruction d’écriture, le pointeur du fichier est d’abord déplacé à la fin du fichier, après le dernier caractère, avant que l’instruction soit effectuée. C’est un mode de protection du fichier.






    ----------------------------------------------



    J’ai fait un comparatif succint de mon code avec le code de rambc dérivé de celui de DelphiManiac.

    J’ai adapté ce dernier pour en faire un code qui recopie un fichier dans un autre fichier et pas seulement de l’afficher:

    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
    from os.path import getsize
    from time import clock
     
    def readContent(filePath):
        """
        This iterator allows to read a text file line by line without keeping
        the first empy lines, and the last ones.
        """
        with open(filePath, 'rb') as fileToAnalyse:
            numberOfEmptyLinesBefore = 0
            contentFound = False
     
            for oneLine in fileToAnalyse:
                if oneLine.strip('\r\n'):
                    if contentFound:
                        for i in range(numberOfEmptyLinesBefore):
                            yield '\r\n'
                        yield oneLine
                    else:
                        contentFound = True
                        yield oneLine
                    numberOfEmptyLinesBefore = 0
                else:
                    numberOfEmptyLinesBefore += 1
     
    fich = raw_input('Entrer le nom du fichier : ')
    print 'Recopiage-ebarbage du fichier '+fich+' par yielding'
     
    fich2 = fich.replace('.','2.')
    g = open(fich2,'wb')
    print getsize(fich)
    te = clock()
    for l in readContent(fich):
        g.write(l)
    g.close()
    tf = clock()
    print getsize(fich2)
    print tf-te,'secondes'
    Entrer le nom du fichier : zl.txt
    Recopiage-ebarbage du fichier zl.txt par yielding
    1207301
    1207185
    0.490571008326 secondes

    0.447550456832 secondes
    0.439849198711 secondes
    0.572808834642 secondes
    0.435068702866 secondes
    0.438258214382 secondes
    0.442971941965 secondes

    Avec mon code:
    Entrer le nom du fichier a recopier-ebarber : zl1.txt
    Recopiage-ebarbage du fichierzl1.txt avec seek()
    nob1 = 1207301
    nob2 = 1207233
    nob3 = 1207183
    0.0279856797442 secondes

    0.0280178067324 secondes
    0.0267059081531 secondes
    0.0277183273293 secondes
    0.0282044226291 secondes
    0.0263840795408 secondes
    0.0268056414992 secondes
    Soit 17 fois plus rapide

    La différence entre 1207183 et 1207185 est due au fait que le code de rambc adapté par moi pour écrire dans un fichier ajoute une fin de ligne ’\r\n’ à la fin du fichier, tandis que mon code coupe absolument tous les caractères ’\r’ et ’\n’ terminaux. Je ne me suis pas cassé la tête avec ce détail

  3. #23
    Membre émérite
    Avatar de DelphiManiac
    Homme Profil pro
    Homme à tout faire
    Inscrit en
    Mars 2002
    Messages
    1 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Homme à tout faire
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 147
    Points : 2 533
    Points
    2 533
    Par défaut
    J'avoue ne pas avoir tout lu de ta réponse et d'être resté sur la solution apportée -> seek.

    Si je comprend bien tu parcours de la fin vers le début le fichier pour éliminer tout les caractères \r et \n, idem en début de fichier. Tu prends les 2 index, tu lis le fichier que sur cette partie là et tu recopies les données lues.

    Mais il en ressort que :

    - Je dois parcourir ligne par ligne mon fichier texte pour l'analyser
    - Je veux le nettoyer en ne gardant pas les lignes vides au début et celles tout à la fin,
    As tu laisser tomber la partie ou il doit lire le fichier ligne par ligne ?







    [ajout]En partant de ton idée, je viens de faire un petit test dans le même sens que toi, et j'arrive (à priori et si j'ai pas fais de bêtises au chronométrage) à améliorer la vitesse de traitement.

    Le code que j'ai pondu (non je ne suis pas vraiment une poule) :
    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
    from time import clock
    from os.path import getsize
     
    import os
     
    def recopie_fichier(input_file, output_file):
        chunk_size = 65536
        small_chunk_size = 2048
     
        f_in = open(input_file, "r+b")
     
        f_in.seek(0, os.SEEK_SET)
        stop = False
        while not stop:
            buffer = f_in.read(small_chunk_size)
            pos = 0
            for c in buffer:
                if c not in ['\r', '\n']:
                    start = f_in.tell() - small_chunk_size + pos
                    stop = True
                    break
                pos += 1
     
        f_in.seek(0, os.SEEK_END)
        optimal_block_size = f_in.tell() % small_chunk_size
     
        stop = False
        while not stop:
            f_in.seek(-optimal_block_size, os.SEEK_CUR)
            buffer = f_in.read(optimal_block_size)
            f_in.seek(-optimal_block_size, os.SEEK_CUR)
            optimal_block_size = small_chunk_size
     
            pos = 0
            for c in buffer[::-1]:
                pos += 1
                if not c in ['\r', '\n']:
                    end = f_in.tell() + optimal_block_size - pos
                    stop = True
                    break
     
        size_to_read = end - start + 1
        f_in.seek(start, os.SEEK_SET)
     
        f_out = open(output_file, 'w+b');
        while size_to_read > 0:
            buffer = f_in.read(min(chunk_size, size_to_read))
            f_out.write(buffer)
            size_to_read -= chunk_size
     
        f_out.close()
        f_in.close()
        print getsize(input_file)
        print getsize(output_file)
     
    te = clock()
    recopie_fichier(r'e:\temp1\test1.tmp', r'e:\temp1\test2.tmp')
    print clock() - te, " secondes"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    17915250
    17893872
    0.0816477373521  secondes
    Ton code avec juste une modif au début pour générer par copie un fichier de test, vu que ton code écrase le fichier.

    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
    from time import clock
    from os.path import getsize
    from shutil import copyfile
     
    copyfile (r'e:\temp1\test1.tmp', r'e:\temp1\test2.tmp')
    fich = r'e:\temp1\test2.tmp'
    print 'Recopiage-ebarbage du fichier'+fich+' avec seek()'
     
    te = clock()
     
    ########## Elimination des lignes vides terminales ##################
    nob = getsize(fich) # nob = number of bytes
    nob1 = nob
    f = open(fich,'rb+')
    x = -1
    recul = 1
    while x==-1 and nob-recul:
        recul += min(5,nob-recul)
        f.seek(-recul,2)
        ch = f.read()
        chcut = ch.rstrip('\n\r')
        x = max(chcut.rfind('\n'),chcut.rfind('\r'))
    if not chcut:
        print '\nLe fichier ne contient que des caracteres \\n et \\r'\
              +'Verifiez.'
    else:
        f.seek(-recul+len(chcut),1)
        f.truncate()
        f.close()
    #####################################################################
     
     
    ########## Elimination des lignes vides initiales ###################
    nob = getsize(fich) # nob = number of bytes
    nob2 = nob
    pLire = open(fich,'rb')
    x = 0
    av = 0
    while x==av and nob-av:
        av += min(5,nob-av)
        pLire.seek(0)
        ch = pLire.read(av)
        chcut = ch.lstrip('\n\r')
        x = av - len(chcut)
        pLire.seek(x)
    pEcrire = open(fich,'rb+')
    ch = 'go'
    while ch:
        ch = pLire.read(70000)
        pEcrire.write(ch)
    pEcrire.truncate()
    pEcrire.close()
    pLire.close()
    #####################################################################
     
    tf = clock()
    print 'nob1 =',nob1
    print 'nob2 =',nob2
    print 'nob3 =',getsize(fich)
    print tf-te,' secondes'
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Recopiage-ebarbage du fichiere:\temp1\test2.tmp avec seek()
    nob1 = 17915250
    nob2 = 17893888
    nob3 = 17893872
    0.444635072335  secondes
    Ce temps étant obtenu avec le fichier de test attaché au message qui fait dans les 17Mo une fois décompressé.








    [re ajout]Dernier test avec mmap comme suggéré par wiztricks sur un autre thread. Résultat encore plus rapide et code plus élégant (utilisation des slices)

    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
    from time import clock
    from os.path import getsize
    import mmap
    import os
     
    def recopie_fichier(input_file, output_file):
        chunk_size = 65536
        size = getsize(input_file)
     
        f_in = open(input_file, "r+b")
        map = mmap.mmap(f_in.fileno(), 0)
     
        i = -1
        while map[i] in ['\r', '\n']:
            i -= 1
     
        end = size + i
     
        i = 1
        while map[i] in ['\r', '\n']:
            i += 1
     
        start = i
     
        size_to_read = end - start + 1
        f_in.seek(start, os.SEEK_SET)
     
        f_out = open(output_file, 'w+b');
        while size_to_read > 0:
            buffer = f_in.read(min(chunk_size, size_to_read))
            f_out.write(buffer)
            size_to_read -= chunk_size
     
        f_out.close()
        f_in.close()
     
        print size
        print getsize(output_file)
     
    te = clock()
    recopie_fichier(r'e:\temp1\test1.tmp', r'e:\temp1\test2.tmp')
    print clock() - te, " secondes"
    P.S. : Si je n'ai pas compris la totalité de ta solution, en lisant en diagonale, ou si une erreur c'est glissée dans mes tests, je m'en excuse d'avance.
    Fichiers attachés Fichiers attachés

  4. #24
    Membre chevronné

    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
    Points : 1 751
    Points
    1 751
    Par défaut
    Citation Envoyé par eyquem Voir le message
    Parmi les différentes manières de mal poser un problème, rambc pratique celle de ne pas prendre le temps nécessaire pour décanter et préciser son objectif. C’est frappant dans cette file où tu modifies plusieurs fois ton objectif, rambc
    Je plaide coupable. En fait, j'ai posté ce post en parallèle avec un autre sur le traitement de gros fichiers. A la base, je voulais juste nettoyer les dernières lignes. Entre temps j'ai décidé de traiter mes fichiers lignes par lignes d'où le changement d'objectif.

    De toute façon, parcourir un fichier en commençant par la fin peut avoir une grande utilité donc tous les codes proposés ne l'ont pas été pour rien.

    Citation Envoyé par DelphiManiac Voir le message
    Dernier test avec mmap comme suggéré par wiztricks sur un autre thread. Résultat encore plus rapide et code plus élégant (utilisation des slices)

    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
    from time import clock
    from os.path import getsize
    import mmap
    import os
     
    def recopie_fichier(input_file, output_file):
        chunk_size = 65536
        size = getsize(input_file)
     
        f_in = open(input_file, "r+b")
        map = mmap.mmap(f_in.fileno(), 0)
     
        i = -1
        while map[i] in ['\r', '\n']:
            i -= 1
     
        end = size + i
     
        i = 1
        while map[i] in ['\r', '\n']:
            i += 1
     
        start = i
     
        size_to_read = end - start + 1
        f_in.seek(start, os.SEEK_SET)
     
        f_out = open(output_file, 'w+b');
        while size_to_read > 0:
            buffer = f_in.read(min(chunk_size, size_to_read))
            f_out.write(buffer)
            size_to_read -= chunk_size
     
        f_out.close()
        f_in.close()
     
        print size
        print getsize(output_file)
     
    te = clock()
    recopie_fichier(r'e:\temp1\test1.tmp', r'e:\temp1\test2.tmp')
    print clock() - te, " secondes"
    Je n'ai pas saisi l'ensemble du code, il faudrait que je m'intéresse au module mmap. Petite question : avec mmap, le parcours des fichiers se fait-il morceau par morceau d'un certain nombre d'octets, ou bien ligne par ligne ?

  5. #25
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Si je comprend bien tu parcours de la fin vers le début le fichier pour éliminer tout les caractères \r et \n, idem en début de fichier. Tu prends les 2 index, tu lis le fichier que sur cette partie là et tu recopies les données lues.
    Non, ce n’est pas tout à fait ça que fait mon code.
    J’avais pensé faire ce genre de parcours, mais j'ai abandonné parce que cela consistait à répéter les mêmes instructions comme un ressassement sur chaque caractère les uns après les autres.
    C'est pourquoi j'ai fait appel à strip(’\n\r’) dont le fonctionnement me semble être plus celui d’une consommation de chaîne en continu que celui d’un examen de façon discrète (au sens mathématique) d’une chaîne.





    DelphiManiac, à la lecture de ton code et de son temps d’exécution, le mien a pris un coup de vieux.
    Je trouve astucieux le calcul de start et end en jouant de la combinaison de deux pointeurs, l’un dans le fichier et l’autre dans un morceau de fichier chargé en buffer.

    En examinant mon code, j’y ai vu deux maladresses et un gros défaut d'algorithme me faisant penser que ton code était meilleur.
    En fait les choses ne sont pas aussi simples.
    Il y a une troisième maladresse dans mon code qui explique sa piètre performance lors de la comparaison que tu as faite. Quelques injections de vitamines prélevées dans ton message lui ont donné un coup de jeunesse.



    -------------------------------------------



    Mais prenons dans l’ordre.

    Je rappelle que l’objectif est de recopier un fichier en éliminant les lignes vides situées à sa fin et en son début.

    Dans mon code du message #22, je ne lis pas le fichier entre deux index préalablement déterminés.

    1) Il y a d’abord traitement de la fin du fichier pour écrire le signal EOF (end of file) juste devant la queue terminale de \r et \n.
    Cette séquence de \r et de \n reste ainsi sur le disque dur mais elle devient extérieure au fichier. Le reste du fichier reste intouché.

    2) Il y a ensuite traitement du début du fichier.
    Là, obligation de lire les données et de les réécrire. On fait donc deux choses à la fois, en fait: traitement des lignes videsdu début et recopiage.

    Mais l’astuce est d’utiliser deux pointeurs, l’un qui repère la position à partir de laquelle il faut lire, l’autre qui repère la position à partir de laquelle il faut réécrire, ceci dans le même fichier. Les deux pointeurs se déplacent indépendamment dans le fichier.

    Comme on réécrit un morceau en amont de son prélèvement , il n’y a écrasement que de données qui ont déjà été lues.

    Ainsi avec un fichier ’\n\n\n\n\nabcdefghijklmnopqrstuv....’ , si on lit et réécrit des morceaux de 7 caractères, on va faire
    - lecture de abcdefg à partir de la position 5
    - écriture de abcdefg à partir de la position 0 -> ’abcdefgcdefghijklmnopqrstuv....’
    - lecture de hijklmn à partir de la position 5+7=12
    - écriture de hijklmn à partir de la position 7
    à la suite de abcdefg -> ’abcdefghijklmnjklmnopqrstuv....’
    - lecture de opqrstu à partir de la position 12+7 =19
    etc


    --------------------------------------


    J’ai essayé de comprendre ce qui peut expliquer un rapport de vitesses de 1 à 5,5 entre mon code et le tien, DelphiManiac.
    Comme tu as parlé du fait que tu as modifié mon code pour éviter qu’il s’écrive dessus (ce qui n’est ni une erreur ni un problème, comme on vient de le voir), je me suis fixé sur la question de savoir si le mode d’enregistrement , en mode ’rb+’ ou en mode ’wb+’, pouvait être le facteur influençant la vitesse.

    J’ai fait des tas de comparaisons, en faisant aussi varier les paramètres (taille du fichier traité, taille des queues de lignes vides, taille de chunk, etc),
    - de mon code en ’rb+’ (auto-écrasement du fichier)
    - de mon code modifié en ’wb+’ (recopiage dans un autre fichier distinct)
    - de ton code en ’wb+’
    - de ton code modifié en ’rb+’

    Mais j’ai arrêté ces comparaisons stériles dans lesquelles je me perdais car les résultats étaient trop fluctuants.
    J’ai alors pensé qu’il fallait comparer chacun des 3 volets qui constituent le problème de façon isolée
    - détecter où commencent les denières lignes vides de la fin
    - détecter où finissent les premières lignes vides en tête
    - recopier les données des lignes non vides comprises entre ces deux séquences de lignes vides

    J’ai commencé par le traitement des lignes vides terminales.

    Dans ce qui suit, je ne parle donc plus que du code pour repérer les dernières lignes vides terminales.



    -----------------------------------------



    Le principe de mon code du message #22 pour traiter la fin du fichier repose sur les idées suivantes:

    Si on fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    f = open(fich,’rb’)
    ch = f.read()
    x = ch.rstrip(’\n’).rfind(’\n’)
    rstrip(’\n’) débarasse ch de tous les caractères ’\n’ terminaux, sans avoir besoin de connaître leur nombre. Sur ce qui reste, rfind(’\n’) trouve le début de la dernière ligne non vide.

    Mais pour faire cela, on est obligé d’avoir obtenu ch en lisant tout le fichier.
    Or avec seek(-recul,2) , on peut rétrograder le pointeur de fichier à partir de sa fin et ne lire que la portion de fichier qui suit cette position. Si le début de dernière ligne non vide est trouvé, c’est que seek(-recul,2) a permis de remonter dans le fichier en amont de la séquence terminale de \r et \n.



    Il y a 2 erreurs mineures dans ce code du message #22:
    - dans
    recul = 1
    while x==-1 and nob-recul:
    ....recul += min(5,nob-recul)

    recul = 1 n'est pas obligatoire, recul = 0 est OK

    - une erreur d'indice à la sortie du while
    il faut
    else:
    f.seek(-recul+len(chcut),2)
    f.truncate()
    f.close()

    au lieu de
    f.seek(-recul+len(chcut),1)
    Ça n’a pas de conséquence parce qu’à la sortie du while, le pointeur est positionné sur la fin du fichier, donc 1 ou 2 c’est pareil.





    Les insuffisances de mon code qui le rendent stupide dans le traitement des lignes vides terminales sont les suivantes:

    A/ j’ai traité ce problème de recopiage-ébarbage de fichier sans m’apercevoir que je restais parasité par le précédent problème de détection de la dernière ligne: j’ai codé pour ce second problème comme s’il importait de trouver la dernière ligne non vide.

    Ainsi mon code cherche par rfind(’\n’) le début de la dernière ligne non vide comme preuve qu’on a suffisamment rétrogradé dans le fichier (grâce à seek(-recul,2) ) en amont de tous les \n et \r terminaux.
    La boucle while comporte ainsi une variable x qui recense la position du début de la dernière ligne non vide.

    Mais c’est plus qu’il n’en faut: en faisant rétrograder le pointeur de fichier jusqu'au milieu de la dernière ligne non vide, ceci est suffisant car il suffit de tester si chcut est vide ou non. La recherche de x est donc inutile, cette variable x est superflue: il suffit en fait de regarder si chcut est [ ] ou non.

    En fait, une fois sorti de la boucle while, j’ai bien écrit
    f.seek(-recul+len(chcut),2) dans quoi n’intervient pas x, mais il faut aussi modifier le code dans le while.


    À la suite de cette remarque, j'ai donc corrigé mon code du message #22 en:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        recul = 0
        while 1:
            recul += min(chunk,nob-recul)
            f.seek(-recul,2)
            ch = f.read()
            chcut = ch.rstrip('\n\r')
            if chcut or nob-recul==0:  break
        end0 = nob-recul+len(chcut)
    Cela amène une petite amélioration qui est objectivée par une comparaison (cf plus loin)




    B/ J’aurais dû faire un code plus simple en ne mettant pas de boucles qui obscurcissent la compréhension dans mon code.

    Mais j’ai mis par exemple une boucle pour prendre des valeurs de la variable recul de plus en plus grandes, c’est à dire des rétrogradations par seek(-recul,2) de plus en plus éloignées de la fin, pour les cas où la portion terminale lue n’est pas assez grande dès le premier coup, afin de rétrograder le pointeur en avant de la séquence de lignes vides.
    En codant ainsi, je me suis fait plaisir, mais c’est vrai que ce n’est pas très pédagogique.

    Pour simplifier, j'aurais pu faire l’hypothèse que pour un fichier "normal", en reculant de 3000 caractères dans un fichier à partir de la fin, on va la plupart du temps sortir en amont de la séquence de ’\n’ terminaux.
    De même pour le début du fichier, on peut lire les 3000 premiers caractères et chercher là dedans le début de la première ligne de vrais caractères (non \n , non \r).
    Ce qui aurait donné un code plus simple que tout le monde aurait été capable de compléter




    C/ Mais la véritable cancrerie de mon code, c’est de faire lire de façon répétée l’entièreté de la fin du fichier, par
    f.seek(-recul,2)
    f.read()

    à chaque fois qu’on fait relire la fin avec une nouvelle valeur de la variable recul.

    Tandis que ton code est bien supérieur par l'idée de procèder par morceaux non chevauchants, DelphiManiac; c'est une évidence qu’il faut faire ainsi.





    Il y a cependant quelques bricoles qui ne vont pas dans ton code:


    - la variable optimal_block_size sert à parcourir l’intégralité du fichier de façon rigoureuse, en ayant sa valeur fixée au début comme reste de la division euclidienne de la taille du fichier, et prenant ensuite la valeur de chunk, diviseur dans cette division euclidienne.

    La répétition de l’instruction optimal_block_size = small_chunk_size qui n'est utile que la première fois et fait ensuite tout le temps la même chose inutile (réaffecter la même valeur), ne me paraît pas une bonne idée.


    - l’instruction pos += 1 est mal placée, elle doit intervenir après le bloc if not c in ['\r', '\n']:
    Ou bien, comme je le préfère, il faut ajouter +1 à l’expression f_in.tell() + optimal_block_size - pos.

    EDIT
    d'ailleurs, il faut aussi ajouter +1 dans le code "Wiztricks"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        i = -1
        while maap[i] in ('\r', '\n') and i>-nob:
            i -= 1
        if i==-nob and maap[i] in ('\r', '\n'):
            i -= 1
        end5 = nob + i + 1

    - L’instruction optimal_block_size = small_chunk_size est elle aussi mal placée:
    quand la taille du fichier est inférieure à small_chunk_size (par exemple taille du fichier == 617 et small_chunk_size == 2048), optimal_block_size = f_in.tell() % small_chunk_size affecte la taille du fichier (617) à optimal_block_size et le premier buffer contient donc la totalité du fichier
    ==> le bloc for c in buffer[::-1]: n’est exécuté qu’une seule fois.
    Or optimal_block_size prend la valeur small_chunk_size (2048), qui est trop grande, avant que le programme entre dans ce bloc.


    - la lecture des caractères 1 par 1 est une très forte source de lenteur, comme le montrent les résultats du comparatif de vitesses que j’ai effectué sur plusieurs programmes:
    les 2 programmes qui font un examen caractère par caractère du fichier pris d'un bloc sont au minimum 6,5 fois moins rapides , et les 2 programmes qui font cet examen caractère par caractère sur des morceaux sont au minimum 3 fois moins rapides, que les autres programmes.

    .

  6. #26
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    ...suite du précédent post.....

    J'ai en effet tiré leçon de ton code, DelphiManiac:
    - découpage de la lecture en morceaux
    - combinaison de deux indices,
    pour écrire et comparer plusieurs autres manières de déterminer la position de la séquence de lignes vides terminales.

    9 codes sont regroupés dans l'unique code ci-après permettant d'effectuer une comparaison de leurs exécutions sur un même fichier.

    J’ai un peu modifié tes codes ( fonctions delph et mapbloc) pour les rendre aptes à traiter des cas particuliers comme les 7 autres codes (fichier constitués seulement de lignes vides, fichier sans lignes vides terminales) et pour corriger les erreurs abordées dans le post précédent.
    Ceci pour rendre le plus possible comparables les codes entre eux.

    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
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    from os.path import getsize
    from time import clock
    import mmap
     
     
    chunk = 2048
    fich = input_file = 'zd5.txt'
     
     
    def fbloc(fich):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(fich)
        f = open(fich,'rb+')
     
        te = clock()#-------------------------------------------------------------------
     
        f.seek(-1,2)
        c = f.read()
        end1 = nob
     
        while c in ('\r','\n') and end1>1:
            f.seek(-2,1) # instruction de rebours
            c = f.read(1)
            end1 -= 1
     
        if end1==1 and c in ('\r','\n'):
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
        elif end1==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
     
        tf = clock()#-------------------------------------------------------------------
        f.close()
        print '    ',nob,' ',end1,"  examen 1 par 1 des caracteres du fichier d'un bloc"
        print tf - te," secondes    au moyen de seek(-2,1)"
    fbloc(fich)
    print '\n-------------------------------\n'
     
     
    def mapbloc(input_file):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(input_file)
        f_in = open(input_file, "r+b")
        maap = mmap.mmap(f_in.fileno(), 0)
     
        te = clock()#-------------------------------------------------------------------
     
        i = -1
        while maap[i] in ('\r', '\n') and i>-nob:
            i -= 1
        if i==-nob and maap[i] in ('\r', '\n'):
            i -= 1
        end5 = nob + i + 1
        if end5==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
        elif end5==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
     
        tf = clock()#-------------------------------------------------------------------
        f_in.close()
        print '    ',nob,' ',end5,"  examen 1 par 1 des caracteres du map d'un bloc"
        print tf - te,' secondes       (code "Wiztricks")'
    mapbloc(fich)   
    print
     
     
    def delph(input_file,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(input_file)
        f_in = open(input_file, "r+b")
     
        te = clock()#-------------------------------------------------------------------
     
        f_in.seek(0, 2)
        stop = False
        optimal_block_size = nob % chunk
        while not stop:
            f_in.seek(-optimal_block_size, 1)
            end2 = f_in.tell()
            buffer = f_in.read(optimal_block_size)
            f_in.seek(-optimal_block_size, 1)
     
            pos = 0
            for c in buffer[::-1]:
                pos += 1
                if c not in ['\r', '\n']:
                    end2 = f_in.tell() + optimal_block_size - pos + 1
                    stop = True
                    break
            if end2==0:
                stop = True
            optimal_block_size = chunk
     
        if end2==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
        elif end2==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
     
        tf = clock()#-------------------------------------------------------------------
        f_in.close()
        print '    ',nob,' ',end2,"  examen 1 par 1 des caracteres de morceaux-fichier"
        print tf - te," secondes       (code DelphiManiac)"
    delph(fich,chunk)
    print
     
     
    def mm(input_file,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(input_file)
        f_in = open(input_file, "r+b")
        maap = mmap.mmap(f_in.fileno(), 0)
     
        te = clock()#-------------------------------------------------------------------
     
        stop = False
        for q in xrange(nob,nob%chunk,-chunk):
            ch = maap[q-chunk:q]
            for i in xrange(-1,-chunk,-1):
                if ch[i] not in ['\r', '\n']:
                    stop = True
                    break
            if stop:  break
     
        if nob>=chunk:
            if stop:
                end9 = q + i + 1
            else:
                end9 = 0
                for i in xrange(-1,-nob%chunk,-1):
                    if maap[i] not in ['\r', '\n']:
                        end9 = nob%chunk+i+1
                        break
        else:
            end9 = 0
            for i in xrange(-1,-nob,-1):
                if maap[i] not in ['\r', '\n']:
                    end9 = nob+i+1
                    break
     
        if end9==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
        elif end9==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
     
        tf = clock()#-------------------------------------------------------------------
        f_in.close()
        print '    ',nob,' ',end9,"  examen 1 par 1 des caracteres de morceaux-map"
        print tf - te, " secondes"
    mm(fich,chunk)
    print '\n-------------------------------\n'
     
     
    def fis22(fich,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(fich) # nob = number of bytes
        f = open(fich,'rb+')
     
        te = clock()#-------------------------------------------------------------------
     
        x = -1
        recul = 0
        while x==-1 and nob-recul:
            recul += min(chunk,nob-recul)
            f.seek(-recul,2)
            ch = f.read()
            chcut = ch.rstrip('\n\r')
            x = max(chcut.rfind('\n'),chcut.rfind('\r'))
        end8 = nob-recul+len(chcut)
     
        if end8==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
        elif end8==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
     
        tf = clock()#-------------------------------------------------------------------
        f.close()
        print '    ',nob,' ',end8,"  examen des fins du fichier par rstrip()"
        print tf - te," secondes      (mon code du message #22)"
    fis22(fich,chunk)
    print
     
     
    def fistri22cor(fich,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(fich)
        f = open(fich,'rb+')
     
        te = clock()#-------------------------------------------------------------------
     
        recul = 0
        while 1:
            recul += min(chunk,nob-recul)
            f.seek(-recul,2)
            ch = f.read()
            chcut = ch.rstrip('\n\r')
            if chcut or nob-recul==0:  break
        end0 = nob-recul+len(chcut)
     
        if end0==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
        elif end0==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
     
        tf = clock()#-------------------------------------------------------------------
        f.close()
        print '    ',nob,' ',end0,'  examen des fins du fichier par rstrip()'
        print tf - te," secondes      (mon code du message #22 corrigé)"
    fistri22cor(fich,chunk)
    print '\n-------------------------------\n'
     
     
    def mfstri1(fich,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(fich)
        f = open(fich,'rb+')
        te = clock()#-------------------------------------------------------------------
     
        if nob<=chunk:
            end4 = len(f.read().rstrip('\r\n'))
        else:  
            #recul = chunk
            f.seek(-chunk,2)
            ch = f.read()
            chcut = ch.rstrip('\r\n')
            if chcut:
                end4 = nob-chunk+len(chcut)
            else:
                deuxrecul = 2*chunk
                f.seek(0, 2)
                stop = False
                while not stop:
                    f.seek(-deuxrecul,1)
                    ch = f.read(chunk)
     
                    chcut = ch.rstrip('\r\n')
                    if chcut:
                        end4 = f.tell()-chunk+len(chcut)
                        stop = True
                        break
     
        if end4==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
        elif end4==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
     
        tf = clock()#-------------------------------------------------------------------
        f.close()
        print '    ',nob,' ',end4,'  examen de morceaux-fichier par rstrip() - 1'
        print tf - te," secondes"
    mfstri1(fich,chunk)
    print
     
     
     
    def mfistri2(fich,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(fich)
        f = open(fich,'rb+')
     
        te = clock()#-------------------------------------------------------------------
     
        if nob<=chunk:
            end3 = len(f.read().rstrip('\r\n'))
        else:    
            f.seek(-chunk,2)
            ch = f.read()
            chcut = ch.rstrip('\r\n')
            if chcut:
                end3 = nob-chunk+len(chcut)
            else:
                recul = chunk
                f.seek(-recul,1)
                stop = False
                while not stop and recul:
                    recul += min(recul,nob-recul)
                    f.seek(-recul,1)
                    ch = f.read(recul)
                    f.seek(-recul,1)
     
                    chcut = ch.rstrip('\r\n')
                    if chcut:
                        end3 = f.tell()+len(chcut)
                        stop = True
                        break
     
        if end3==0:
            print "Il n'y a que des caracteres \\r et \\n dans le fichier"
        elif end3==nob:
            print "Il n'y a pas de \\n ou \\r à la fin du fichier."
     
        tf = clock()#-------------------------------------------------------------------
        f.close()
        print '    ',nob,' ',end3,"  examen de morceaux-fichier par rstrip() - 2"
        print tf - te," secondes"
    mfistri2(fich,chunk)
    print '\n-------------------------------\n'
     
     
     
    def mmstrip(input_file,chunk):#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        nob = getsize(input_file)
        f_in = open(input_file, "r+b")
        maap = mmap.mmap(f_in.fileno(), 0)
     
        te = clock()#-------------------------------------------------------------------
     
        for q in xrange(nob,nob%chunk,-chunk):
            chcut = maap[q-chunk:q].rstrip('\r\n')
            if chcut:  break
     
        if nob>=chunk:
            if chcut:
                end6 = q-chunk+len(chcut)
            else:
                end6 = len(maap[0:nob%chunk].rstrip('\r\n'))
            if end6==nob:
                print "Il n'y a pas de \\n ou \\r à la fin du fichier."
        else:
            end6 = len(maap[:].rstrip('\r\n'))
        if end6==0:
            print "Il n'y a pas d'autres caracteres que \\r et \\n dans ce fichier"
     
        tf = clock()#-------------------------------------------------------------------
        f_in.close()
        print '    ',nob,' ',end6, "  examen de morceaux-map     par rstrip()"
        print tf - te," secondes"
    mmstrip(fich,chunk)
    print





    Voici la synthèse de plusieurs résultats donnés par ce programme après avoir été lancé sur le fichier de 17 Mo que tu as donné en lien, avec chunk = 2048.
    chunk est la taille des morceaux qui sont découpés dans le fichier avant leur examen, dans certains des codes.

    La synthèse consiste en le fait que j'ai fait tourner le programme un grand nombre de fois et que j'ai relevé pour chacun des codes le temps d'exécution le plus petit: c'est ce temps qui, ne tenant pas compte des variations aléatoires dues à on ne sait quoi, mesure le mieux l'efficacité intrinsèque d’un code et permet sa comparaison.


    17915250 17893888 examen 1 par 1 des caracteres du fichier d'un bloc
    0.929516460891 secondes au moyen de seek(-2,1)

    -------------------------------

    17915250 17893888 examen 1 par 1 des caracteres du map d'un bloc
    0.0586912582465 secondes (code "Wiztricks")

    17915250 17893888 examen 1 par 1 des caracteres de morceaux-fichier
    0.0305452229263 secondes (code DelphiManiac)

    17915250 17893888 examen 1 par 1 des caracteres de morceaux-map
    0.0270394701002 secondes

    -------------------------------

    17915250 17893888 examen des fins du fichier par rstrip()
    0.00892878843541 secondes (mon code du message #22)

    17915250 17893888 examen des fins du fichier par rstrip()
    0.00855723283266 secondes (mon code du message #22 corrigé)

    -------------------------------

    17915250 17893888 examen de morceaux-fichier par rstrip() - 1
    0.00193097167377 secondes

    17915250 17893888 examen de morceaux-fichier par rstrip() - 2
    0.00173066688643 secondes

    -------------------------------

    17915250 17893888 examen de morceaux-map par rstrip()
    0.00138565096961 secondes




    OBSERVATIONS

    En prenant comme référence l’examen fruste du fichier caractère par caractère (0.9295) :

    - le recours à mmap tout seul divise le temps par 16 (0,0586)
    - le recours au seul découpage de morceaux à examiner divise le temps par 30 (0.03064): code DelphiManiac
    - le recours concommitant au découpage en morceaux et à mmap divise le temps par 34

    Le recours à mmap n’est manifestement pas ce qui apporte le plus de vitesse. C'est bien le découpage par morceaux qui est l'apport le plus judicieux.

    - le recours à seulement rstrip() divise le temps par 108 (0,00855)

    L'algorithme avec rstrip() est plus efficace que les deux autres ensemble.

    - le recours concommitant au découpage de morceaux et à rstrip() divise le temps jusqu’à 537 fois (0,00173)
    - enfin si on utilise les 3 méthodes à la fois, le code est 671 fois plus rapide (0.001385) !!






    La vitesse de mes deux codes, qui n'utilisent que rstrip() , est à mi-chemin entre celles des codes les plus lents (1 par 1) et des codes les plus rapides (morceaux + rstrip).
    Ce constat est fait pour une une même valeur de chunk, égale à 2048. Or cette vitesse est fortement dépendante de cette valeur de chunk: si on fait varier le chunk passé à mes deux codes (fonctions fis22 et fistri22cor), la vitesse varie ansi

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    chunk temps d exécution (secondes)
    5      3,1
    10     1,5 
    16     0,94
    32     0,47
    64     0,24
    128    0,12
    256    0,066
    512    0,035
    1024   0,016
    2048   0,0089
    4096   0,0053
    8192   0,0030
    16384  0,0025

    Dans la comparaison que tu as faite de mon code #22 et du tien, DelphiManiac, tu as gardé un chunk égal à 5 dans mon code,
    si je me fie à
    «avec juste une modif au début pour générer par copie un fichier de test»
    et
    à ce qui est écrit dans mon code dans ton message #23.
    Alors que chunk=2048 dans ton code.

    Cette différence explique la lenteur de mon code dans ta comparaison: 0.444 secondes contre 0.081 secondes.

    En reprenant cette comparaison,
    j'ai mesuré le rapport vitesse de ton code/ vitesse de mon code:
    avec chunk=5 partout dans mon code, ce rapport est 5,48
    avec chunk=2048 seulement pour la recherche des vides terminales, il tombe à 1,45
    avec chunk=2048 en sus pour la partie recopiage dans mon code, ce rapport devient 0,65,
    c'est à dire que après mise à égalité des conditions, le code avec rstrip() est plus rapide que ton code, DelphiManiac





    Concrètement, j'ai bien conscience que la recherche d'un tel fignolage n'est pas foncièrement important, car je n'oublie pas que le fichier de 17 Mo testé comporte 21363 lignes vides à la fin, ce qui n'est pas un cas de figure qu'on est susceptible de rencontrer fréquemment et que l'optimisation de son traitement peut être considéré comme un exercice un peu vain. Sur un fichier avec un nombre de lignes vides raisonnables, les différences de vitesses entre codes ne se manifestent pas car il n'y a pas à renouveler des lectures par morceaux de la fin du fichier.

    Mais d'un point de vue théorique et algorithmique, je trouve intéressant d'acquérir une connaissance intime des fonctions et des différentes pratiques utilisables dans un programme. Ça ne peut que servir plus tard à un moment ou à un autre.

    Par ailleurs, je trouve le code le plus rapide d'une simplicité biblique et d'une facilité à être compris qui me plaîsent bien.







    As tu laissé tomber la partie où il doit lire le fichier ligne par ligne ?
    Un peu. Je ne me fixe pas de toujours répondre précisément aux questions.
    Parfois je me lance dans un code pour le plaisir de voir si et comment on peut faire tel ou tel sujet que m’inspire une question.
    Parfois, il est mieux de suggérer au questionneur une autre façon de procéder.
    Parfois la question est tellement mal ficelée que je n’ai pas envie de m’enquiquiner et que je préfère coder ce que bon me semble et laisser le questionneur se débrouiller avec les idées qu’il pourra piocher dans ma réponse.
    En l’occurence, il y a des 3, et comme je l’ai écrit:
    J’ai pensé qu’il valait mieux recopier un fichier en l’ébavurant avec mon code avant de l’afficher, c’est plus rapide et fait une fois pour toutes.


    .

  7. #27
    Membre émérite
    Avatar de DelphiManiac
    Homme Profil pro
    Homme à tout faire
    Inscrit en
    Mars 2002
    Messages
    1 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Homme à tout faire
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 147
    Points : 2 533
    Points
    2 533
    Par défaut
    Juste une petite précision, le but du chunk_size est de se caler sur la taille du cluster du disque où est stocké le fichier. J'ai juste eu la flegme de chercher dans quelles librairie je peux trouver la taille du cluster d'un disque.

    Le but de cette opération est de ne pas demander à l'os à lire des "bouts" de fichiers qui sont à cheval sur 2 clusters (2 lectures du coup)

    C'étais aussi le but de optimal_block_size qui se cale sur un nombre multiple de cluster.

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. SPOOL : Effacer dernière ligne vide du fichier.
    Par Ujitsu dans le forum Sql*Plus
    Réponses: 6
    Dernier message: 30/07/2007, 18h15
  2. Effacer la dernière ligne d'un fichier texte
    Par marsupilami34 dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 31/05/2007, 11h52
  3. Supprimer 1ère et dernière ligne d'un fichier
    Par yabbiyou dans le forum MATLAB
    Réponses: 3
    Dernier message: 02/03/2007, 09h05
  4. Comment lire la dernière ligne d'un fichier text.
    Par Ardely dans le forum Delphi
    Réponses: 29
    Dernier message: 20/01/2007, 23h29
  5. Réponses: 2
    Dernier message: 19/09/2006, 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