fichier.tell() la contradiction

Quelqu'un arrive à savoir pourquoi lorsque vous parcourez un fichier de cette façon:

Entrée:

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 toujours le mauvais index de fichier de tell (), cependant, si j'utilise readline, j'obtiens l'index approprié pour tell ():

Entrée:

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

Je cours Python 2.7.1 BTW.

39
demandé sur Martijn Pieters 2013-01-03 22:39:14

3 réponses

L'utilisation de fichiers ouverts en tant qu'itérateur utilise un tampon de lecture anticipée pour augmenter l'efficacité. Par conséquent, le pointeur de fichier avance en grandes étapes à travers le fichier que vous faites une boucle sur les lignes.

À partir des objets du fichier documentation:

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

Si vous devez vous appuyer sur .tell(), n'utilisez pas l'objet file comme itérateur. Vous pouvez transformer .readline() en un itérateur à la place (au prix d'une perte de performance):

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

Cela utilise le iter() Fonction sentinel argument pour transformer tout appelable en un itérateur.

56
répondu Martijn Pieters 2013-01-03 18:46:51

La réponse réside dans la partie suivante du code source 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 voir, l'interface itérateur de file lit le fichier en blocs de 8 Ko. Cela explique pourquoi f.tell() se comporte comme il le fait.

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

12
répondu NPE 2013-01-03 18:44:12

J'ai rencontré le même problème de tampon de lecture anticipée et je l'ai résolu en utilisant la suggestion de Martijn .

J'ai depuis généralisé ma solution pour tous ceux qui cherchent à faire de telles choses:

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

Bonne analyse CSV!

0
répondu Loisaida Sam 2018-08-10 19:23:14