Lire et écrire dans un fichier

Comme il existe un objet int ou float il existe également un objet file. Pour lire et écrire dans un fichier il faut donc apprendre à utiliser cet objet.

La fonction open()

Avant de lire et écrire dans un fichier on dit qu'on "ouvre" ce fichier. Cette étape passe par la fonction open() qui crée un objet file.

Lorsqu'on travaille avec un fichier, il est préférable de s'assurer que certaines opérations sont exécutées avant et après l'ouverture. Pour ce faire on utilise un contexte. Il permet de séparer un bloc d’instructions donné. Python se charge alors d'exécuter des opérations de contrôle avant et après le bloc d'instructions.

Voici la syntaxe :

with objet as alias:
    # instructions sur alias

Lors de la création de alias et après l'exécution des instructions le concernant python fait le nécessaire pour nous simplifier la gestion de certaines erreurs.

On se donne un fichier donnees.dat :

# titre du fichier
1. 2.1
2. 2.9
3. 4.2
4. 5.05
5. 5.85
6. 6.95
7. 8.1
8. 9.
9. 10.2
10. 10.9

On va donc écrire :

>>> with open("donnees.dat", "r") as f:
...     contenu = f.read()
>>> print(contenu)
# titre du fichier
1. 2.1
2. 2.9
3. 4.2
4. 5.05
5. 5.85
6. 6.95
7. 8.1
8. 9.
9. 10.2
10. 10.9

On a utilisé la méthode read() pour lire tout le contenu du fichier.

La fonction open() :

  • Le premier argument est le nom du fichier (son chemin pour être précis)
  • Le second argument donne le mode d'accès :
    • "r" pour read, lecture du fichier
    • "w" pour write, écriture du fichier
    • "a" pour append, écriture à la fin du fichier

Lecture ligne par ligne

On va maintenant lire ligne par ligne le fichier pour récupérer dans une liste la première colonne (x) et dans une autre liste la deuxième colonne (y).

with open("donnees.dat", "r") as f:
    x = list()
    y = list()
    for line in f:
        if "#" in line:
            # on saute la ligne
            continue
        data = line.split()
        x.append(data[0])
        y.append(data[1])
