Lecture de lignes spécifiques uniquement

215

J'utilise une boucle for pour lire un fichier, mais je veux seulement lire des lignes spécifiques, disons les lignes # 26 et # 30. Existe-t-il une fonctionnalité intégrée pour y parvenir?

Merci

eozzy
la source
1
Dup possible: stackoverflow.com/questions/620367/…
Adam Matan

Réponses:

253

Si le fichier à lire est volumineux et que vous ne souhaitez pas lire l'intégralité du fichier en mémoire à la fois:

fp = open("file")
for i, line in enumerate(fp):
    if i == 25:
        # 26th line
    elif i == 29:
        # 30th line
    elif i > 29:
        break
fp.close()

Notez que i == n-1pour la ne ligne.


En Python 2.6 ou version ultérieure:

with open("file") as fp:
    for i, line in enumerate(fp):
        if i == 25:
            # 26th line
        elif i == 29:
            # 30th line
        elif i > 29:
            break
Alok Singhal
la source
8
enumerate(x)utilise x.next, il n'a donc pas besoin de la totalité du fichier en mémoire.
Alok Singhal
3
Mon petit boeuf avec c'est que A) Vous voulez utiliser avec au lieu de la paire ouverte / fermée et donc garder le corps court, B) Mais le corps n'est pas si court. Cela ressemble à un compromis entre vitesse / espace et être Pythonic. Je ne sais pas quelle serait la meilleure solution.
Hamish Grubijan
5
avec est surfaite, python s'entend bien depuis plus de 13 ans sans lui
Dan D.
38
@Dan D. L'électricité est surfaite, l'humanité s'en est bien sortie pendant plus de 200 mille ans sans elle. ;-) 'avec' le rend plus sûr, plus lisible et plus court d'une ligne.
Romain Vincent
9
pourquoi utiliser pour boucle, je ne pense pas que vous compreniez le sens de big file. La boucle mettra des années à atteindre l'indice
devssh
159

La réponse rapide:

f=open('filename')
lines=f.readlines()
print lines[25]
print lines[29]

ou:

lines=[25, 29]
i=0
f=open('filename')
for line in f:
    if i in lines:
        print i
    i+=1

Il existe une solution plus élégante pour extraire de nombreuses lignes: linecache (gracieuseté de "python: comment passer à une ligne particulière dans un énorme fichier texte?" , Une précédente question stackoverflow.com).

Citant la documentation python liée ci-dessus:

>>> import linecache
>>> linecache.getline('/etc/passwd', 4)
'sys:x:3:3:sys:/dev:/bin/sh\n'

Remplacez le 4par le numéro de ligne souhaité, et c'est parti. Notez que 4 apporterait la cinquième ligne car le compte est basé sur zéro.

Si le fichier peut être très volumineux et causer des problèmes lors de la lecture en mémoire, il peut être judicieux de suivre les conseils de @ Alok et d'utiliser enumerate () .

De conclure:

  • Utilisez fileobject.readlines()ou for line in fileobjectcomme solution rapide pour les petits fichiers.
  • Utilisez linecachepour une solution plus élégante, qui sera assez rapide pour lire de nombreux fichiers, possible à plusieurs reprises.
  • Suivez les conseils de @ Alok et utilisez-leenumerate() pour les fichiers qui peuvent être très volumineux et qui ne rentrent pas en mémoire. Notez que l'utilisation de cette méthode peut ralentir car le fichier est lu séquentiellement.
