Czytanie tylko określonych linii

215

Używam pętli for do odczytu pliku, ale chcę tylko czytać określone linie, powiedzmy linie 26 i 30. Czy jest jakaś wbudowana funkcja, aby to osiągnąć?

Dzięki

eozzy
źródło
1
Możliwe dup: stackoverflow.com/questions/620367/…
Adam Matan

Odpowiedzi:

253

Jeśli plik do odczytu jest duży i nie chcesz jednocześnie czytać całego pliku w pamięci:

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()

Zauważ, że i == n-1dla nlinii th.


W Pythonie 2.6 lub nowszym:

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
źródło
8
enumerate(x)używa x.next, więc nie potrzebuje całego pliku w pamięci.
Alok Singhal
3
Moja mała wołowina polega na tym, że A) chcesz używać zamiast pary otwarta / zamknięta, a tym samym utrzymywać ciało krótkie, B) Ale ciało nie jest tak krótkie. Brzmi jak kompromis między prędkością / przestrzenią a byciem Pythonem. Nie jestem pewien, jakie byłoby najlepsze rozwiązanie.
Hamish Grubijan,
5
z jest przereklamowany, python radził sobie dobrze bez niego przez ponad 13 lat
Dan D.
38
@Dan D. Elektryczność jest przereklamowana, bez niej ludzkość dobrze sobie radzi przez ponad 200 tysięcy lat. ;-) „with” czyni go bezpieczniejszym, bardziej czytelnym i krótszym o jedną linię.
Romain Vincent
9
dlaczego używać pętli, nie sądzę, że rozumiesz znaczenie big file. Pętla zajmie lata, aby dotrzeć do indeksu
devssh
159

Szybka odpowiedź:

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

lub:

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

Istnieje bardziej eleganckie rozwiązanie do wyodrębniania wielu wierszy: linecache (dzięki uprzejmości „python: jak przejść do konkretnej linii w ogromnym pliku tekstowym?” , Poprzednie pytanie stackoverflow.com).

Cytując powyższą dokumentację w języku Python:

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

Zmień 4żądany numer linii i gotowe. Zauważ, że 4 przyniosłoby piątą linię, ponieważ liczba jest liczona od zera.

Jeśli plik może być bardzo duży i powodować problemy podczas wczytywania do pamięci, dobrym pomysłem może być skorzystanie z porady @ Alok i użycie funkcji enumerate () .

Podsumowując:

  • Użyj fileobject.readlines()lub for line in fileobjectjako szybkiego rozwiązania dla małych plików.
  • Użyj linecachedo bardziej eleganckiego rozwiązania, które będzie dość szybkie do odczytu wielu plików, możliwe wielokrotnie.
  • Skorzystaj z porad @ Aloka i użyjenumerate() plików, które mogą być bardzo duże i nie zmieściłyby się w pamięci. Pamiętaj, że użycie tej metody może spowolnić, ponieważ plik jest odczytywany sekwencyjnie.
Adam Matan
źródło
7
Miły. Właśnie spojrzałem na źródło linecachemodułu i wygląda na to, że czyta cały plik z pamięci. Tak więc, jeśli losowy dostęp jest ważniejszy niż optymalizacja rozmiaru, linecachejest najlepszą metodą.
Alok Singhal
7
z linecache.getlin ('some_file', 4) Dostaję czwartą linię, a nie piątą.
Juan
fajny fakt: jeśli użyjesz zestawu zamiast listy w drugim przykładzie, otrzymasz czas działania O (1). Wyszukiwanie na liście to O (n). Zestawy wewnętrzne są reprezentowane jako skróty i dlatego otrzymujesz czas działania O (1). w tym przykładzie nie jest to wielka sprawa, ale jeśli używasz dużej listy liczb i zależy Ci na wydajności, to zestawy są właściwą drogą.
rady
linecacheteraz wydaje się działać tylko dla plików źródłowych Pythona
Paul H
Możesz także użyć linecache.getlines('/etc/passwd')[0:4]do czytania pierwszego, drugiego, trzeciego i czwartego wiersza.
zyy
30