print(x)
print(y)
['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '10.']
['2.1', '2.9', '4.2', '5.05', '5.85', '6.95', '8.1', '9.', '10.2', '10.9']

Remarques :

  • La fonction split() découpe une chaîne de caractères suivant les espaces qu'elle contient.
  • L'instruction continue impose à la boucle de passer à l'itération suivante sans exécuter les instructions suivantes.
  • On remarquera que la liste contient des chaines de caractères qu'il faudrait convertir en nombre flottant avec la fonction float().

D'autres méthodes existent : readlines() lit toutes les lignes et retourne une liste :

with open("donnees.dat", "r") as f:
    lines = f.readlines()
print(lines)
['1. 2.1\n', '2. 2.9\n', '3. 4.2\n', '4. 5.05\n', '5. 5.85\n', '6. 6.95\n',
'7. 8.1\n', '8. 9.\n', '9. 10.2\n', '10. 10.9\n']

On pourra se servir de strip() qui supprime les caractères spéciaux dans la chaine de caractères.

Nous verrons dans la partie sur numpy qu'il existe une façon très efficace de lire un fichier de ce type.

Écrire un fichier

Essayons par exemple de calculer les valeurs d'une fonction et d'écrire les résultats dans un fichier. La méthode write() attend une chaîne de caractères.

def fonction(x):
    return 2 * x**2 - 9 * x + 4

x = range(-5, 6, 1)
with open("valeurs.dat", "w") as f:
    f.write("# Un titre\n")
    for xi in x:
        valeurs = "%12.6f %12.6f\n" % (xi, fonction(xi))
        f.write(valeurs)

Dans la boucle, xi et fonction(xi) sont convertis en chaînes de caractères avec l'utilisation d'un format. On aurait pu simplement écrire str(xi) et str(fonction(xi)) mais il faut penser à mettre un espace entre les deux. Le format %12.6f indique qu'on va écrire un nombre flottant, f, sur 12 caractères, avec 6 chiffres après la virgule.

Note : On n'oubliera pas le caractère de fin de ligne \n, sinon python ajoute le texte à la suite. Il ne passe pas à la ligne suivante après chaque appel à la méthode write().

Autre solution : nous allons écrire toutes les valeurs dans une chaîne de caractères puis écrire cette chaîne dans le fichier.

def fonction(x):
    return 2 * x**2 - 9 * x + 4

x = range(-5, 6, 1)
lines = "# Un titre÷\n"
for xi in x:
    lines += "%12.6f %12.6f\n" % (xi, fonction(xi))

with open("valeurs.dat", "w") as f:
    f.write(lines)

Extraire des données

Le code suivant présente deux techniques pour extraire des données d'un fichier. Nous allons travailler sur le fichier soup.txt qui est affiché ci-dessous.

Combinons un test et split()

Premier cas simple

L'objectif est de rechercher la valeur de l'énergie écrite sur la ligne 39 du fichier soup.txt.

SCF Done:  E(RPBE-PBE) =  -548.263942119     A.U. after   14 cycles

Voici le code d'une fonction qui renvoie cette énergie :

def read_SCF(fichier):
    """ Lit la ligne SCF Done dans le fichier et retourne l'énergie """
    with open(fichier, "r") as f:
        for line in f:
            if "SCF Done:" in line:
                mots = line.split()
                energie = float(mots[4])
    return energie

Le principe est de chercher si "SCF Done" est sur la ligne courante, puis de la découper avec split et d'enregistrer la bonne valeur.

Supposons que cette ligne, contenant l'énergie, apparaisse plusieurs fois dans le fichier, on peut construire une liste contenant toutes les valeurs en seulement deux lignes de code :

with open(fichier, "r") as f:
    energies = [float(line.split()[4]) for line in f if "SCF Done:" in line]

Un cas plus complexe

On veut maintenant lire les valeurs de la section excitation energies sur les lignes 68 à 85. Par exemple, sur la ligne suivante, on cherche à lire l'énergie, la longueur d'onde et la force d'oscillateur (f) :

Excited State   2:      Singlet-A"     4.2831 eV  289.47 nm  f=0.0000  <S**2>=0.000

Voici une proposition :

def read_td(fichier):
    """ Lit la section excitation energies """

    # les résultats seront dans une liste
    transitions = list()

    with open(fichier, "r") as f:
        line = f.readline()
        td = False
        # lecture tant qu'on n'est pas à la fin du fichier
        while line != "":
            if "Excitation energies and oscillator strengths:" in line:
                td = True

            if td:
                if "Excited State" in line:
                    val = line.split()
                    energie = float(val[4])
                    nm = float(val[6])
                    force = float(val[8].strip("f="))
                    transitions.append((energie, nm, force))
            line = f.readline()

    return transitions

transitions = read_td("soup.txt")
print(transitions)

Ce qui donnera :

[(3.9281, 315.64, 0.0054), (4.2831, 289.47, 0.0), (6.4162, 193.24, 0.0457), (7.949, 155.97, 0.0013)]

Mieux que split les expressions régulières

Les expressions régulières permettent de réaliser avec peu de lignes des choses très complexes mais elles sont également difficiles à lire. Elles permettent d'identifier une chaîne de caractères suivant des critères précis. Nous allons reprendre l'exemple précédent.

Voici un outils en ligne très pratique pour tester vos expressions régulières : regex101

import re
def read_td(fichier):
    """ Lit la section excitation energies """

    # cette expression régulière repère tout type de nombre flottant
    float_patt = re.compile("\s*([+-]?\d+\.\d+)")

    transitions = list()

    # read in file
    with open(fichier, "r") as f:
        line = f.readline()
        td = False
        # lecture tant qu'on n'est pas à la fin du fichier
        while line != "":
            if re.search("^Excitation energies and oscillator strengths:", line):
                td = True

            if td:
                if re.search("^Excited State\s*\d", line):
                    val = [float(v) for v in float_patt.findall(line)]
                    transitions.append(tuple(val[0:3]))
            line = f.readline()
    return transitions

transitions = read_td("soup.txt")
print(transitions)

Commentaires :

  • Il faut utiliser le module re pour regular expression
  • re.search("regex", line) est équivalent à if "regex" in line:
  • float_patt.findall(line) renvoie toutes les valeurs qui correspondent au modèle défini par float_patt
  • "\s*([+-]?\d+\.\d+)" nombre à virgule avec éventuellement un signe : +0.1 ou 3.14 ou 100. ou -27.21
  • Quelques éléments de syntaxe :
    • \d pour digit
    • \s pour whitespace
    • * caractère précédent de 0 à un nombre de fois illimité
    • + caractère précédent de 0 à un nombre de fois illimité
    • ? caractère précédent 0 ou 1 fois
    • ^ début de la ligne

Quelques exemples d'expressions régulières

Le fichier soup.txt :

télécharger le fichier

Standard basis: 6-31G(d,p) (5D, 7F)
There are    36 symmetry adapted cartesian basis functions of A'  symmetry.
There are    13 symmetry adapted cartesian basis functions of A"  symmetry.
There are    33 symmetry adapted basis functions of A'  symmetry.
There are    13 symmetry adapted basis functions of A"  symmetry.
   46 basis functions,   108 primitive gaussians,    49 cartesian basis functions
   16 alpha electrons       16 beta electrons
      nuclear repulsion energy       106.6890740164 Hartrees.
NAtoms=    3 NActive=    3 NUniq=    3 SFac= 1.00D+00 NAtFMM=   60 NAOKFM=F Big=F
Integral buffers will be    131072 words long.
Regular integral format.
Two-electron integral symmetry is turned on.
One-electron integrals computed using PRISM.
NBasis=    46 RedAO= T EigKep=  3.97D-02  NBF=    33    13
NBsUse=    46 1.00D-06 EigRej= -1.00D+00 NBFU=    33    13
ExpMin= 1.17D-01 ExpMax= 2.19D+04 ExpMxC= 3.30D+03 IAcc=1 IRadAn=         1 AccDes= 0.00D+00
Harris functional with IExCor= 1009 and IRadAn=       1 diagonalized for initial guess.
HarFok:  IExCor= 1009 AccDes= 0.00D+00 IRadAn=         1 IDoV= 1 UseB2=F ITyADJ=14
ICtDFT=  3500011 ScaDFX=  1.000000  1.000000  1.000000  1.000000
FoFCou: FMM=F IPFlag=           0 FMFlag=      100000 FMFlg1=           0
        NFxFlg=           0 DoJE=T BraDBF=F KetDBF=T FulRan=T
        wScrn=  0.000000 ICntrl=     500 IOpCl=  0 I1Cent=   200000004 NGrid=           0
        NMat0=    1 NMatS0=      1 NMatT0=    0 NMatD0=    1 NMtDS0=    0 NMtDT0=    0
Petite list used in FoFCou.
Initial guess orbital symmetries:
      Occupied  (A') (A') (A') (A') (A') (A') (A") (A') (A') (A')
                (A') (A') (A") (A") (A') (A')
      Virtual   (A") (A') (A') (A') (A") (A') (A') (A") (A') (A")
                (A') (A') (A") (A') (A') (A") (A') (A') (A') (A')
                (A") (A") (A') (A') (A') (A") (A") (A') (A') (A')
The electronic state of the initial guess is 1-A'.
Keep J ints in memory in symmetry-blocked form, NReq=1482832.
Requested convergence on RMS density matrix=1.00D-08 within 128 cycles.
Requested convergence on MAX density matrix=1.00D-06.
Requested convergence on             energy=1.00D-06.
No special actions if energy rises.
Integral accuracy reduced to 1.0D-05 until final iterations.
Initial convergence to 1.0D-05 achieved.  Increase integral accuracy.
SCF Done:  E(RPBE-PBE) =  -548.263942119     A.U. after   14 cycles
           NFock= 14  Conv=0.63D-08     -V/T= 2.0030
ExpMin= 1.17D-01 ExpMax= 2.19D+04 ExpMxC= 3.30D+03 IAcc=3 IRadAn=         5 AccDes= 0.00D+00
HarFok:  IExCor=  205 AccDes= 0.00D+00 IRadAn=         5 IDoV=-2 UseB2=F ITyADJ=14
ICtDFT= 12500011 ScaDFX=  1.000000  1.000000  1.000000  1.000000
Range of M.O.s used for correlation:     8    46
NBasis=    46 NAE=    16 NBE=    16 NFC=     7 NFV=     0
NROrb=     39 NOA=     9 NOB=     9 NVA=    30 NVB=    30
Keep J ints in memory in symmetry-blocked form, NReq=1810774.
Orbital symmetries:
      Occupied  (A') (A') (A') (A') (A') (A") (A') (A') (A') (A')
                (A") (A') (A') (A") (A') (A')
      Virtual   (A") (A') (A') (A') (A") (A') (A') (A") (A") (A')
                (A') (A') (A") (A') (A") (A') (A') (A') (A') (A')
                (A") (A") (A') (A') (A') (A") (A") (A') (A') (A')
   16 initial guesses have been made.
Convergence on wavefunction:    0.001000000000000
Iteration     1 Dimension    16 NMult     0 NNew     16
CISAX will form    16 AO SS matrices at one time.
Iteration     2 Dimension    32 NMult    16 NNew     16
Iteration     3 Dimension    36 NMult    32 NNew      4
Iteration     4 Dimension    39 NMult    36 NNew      3
Iteration     5 Dimension    40 NMult    39 NNew      1
***********************************************************************
Excited states from <AA,BB:AA,BB> singles matrix:
***********************************************************************

Ground to excited state transition densities written to RWF  633

Excitation energies and oscillator strengths:

Excited State   1:      Singlet-A"     3.9281 eV  315.64 nm  f=0.0054  <S**2>=0.000
     16 -> 17         0.70680
This state for optimization and/or second-order correction.
Total Energy, E(TD-HF/TD-KS) =  -548.119587803
Copying the excited state density for this state as the 1-particle RhoCI density.

Excited State   2:      Singlet-A"     4.2831 eV  289.47 nm  f=0.0000  <S**2>=0.000
     15 -> 17         0.70716

Excited State   3:      Singlet-A'     6.4162 eV  193.24 nm  f=0.0457  <S**2>=0.000
     14 -> 17         0.65477
     15 -> 18         0.20991
     16 -> 19         0.19021

Excited State   4:      Singlet-A"     7.9490 eV  155.97 nm  f=0.0013  <S**2>=0.000
     13 -> 17         0.70470
SavETr:  write IOETrn=   770 NScale= 10 NData=  16 NLR=1 NState=    4 LETran=      82.

Symmetry A'   KE= 5.029717769773D+02
Symmetry A"   KE= 4.367837409063D+01
1\1\GINC-DNAS-NODE33\SP\RPBEPBE TD-FC\6-31G(d,p)\O2S1\GVALLVER\22-Sep-
2015\0\\# PBEPBE/6-31G** 5d td=(nstates=4)\\SO2 molecule\\0,1\S,0,4.99
586601,4.99305474,5.\O,0,6.44958723,4.99309717,5.\O,0,4.28472976,6.260
9081,5.\\Version=EM64L-G09RevD.01\State=1-A'\HF=-548.2639421\RMSD=6.29
7e-09\PG=CS [SG(O2S1)]\\@


HEAVEN'S NET CASTS WIDE.
THOUGH ITS MESHES ARE COARSE, NOTHING SLIPS THROUGH.

                                         -- LAO-TSU
Job cpu time:       0 days  0 hours  0 minutes  6.1 seconds.
File lengths (MBytes):  RWF=      6 Int=      0 D2E=      0 Chk=      1 Scr=      1
Normal termination of Gaussian 09 at Tue Sep 22 21:55:11 2015.
Fin  execution du job  : mar. sept. 22 21:55:11 CEST 2015

results matching ""

    No results matching ""