Adam Matan
la source
7
Agréable. Je viens de regarder la source du linecachemodule et on dirait qu'il lit tout le fichier en mémoire. Donc, si l'accès aléatoire est plus important que l'optimisation de la taille, linecachec'est la meilleure méthode.
Alok Singhal
7
avec linecache.getlin ('some_file', 4) j'obtiens la 4ème ligne, pas la 5ème.
Juan
fait amusant: si vous utilisez un ensemble au lieu de la liste dans le deuxième exemple, vous obtenez le temps d'exécution O (1). Rechercher dans une liste est O (n). Les ensembles internes sont représentés comme des hachages, et c'est pourquoi vous obtenez le temps d'exécution O (1). pas un gros problème dans cet exemple, mais si vous utilisez une grande liste de nombres et que vous vous souciez de l'efficacité, les ensembles sont la voie à suivre.
rady
linecachesemble désormais fonctionner uniquement pour les fichiers source python
Paul H
Vous pouvez également utiliser linecache.getlines('/etc/passwd')[0:4]pour lire les première, deuxième, troisième et quatrième lignes.
zyy
30

Une approche rapide et compacte pourrait être:

def picklines(thefile, whatlines):
  return [x for i, x in enumerate(thefile) if i in whatlines]

cela accepte tout objet de type fichier ouvert thefile(laissant à l'appelant le soin de l'ouvrir à partir d'un fichier disque, ou via, par exemple, un socket ou un autre flux de type fichier) et un ensemble d'index de ligne de base zéro whatlines, et retourne un liste, avec une faible empreinte mémoire et une vitesse raisonnable. Si le nombre de lignes à renvoyer est énorme, vous préférerez peut-être un générateur:

def yieldlines(thefile, whatlines):
  return (x for i, x in enumerate(thefile) if i in whatlines)

ce qui n'est fondamentalement bon que pour boucler - notez que la seule différence vient de l'utilisation de parenthèses arrondies plutôt que carrées dans l' returninstruction, faisant une compréhension de liste et une expression de générateur respectivement.

Notez en outre que malgré la mention de "lignes" et de "fichier", ces fonctions sont beaucoup, beaucoup plus générales - elles fonctionneront sur n'importe quel itérable, que ce soit un fichier ouvert ou tout autre, renvoyant une liste (ou un générateur) d'éléments en fonction de leur numéro d'article progressif. Donc, je suggère d'utiliser des noms généraux plus appropriés ;-).

Alex Martelli
la source
@ephemient, je ne suis pas d'accord - le genexp lit en douceur et parfaitement.
Alex Martelli
Excellente et élégante solution, merci! En effet, même les gros fichiers doivent être supportés, avec l'expression générateur. Impossible d'être plus élégant que ça, n'est-ce pas? :)
Samuel Lampa
Belle solution, comment cela se compare-t-il à celui proposé par @AdamMatan? La solution Adam pourrait être plus rapide car elle exploite des informations supplémentaires (les numéros de ligne augmentent de façon monotone) qui pourraient conduire à un arrêt précoce. J'ai un fichier de 10 Go que je ne peux pas charger en mémoire.
Mannaggia
2
@Mannaggia Ce n'est pas assez souligné dans cette réponse, mais whatlinesdevrait être un set, car if i in whatlinesil s'exécutera plus rapidement avec un ensemble plutôt qu'avec une liste (triée). Je ne l'ai pas remarqué en premier et j'ai plutôt conçu ma propre solution laide avec une liste triée (où je n'avais pas à numériser une liste à chaque fois, alors if i in whatlinesque c'est juste cela), mais la différence de performance était négligeable (avec mes données) et cela la solution est beaucoup plus élégante.
Victor K
28

Pour offrir une autre solution:

import linecache
linecache.getline('Sample.txt', Number_of_Line)

J'espère que c'est rapide et facile :)

KingMak
la source
1
J'espère que c'est la solution la plus optimale.
maniac_user
2
Cela lit le fichier entier en mémoire. Vous pourriez aussi bien appeler file.read (). Split ('\ n') puis utiliser les recherches d'index de tableau pour obtenir la ligne d'intérêt ...
duhaime
Pourriez-vous fournir un exemple @duhaime
anon
14

si vous voulez la ligne 7

line = open ("file.txt", "r"). readlines () [7]
MadSc13ntist
la source
14
Soigné. Mais comment faites-vous close()le fichier lorsque vous l'ouvrez de cette façon?
Milo Wielondek
1
@ 0sh devons-nous fermer?
Ooker
1
Oui. nous devons fermer après cela. Lorsque nous ouvrons un fichier en utilisant "avec" ... il se ferme.
reetesh11
10