Szybkie i kompaktowe podejście mogłoby być:

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

akceptuje to dowolny otwarty obiekt podobny do pliku thefile(pozostawiając programowi wywołującemu, czy należy go otworzyć z pliku dyskowego, czy np. przez gniazdo lub inny strumień podobny do pliku) i zestaw wskaźników wiersza od zera whatlines, i zwraca lista, z małą powierzchnią pamięci i rozsądną prędkością. Jeśli liczba zwracanych wierszy jest ogromna, możesz preferować generator:

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

co w zasadzie jest dobre tylko do zapętlania - zwróć uwagę, że jedyna różnica wynika z użycia w returninstrukcji zaokrąglonych, a nie kwadratowych nawiasów , dzięki czemu możliwe jest zrozumienie listy i wyrażenie generatora.

Zwróć też uwagę, że pomimo wzmianki o „wierszach” i „pliku” funkcje te są znacznie, znacznie bardziej ogólne - będą działać na każdym iterowalnym, czy to otwartym pliku, czy innym, zwracając listę (lub generator) elementów na podstawie ich progresywnych numerów przedmiotów. Sugeruję więc stosowanie bardziej odpowiednio ogólnych nazw ;-).

Alex Martelli
źródło
@ephemient, nie zgadzam się - genexp czyta płynnie i idealnie.
Alex Martelli
Doskonałe i eleganckie rozwiązanie, dzięki! Rzeczywiście, nawet duże pliki powinny być obsługiwane z wyrażeniem generatora. Nie może być bardziej elegancki niż to, prawda? :)
Samuel Lampa
Ładne rozwiązanie, jak to się ma do proponowanego przez @AdamMatan? Rozwiązanie Adama może być szybsze, ponieważ wykorzystuje dodatkowe informacje (monotonicznie rosnące numery linii), które mogą prowadzić do wczesnego zatrzymania. Mam plik 10 GB, którego nie mogę załadować do pamięci.
Mannaggia
2
@Mannaggia Nie jest wystarczająco podkreślone w tej odpowiedzi, ale whatlinespowinno być set, ponieważ if i in whatlinesbędzie działać szybciej z zestawem niż (posortowaną) listą. Nie zauważyłem tego po raz pierwszy i zamiast tego opracowałem własne brzydkie rozwiązanie z posortowaną listą (gdzie nie musiałem skanować listy za każdym razem, podczas gdyif i in whatlines tylko to robi), ale różnica w wydajności była znikoma (z moimi danymi) i to rozwiązanie jest znacznie bardziej eleganckie.
Victor K
28

Aby zaoferować inne rozwiązanie:

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

Mam nadzieję, że to szybkie i łatwe :)

KingMak
źródło
1
Mam nadzieję, że jest to najbardziej optymalne rozwiązanie.
maniac_user
2
Odczytuje to cały plik do pamięci. Równie dobrze możesz wywołać file.read (). Split ('\ n'), a następnie użyć odnośników indeksu tablic, aby uzyskać linię zainteresowania ...
duhaime
Czy możesz podać przykład @duhaime
anon
14

jeśli chcesz wiersz 7

line = open („file.txt”, „r”). readlines () [7]
MadSc13ntist
źródło
14
Schludny. Ale jak ci się podoba close()plik, otwierając go w ten sposób?
Milo Wielondek
1
@ 0sh czy musimy zamknąć?
Ooker
1
tak. po tym musimy zamknąć. Kiedy otwieramy plik za pomocą „z” ... zamyka się.
reetesh11
10

Dla kompletności, oto jeszcze jedna opcja.

Zacznijmy od definicji z dokumentacji Pythona :

