web-dev-qa-db-fra.com

inconsistance de file.tell ()

Est-ce que quelqu'un sait pourquoi, lorsque vous parcourez un fichier de cette façon:

Contribution:

f = open('test.txt', 'r')
for line in f:
    print "f.tell(): ",f.tell()

Sortie:

f.tell(): 8192
f.tell(): 8192
f.tell(): 8192
f.tell(): 8192

J'obtiens systématiquement le mauvais index de fichier de tell (). Cependant, si j'utilise readline, j'obtiens l'index approprié pour tell ():

Contribution:

f = open('test.txt', 'r')
while True:
    line = f.readline()
    if (line == ''):
        break
    print "f.tell(): ",f.tell()

Sortie:

f.tell(): 103
f.tell(): 107
f.tell(): 115
f.tell(): 124

J'exécute python 2.7.1 BTW.

40
nigp4w rudy

L'utilisation de fichiers ouverts en tant qu'itérateur utilise un tampon de lecture anticipée pour améliorer l'efficacité. Par conséquent, le pointeur de fichier avance par étapes importantes au fur et à mesure que vous parcourez les lignes.

Depuis le Objets du fichier documentation:

Afin de faire de la boucle for le moyen le plus efficace de boucler sur les lignes d'un fichier (opération très courante), la méthode next() utilise un tampon de lecture anticipée masqué. L'utilisation d'un tampon de lecture anticipée a pour conséquence que la combinaison de next() avec d'autres méthodes de fichier (comme readline()) ne fonctionne pas correctement. Cependant, utiliser seek() pour repositionner le fichier sur une position absolue videra le tampon de lecture anticipée.

Si vous devez vous fier à .tell(), n'utilisez pas l'objet fichier en tant qu'itérateur. Vous pouvez plutôt transformer .readline() en itérateur (au prix d'une perte de performance):

for line in iter(f.readline, ''):
    print f.tell()

Ceci utilise l'argument iter() functionsentinel pour transformer tout appelable en itérateur.

58
Martijn Pieters

La réponse se trouve dans la partie suivante du code source de Python 2.7 (fileobject.c):

#define READAHEAD_BUFSIZE 8192

static PyObject *
file_iternext(PyFileObject *f)
{
    PyStringObject* l;

    if (f->f_fp == NULL)
        return err_closed();
    if (!f->readable)
        return err_mode("reading");

    l = readahead_get_line_skip(f, 0, READAHEAD_BUFSIZE);
    if (l == NULL || PyString_GET_SIZE(l) == 0) {
        Py_XDECREF(l);
        return NULL;
    }
    return (PyObject *)l;
}

Comme vous pouvez le constater, l’interface itérateur de file lit le fichier par blocs de 8 Ko. Ceci explique pourquoi f.tell() se comporte comme il le fait.

La documentation suggère c'est fait pour des raisons de performances (et ne garantit pas une taille particulière du tampon readahead).

12
NPE

J'ai rencontré le même problème de tampon de lecture anticipée et l'ai résolu à l'aide de suggestion de Martijn .

Depuis, j'ai généralisé ma solution à quiconque cherche à faire de telles choses:

https://github.com/loisaidasam/csv-position-reader

Bonne analyse CSV!

0
Loisaida Sam