Par souci d'exhaustivité, voici une autre option.

Commençons par une définition à partir des documents python :

tranche Un objet contenant généralement une partie d'une séquence. Une tranche est créée en utilisant la notation en indice, [] avec des deux-points entre les nombres lorsque plusieurs sont donnés, comme dans variable_name [1: 3: 5]. La notation crochet (indice) utilise des objets slice en interne (ou dans les anciennes versions, __getslice __ () et __setslice __ ()).

Bien que la notation de tranche ne soit pas directement applicable aux itérateurs en général, le itertoolspackage contient une fonction de remplacement:

from itertools import islice

# print the 100th line
with open('the_file') as lines:
    for line in islice(lines, 99, 100):
        print line

# print each third line until 100
with open('the_file') as lines:
    for line in islice(lines, 0, 100, 3):
        print line

L'avantage supplémentaire de la fonction est qu'elle ne lit l'itérateur qu'à la fin. Vous pouvez donc faire des choses plus complexes:

with open('the_file') as lines:
    # print the first 100 lines
    for line in islice(lines, 100):
        print line

    # then skip the next 5
    for line in islice(lines, 5):
        pass

    # print the rest
    for line in lines:
        print line

Et pour répondre à la question d'origine:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]
newtover
la source
1
De loin la meilleure approche lorsque vous travaillez avec des fichiers volumineux. Mon programme est passé de 8 Go + à presque rien. Le compromis était l'utilisation du processeur qui est passée de ~ 15% à ~ 40% mais le traitement réel du fichier était 70% plus rapide. Je ferai ce compromis toute la journée. Merci! 🎉🎉🎉
GollyJer
1
Cela me semble le plus pythonique. Merci!
ipetrik
10

La lecture de fichiers est incroyablement rapide. La lecture d'un fichier de 100 Mo prend moins de 0,1 seconde (voir mon article Lecture et écriture de fichiers avec Python ). Par conséquent, vous devez le lire complètement, puis travailler avec les lignes simples.

Ce que la plupart des réponses font ici n'est pas mauvais, mais un mauvais style. L'ouverture des fichiers doit toujours se faire withcar elle garantit que le fichier est refermé.

Vous devez donc le faire comme ceci:

with open("path/to/file.txt") as f:
    lines = f.readlines()
print(lines[26])  # or whatever you want to do with this line
print(lines[30])  # or whatever you want to do with this line

D'énormes fichiers

Si vous avez un fichier volumineux et que la consommation de mémoire est un problème, vous pouvez le traiter ligne par ligne:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i
Martin Thoma
la source
IMO c'est un très mauvais style de lire un fichier entier de longueur inconnue, juste pour obtenir les 30 premières lignes .. qu'en est-il de la consommation de mémoire .. et qu'en est-il des flux sans fin?
return42
@ return42 Cela dépend beaucoup de l'application. Pour beaucoup, il est tout à fait correct de supposer qu'un fichier texte a une taille bien inférieure à la mémoire disponible. S'il vous arrive d'avoir des fichiers potentiellement volumineux, j'ai modifié ma réponse.
Martin Thoma
merci pour votre ajout, qui est le même que la réponse alok . Et désolé non, je ne pense pas que cela dépende de l'application. OMI, il est toujours préférable de ne pas lire plus de lignes que nécessaire.
return42
7

Certains d'entre eux sont charmants, mais cela peut être fait beaucoup plus simplement:

start = 0 # some starting index
end = 5000 # some ending index
filename = 'test.txt' # some file we want to use

with open(filename) as fh:
    data = fin.readlines()[start:end]

print(data)

Cela utilisera simplement le découpage de liste, il charge tout le fichier, mais la plupart des systèmes minimisent l'utilisation de la mémoire de manière appropriée, c'est plus rapide que la plupart des méthodes indiquées ci-dessus et fonctionne sur mes fichiers de données 10G +. Bonne chance!