plasterek Obiekt zwykle zawierający część sekwencji. Wycinek jest tworzony za pomocą notacji indeksu dolnego [] z dwukropkami między liczbami, gdy podano kilka, na przykład w zmiennej_nazwa [1: 3: 5]. Notacja w nawiasach (indeks dolny) wykorzystuje obiekty wycinków wewnętrznie (lub w starszych wersjach __getslice __ () i __setslice __ ()).

Chociaż notacja wycinka nie ma bezpośredniego zastosowania do iteratorów, itertoolspakiet zawiera funkcję zastępowania:

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

Dodatkową zaletą tej funkcji jest to, że nie czyta iteratora do końca. Możesz więc robić bardziej złożone rzeczy:

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

Aby odpowiedzieć na pierwotne pytanie:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]
Newtover
źródło
1
Zdecydowanie najlepsze podejście do pracy z dużymi plikami. Mój program przeszedł z konsumpcji 8 GB + na prawie nic. Kompromisem było zużycie procesora, które wzrosło z ~ 15% do ~ 40%, ale faktyczne przetwarzanie pliku było o 70% szybsze. Będę to kompromis przez cały dzień. Dzięki Ci! 🎉🎉🎉
GollyJer
1
Wydaje mi się to najbardziej pytoniczne. Dzięki!
ipetrik
10

Odczytywanie plików jest niesamowicie szybkie. Odczyt pliku 100 MB zajmuje mniej niż 0,1 sekundy (zobacz mój artykuł Odczytywanie i zapisywanie plików w Pythonie ). Dlatego powinieneś przeczytać go całkowicie, a następnie pracować z pojedynczymi wierszami.

Większość odpowiedzi tutaj nie jest zła, ale zły styl. Zawsze należy otwierać plikiwith ponieważ zapewnia to, że plik zostanie ponownie zamknięty.

Więc powinieneś to zrobić w ten sposób:

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

Ogromne pliki

Jeśli zdarzy ci się mieć ogromny plik i zużycie pamięci stanowi problem, możesz go przetworzyć wiersz po wierszu:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i
Martin Thoma
źródło
IMO to naprawdę zły styl, aby przeczytać cały plik o nieznanej długości, tylko po to, aby uzyskać pierwsze 30 wierszy .. co jest z zużyciem pamięci .. a co z niekończącymi się strumieniami?
return42
@ return42 To zależy bardzo od aplikacji. Dla wielu jest całkowicie w porządku założyć, że plik tekstowy ma znacznie mniejszy rozmiar niż dostępna pamięć. Jeśli zdarzy ci się mieć potencjalnie ogromne pliki, zredagowałem moją odpowiedź.
Martin Thoma,
dzięki za dodanie, które jest takie samo jak odpowiedź alok . I przepraszam nie, nie sądzę, że to zależy od aplikacji. IMO zawsze lepiej nie czytaj więcej linii niż potrzebujesz.
return42
7

Niektóre z nich są piękne, ale można to zrobić o wiele prościej:

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)

Wykorzysta to proste wycinanie list, ładuje cały plik, ale większość systemów odpowiednio zminimalizuje zużycie pamięci, jest szybsza niż większość metod podanych powyżej i działa na moich plikach danych 10G +. Powodzenia!

Będzie
źródło
4

Możesz wykonać wywołanie seek (), które pozycjonuje twoją głowicę odczytu do określonego bajtu w pliku. To ci nie pomoże, chyba że dokładnie wiesz, ile bajtów (znaków) zapisano w pliku przed wierszem, który chcesz przeczytać. Być może twój plik jest ściśle sformatowany (każda linia ma liczbę X bajtów?) Lub możesz sam policzyć liczbę znaków (pamiętaj o dołączaniu niewidocznych znaków, takich jak podział linii), jeśli naprawdę chcesz przyspieszenia.

W przeciwnym razie musisz przeczytać każdą linię przed linią, której chcesz, zgodnie z jednym z wielu rozwiązań już tutaj zaproponowanych.

rzymski
źródło
3

Jeśli twój duży plik tekstowy filema ściśle dobrą strukturę (co oznacza, że ​​każda linia ma tę samą długość l), możesz użyć dla n-tego wiersza

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