Volonté
la source
4

Vous pouvez faire un appel à la fonction de recherche () qui positionne votre tête de lecture sur un octet spécifié dans le fichier. Cela ne vous sera utile que si vous savez exactement combien d'octets (caractères) sont écrits dans le fichier avant la ligne que vous souhaitez lire. Peut-être que votre fichier est strictement formaté (chaque ligne est un nombre X d'octets?) Ou, vous pouvez compter le nombre de caractères vous-même (n'oubliez pas d'inclure des caractères invisibles comme les sauts de ligne) si vous voulez vraiment augmenter la vitesse.

Sinon, vous devez lire chaque ligne avant la ligne souhaitée, conformément à l'une des nombreuses solutions déjà proposées ici.

romain
la source
3

Si votre gros fichier texte fileest strictement bien structuré (ce qui signifie que chaque ligne a la même longueur l), vous pouvez utiliser la nligne -th

with open(file) as f:
    f.seek(n*l)
    line = f.readline() 
    last_pos = f.tell()

Avis de non-responsabilité Cela ne fonctionne que pour les fichiers de même longueur!

Michael Dorner
la source
2

Que dis-tu de ça:

>>> with open('a', 'r') as fin: lines = fin.readlines()
>>> for i, line in enumerate(lines):
      if i > 30: break
      if i == 26: dox()
      if i == 30: doy()
Hamish Grubijan
la source
C'est vrai, c'est moins efficace que celui d'Alok, mais le mien utilise une déclaration with;)
Hamish Grubijan
2

Si cela ne vous dérange pas d'importer, fileinput fait exactement ce dont vous avez besoin (c'est-à-dire que vous pouvez lire le numéro de ligne de la ligne actuelle)

ennuikiller
la source
2
def getitems(iterable, items):
  items = list(items) # get a list from any iterable and make our own copy
                      # since we modify it
  if items:
    items.sort()
    for n, v in enumerate(iterable):
      if n == items[0]:
        yield v
        items.pop(0)
        if not items:
          break

print list(getitems(open("/usr/share/dict/words"), [25, 29]))
# ['Abelson\n', 'Abernathy\n']
# note that index 25 is the 26th item

la source
Roger, mon gars préféré! Cela pourrait bénéficier d'une déclaration with.
Hamish Grubijan
2

Je préfère cette approche car elle est plus polyvalente, c'est-à-dire que vous pouvez l'utiliser sur un fichier, sur le résultat de f.readlines(), sur un StringIOobjet, peu importe:

def read_specific_lines(file, lines_to_read):
   """file is any iterable; lines_to_read is an iterable containing int values"""
   lines = set(lines_to_read)
   last = max(lines)
   for n, line in enumerate(file):
      if n + 1 in lines:
          yield line
      if n + 1 > last:
          return

>>> with open(r'c:\temp\words.txt') as f:
        [s for s in read_specific_lines(f, [1, 2, 3, 1000])]
['A\n', 'a\n', 'aa\n', 'accordant\n']
Robert Rossney
la source
2

Voici mon petit 2 cents, pour ce que ça vaut;)

def indexLines(filename, lines=[2,4,6,8,10,12,3,5,7,1]):
    fp   = open(filename, "r")
    src  = fp.readlines()
    data = [(index, line) for index, line in enumerate(src) if index in lines]
    fp.close()
    return data


# Usage below
filename = "C:\\Your\\Path\\And\\Filename.txt"
for line in indexLines(filename): # using default list, specify your own list of lines otherwise
    print "Line: %s\nData: %s\n" % (line[0], line[1])
AWainb
la source
2

Un changement meilleur et mineur pour la réponse d'Alok Singhal

fp = open("file")
for i, line in enumerate(fp,1):
    if i == 26:
        # 26th line
    elif i == 30:
        # 30th line
    elif i > 30:
        break
fp.close()
sedic
la source
1

@OP, vous pouvez utiliser énumérer

for n,line in enumerate(open("file")):
    if n+1 in [26,30]: # or n in [25,29] 
       print line.rstrip()
ghostdog74
la source
1
file = '/path/to/file_to_be_read.txt'
with open(file) as f:
    print f.readlines()[26]
    print f.readlines()[30]

À l'aide de l'instruction with, cela ouvre le fichier, imprime les lignes 26 et 30, puis ferme le fichier. Facile!

user3901273
la source
ce n'est pas une réponse valable. après le premier appel à readlines()l'itérateur sera épuisé et le deuxième appel renverra une liste vide ou lancera une erreur (je ne me souviens plus de laquelle)
Paul H
1

Vous pouvez le faire très simplement avec cette syntaxe que quelqu'un a déjà mentionnée, mais c'est de loin la façon la plus simple de le faire:

inputFile = open("lineNumbers.txt", "r")
lines = inputFile.readlines()
print (lines[0])
print (lines[2])
Trey50Daniel
la source
1

Pour imprimer la ligne n ° 3,

line_number = 3

with open(filename,"r") as file:
current_line = 1
for line in file:
    if current_line == line_number:
        print(file.readline())
        break
    current_line += 1

Auteur original: Frank Hofmann

crazy_daffodils
la source
1

Assez rapide et au point.

Pour imprimer certaines lignes dans un fichier texte. Créez une liste "lines2print" puis imprimez simplement lorsque l'énumération est "dans" la liste lines2print. Pour se débarrasser des '\ n' supplémentaires, utilisez line.strip () ou line.strip ('\ n'). J'aime juste la "compréhension de liste" et j'essaye d'utiliser quand je peux. J'aime la méthode "avec" pour lire les fichiers texte afin d'éviter de laisser un fichier ouvert pour une raison quelconque.

lines2print = [26,30] # can be a big list and order doesn't matter.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in lines2print]

ou si la liste est petite, tapez simplement list comme liste dans la compréhension.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in [26,30]]
Mike Adrion
la source
0

Pour imprimer la ligne souhaitée. Pour imprimer la ligne au-dessus / en dessous de la ligne requise.

def dline(file,no,add_sub=0):
    tf=open(file)
    for sno,line in enumerate(tf):
        if sno==no-1+add_sub:
         print(line)
    tf.close()

exécuter ----> dline ("D: \ dummy.txt", 6) ie dline ("file path", line_number, si vous voulez que la ligne supérieure de la ligne recherchée donne 1 pour -1 inférieur c'est la valeur par défaut optionnelle sera être pris 0)

sudhir tataraju
la source
0

Si vous souhaitez lire des lignes spécifiques, telles que la ligne commençant après une ligne de seuil, vous pouvez utiliser les codes suivants, file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines

Niharranjan Pradhan
la source
-1
f = open(filename, 'r')
totalLines = len(f.readlines())
f.close()
f = open(filename, 'r')

lineno = 1
while lineno < totalLines:
    line = f.readline()

    if lineno == 26:
        doLine26Commmand(line)

    elif lineno == 30:
        doLine30Commmand(line)

    lineno += 1
f.close()
inspecteurG4dget
la source
7
c'est aussi impythonique que possible.
SilentGhost
Donne le mauvais résultat, car vous ne pouvez pas utiliser des lignes de lecture et des lignes de lecture comme ça (elles modifient chacune la position de lecture actuelle).
Je suis désolé d'avoir négligé une énorme erreur dans mon premier code. L'erreur a été corrigée et le code actuel devrait fonctionner comme prévu. Merci d'avoir signalé mon erreur, Roger Pate.
inspectorG4dget
-1

Je pense que cela fonctionnerait

 open_file1 = open("E:\\test.txt",'r')
 read_it1 = open_file1.read()
 myline1 = []
 for line1 in read_it1.splitlines():
 myline1.append(line1)
 print myline1[0]
San k
la source
Il y avait déjà une douzaine de méthodes de ligne de lecture lorsque vous avez posté cela - en ajouter une autre ajoute simplement de l'encombrement
duhaime