Oświadczenie Działa to tylko w przypadku plików o tej samej długości!

Michael Dorner
źródło
2

Co powiesz na to:

>>> 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
źródło
To prawda, jest to mniej wydajne niż Alok, ale moje używa instrukcji with;)
Hamish Grubijan,
2

Jeśli nie przeszkadza Ci importowanie, fileinput wykonuje dokładnie to, czego potrzebujesz (możesz odczytać numer bieżącej linii)

ennuikiller
źródło
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

źródło
Roger, mój ulubiony facet! To może skorzystać z oświadczenia z.
Hamish Grubijan
2

Wolę to podejście, ponieważ jest bardziej ogólne, tzn. Możesz użyć go w pliku, w wyniku f.readlines(), na StringIOobiekcie, cokolwiek:

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
źródło
2

Oto moje małe 2 centy, za ile są warte;)

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
źródło
2

Lepsza i drobna zmiana w odpowiedzi Aloka Singhala

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()
uspokajający
źródło
1

@OP, możesz użyć wyliczenia

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

Za pomocą instrukcji with otwiera to plik, drukuje linie 26 i 30, a następnie zamyka plik. Prosty!

użytkownik3901273
źródło
to nie jest poprawna odpowiedź. po pierwszym wezwaniu do readlines()iteratora wyczerpie się, a drugie wywołanie zwróci pustą listę lub wyrzuci błąd (nie pamiętam, które)
Paul H
1

Możesz to zrobić bardzo prosto, korzystając ze składni, o której ktoś już wspomniał, ale jest to zdecydowanie najłatwiejszy sposób:

inputFile = open("lineNumbers.txt", "r")
lines = inputFile.readlines()
print (lines[0])
print (lines[2])
Trey50Daniel
źródło
1

Aby wydrukować linię nr 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

Oryginalny autor: Frank Hofmann

crazy_daffodils
źródło
1

Dość szybko i na temat.

Aby wydrukować określone wiersze w pliku tekstowym. Utwórz listę „lines2print”, a następnie po prostu wydrukuj, gdy wyliczenie jest „na” liście lines2print. Aby pozbyć się dodatkowych „\ n”, użyj line.strip () lub line.strip ('\ n'). Po prostu lubię „rozumienie listy” i staram się używać, kiedy mogę. Lubię metodę „with” do czytania plików tekstowych, aby zapobiec pozostawieniu pliku otwartego z jakiegokolwiek powodu.

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]

lub jeśli lista jest mała, po prostu wpisz listę jako listę do zrozumienia.

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

Aby wydrukować żądaną linię. Aby wydrukować linię powyżej / poniżej wymaganej linii.

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()

wykonaj ----> dline („D: \ dummy.txt”, 6) tj. dline („ścieżka pliku”, numer_wiersza, jeśli chcesz, aby górna linia szukanej linii dawała 1 dla dolnej -1, jest to opcjonalna wartość domyślna będzie być zajęty 0)

sudhir tataraju
źródło
0

Jeśli chcesz odczytać określone linie, takie jak linia rozpoczynająca się po pewnej linii progowej, możesz użyć następujących kodów, file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines

Niharranjan Pradhan
źródło
-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()
inspectorG4dget
źródło
7
jest to tak mało mityczne, jak to możliwe.
SilentGhost,
Daje zły wynik, ponieważ nie można używać linii readline i readline w ten sposób (każda z nich zmienia bieżącą pozycję odczytu).
Przepraszam, że przeoczyłem OGROMNY błąd w moim pierwszym kodzie. Błąd został poprawiony i bieżący kod powinien działać zgodnie z oczekiwaniami. Dzięki za zwrócenie uwagi na mój błąd, Roger Pate.
inspectorG4dget
-1

Myślę, że to zadziała

 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]
Zatonął
źródło
Kiedy już to opublikowałeś, było już kilkanaście metod readline - dodanie kolejnej dodaje bałaganu
duhaime