Hanya membaca baris tertentu

215

Saya menggunakan loop for untuk membaca file, tetapi saya hanya ingin membaca baris tertentu, katakanlah baris # 26 dan # 30. Apakah ada fitur bawaan untuk mencapai ini?

Terima kasih

eozzy
sumber
1
Kemungkinan dup: stackoverflow.com/questions/620367/…
Adam Matan

Jawaban:

253

Jika file yang dibaca besar, dan Anda tidak ingin membaca seluruh file dalam memori sekaligus:

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

Perhatikan bahwa i == n-1untuk nbaris ke - th.


Dalam Python 2.6 atau yang lebih baru:

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
sumber
8
enumerate(x)menggunakan x.next, jadi tidak perlu seluruh file dalam memori.
Alok Singhal
3
Daging sapi kecil saya dengan ini adalah bahwa A) Anda ingin menggunakan dengan bukan pasangan buka / tutup dan dengan demikian menjaga tubuh tetap pendek, B) Tetapi tubuh tidak terlalu pendek. Kedengarannya seperti pertukaran antara kecepatan / ruang dan menjadi Pythonic. Saya tidak yakin solusi apa yang terbaik.
Hamish Grubijan
5
dengan dibesar-besarkan, python bergaul dengan baik selama lebih dari 13 tahun tanpanya
Dan D.
38
@Dan D. Listrik berlebihan, umat manusia hidup baik-baik saja selama lebih dari 200 ribu tahun tanpanya. ;-) 'with' membuatnya lebih aman, lebih mudah dibaca, dan satu baris lebih pendek.
Romain Vincent
9
mengapa digunakan untuk loop, saya tidak berpikir Anda mengerti artinya big file.
Pengulangan
159

Jawaban cepat:

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

atau:

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

Ada solusi yang lebih elegan untuk mengekstraksi banyak baris: linecache (milik "python: cara melompat ke baris tertentu dalam file teks besar?" , Pertanyaan stackoverflow.com sebelumnya).

Mengutip dokumentasi python yang ditautkan di atas:

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

Ubah 4ke nomor baris yang Anda inginkan, dan Anda aktif. Perhatikan bahwa 4 akan membawa baris kelima karena penghitungannya berbasis nol.

Jika file tersebut mungkin sangat besar, dan menyebabkan masalah saat dibaca ke dalam memori, mungkin ide yang baik untuk mengambil saran @ Alok dan menggunakan enumerate () .

Untuk menyimpulkan:

  • Gunakan fileobject.readlines()atau for line in fileobjectsebagai solusi cepat untuk file kecil.
  • Gunakan linecacheuntuk solusi yang lebih elegan, yang akan cukup cepat untuk membaca banyak file, mungkin berulang kali.
  • Ambil saran @ Alok dan gunakanenumerate() untuk file yang bisa sangat besar, dan tidak akan masuk ke dalam memori. Perhatikan bahwa menggunakan metode ini mungkin lambat karena file dibaca berurutan.
Adam Matan
sumber
7
Bagus. Saya baru saja melihat sumber linecachemodul, dan sepertinya membaca seluruh file di memori. Jadi, jika akses acak lebih penting daripada optimasi ukuran, linecacheadalah metode terbaik.
Alok Singhal
7
dengan linecache.getlin ('some_file', 4) Saya mendapatkan baris ke-4, bukan ke-5.
Juan
fakta menyenangkan: jika Anda menggunakan satu set dan bukan daftar dalam contoh kedua, Anda mendapatkan O (1) waktu berjalan. Mencari dalam daftar adalah O (n). Set internal direpresentasikan sebagai hash, dan itulah sebabnya Anda mendapatkan O (1) waktu berjalan. bukan masalah besar dalam contoh ini, tetapi Jika menggunakan daftar angka yang besar, dan peduli efisiensi, set adalah cara yang harus dilakukan.
rady
linecachesekarang tampaknya hanya berfungsi untuk file sumber python
Paul H
Anda juga dapat menggunakan linecache.getlines('/etc/passwd')[0:4]untuk membaca di baris pertama, kedua, ketiga dan keempat.
zyy
30

Pendekatan yang cepat dan ringkas dapat:

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

ini menerima objek seperti file terbuka thefile(menyerahkan kepada pemanggil apakah itu harus dibuka dari file disk, atau melalui misalnya soket, atau aliran file-suka lainnya) dan satu set indeks garis berbasis nol whatlines, dan mengembalikan sebuah daftar, dengan jejak memori rendah dan kecepatan wajar. Jika jumlah baris yang dikembalikan besar, Anda mungkin lebih suka generator:

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

yang pada dasarnya hanya baik untuk perulangan - perhatikan bahwa satu - satunya perbedaan berasal dari menggunakan kurung bulat daripada tanda kurung di return pernyataan, membuat daftar pemahaman dan ekspresi generator masing-masing.

Catatan lebih lanjut bahwa meskipun disebutkan "baris" dan "file" fungsi-fungsi ini banyak, banyak lebih umum - mereka akan bekerja pada setiap iterable, baik itu file terbuka atau lainnya, kembali daftar (atau generator) item berdasarkan nomor-item progresif mereka. Jadi, saya sarankan menggunakan nama-nama umum yang lebih tepat ;-).

Alex Martelli
sumber
@ephemient, saya tidak setuju - genexp membaca dengan lancar dan sempurna.
Alex Martelli
Solusi yang bagus dan elegan, terima kasih! Memang, bahkan file besar harus didukung, dengan ekspresi generator. Tidak bisa lebih elegan dari ini, bukan? :)
Samuel Lampa
Solusi yang bagus, bagaimana ini dibandingkan dengan yang diusulkan oleh @AdamMatan? Solusi Adam bisa lebih cepat karena mengeksploitasi informasi tambahan (nomor baris meningkat secara monoton) yang dapat menyebabkan pemberhentian awal. Saya memiliki file 10GB yang tidak dapat saya muat ke dalam memori.
Mannaggia
2
@Mannaggia Ini tidak cukup ditekankan dalam jawaban ini, tetapi whatlinesharus a set, karena if i in whatlinesakan mengeksekusi lebih cepat dengan set daripada daftar (diurutkan). Saya tidak memperhatikannya terlebih dahulu dan malah menemukan solusi jelek saya sendiri dengan daftar terurut (di mana saya tidak perlu memindai daftar setiap kali, sementara if i in whatlinesmelakukan hal itu), tetapi perbedaan kinerja dapat diabaikan (dengan data saya) dan ini solusi jauh lebih elegan.
Victor K
28

Demi menawarkan solusi lain:

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

Saya harap ini cepat dan mudah :)

KingMak
sumber
1
Semoga ini solusi paling optimal.
maniac_user
2
Ini membaca seluruh file ke dalam memori. Anda mungkin juga memanggil file.read (). Split ('\ n') kemudian gunakan pencarian indeks array untuk mendapatkan garis yang menarik ...
duhaime
Bisakah Anda memberikan contoh @duhaime
anon
14

jika Anda ingin saluran 7

line = open ("file.txt", "r"). readlines () [7]
MadSc13ntist
sumber
14
Rapi. Tapi bagaimana Anda close()file saat membukanya dengan cara ini?
Milo Wielondek
1
@ 0sh, apakah kita perlu menutup?
Ooker
1
Iya. kita perlu menutup setelah ini. Ketika kita membuka file menggunakan "dengan" ... itu menutup sendiri.
reetesh11
10

Demi kelengkapan, berikut adalah satu opsi lagi.

Mari kita mulai dengan definisi dari python docs :

slice Suatu objek yang biasanya berisi sebagian dari urutan. Sepotong dibuat menggunakan notasi subskrip, [] dengan titik dua di antara angka ketika beberapa diberikan, seperti dalam variabel_name [1: 3: 5]. Notasi braket (subskrip) menggunakan objek slice secara internal (atau dalam versi yang lebih lama, __ widgetlice __ () dan __setslice __ ()).

Meskipun notasi irisan tidak langsung berlaku untuk iterator secara umum, itertoolspaket berisi fungsi penggantian:

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

Keuntungan tambahan dari fungsi ini adalah ia tidak membaca iterator sampai akhir. Jadi Anda dapat melakukan hal-hal yang lebih kompleks:

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

Dan untuk menjawab pertanyaan awal:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]
baru ditemukan
sumber
1
Sejauh ini pendekatan terbaik saat bekerja dengan file besar. Program saya berubah dari mengkonsumsi 8GB + menjadi hampir tidak ada. Tradeoffnya adalah penggunaan CPU yang berubah dari ~ 15% menjadi ~ 40% tetapi pemrosesan aktual file adalah 70% lebih cepat. Saya akan mengambil tradeoff sepanjang hari. Terimakasih! 🎉🎉🎉
GollyJer
1
Ini tampaknya yang paling pythonic bagi saya. Terima kasih!
ipetrik
10

Membaca file sangat cepat. Membaca file 100MB membutuhkan waktu kurang dari 0,1 detik (lihat artikel saya Membaca dan Menulis File dengan Python ). Karenanya Anda harus membacanya sepenuhnya dan kemudian bekerja dengan satu baris.

Apa yang paling banyak dilakukan di sini bukanlah salah, tetapi gaya yang buruk. Membuka file harus selalu dilakukanwith karena memastikan bahwa file ditutup kembali.

Jadi Anda harus melakukannya seperti ini:

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

File besar

Jika Anda memiliki file yang besar dan konsumsi memori menjadi masalah, Anda dapat memprosesnya baris demi baris:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i
Martin Thoma
sumber
IMO itu adalah gaya yang sangat buruk untuk membaca seluruh file dengan panjang yang tidak diketahui, hanya untuk mendapatkan 30 baris pertama .. bagaimana dengan konsumsi memori .. dan bagaimana dengan aliran tanpa akhir?
kembali42
@ return42 Ini sangat tergantung pada aplikasi. Bagi banyak orang, sama sekali tidak masalah untuk menganggap bahwa file teks memiliki ukuran yang jauh lebih rendah daripada memori yang tersedia. Jika Anda berpotensi memiliki file besar, saya telah mengedit jawaban saya.
Martin Thoma
terima kasih untuk tambahan Anda, yang sama dengan jawaban alok . Dan maaf tidak, saya rasa ini tidak tergantung pada aplikasinya. IMO selalu lebih baik tidak membaca lebih banyak baris dari yang Anda butuhkan.
kembali42
7

Beberapa di antaranya indah, tetapi bisa dilakukan lebih sederhana:

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)

Itu akan menggunakan daftar slicing, memuat seluruh file, tetapi sebagian besar sistem akan meminimalkan penggunaan memori dengan tepat, lebih cepat dari sebagian besar metode yang diberikan di atas, dan bekerja pada file data 10G + saya. Semoga berhasil!

Akan
sumber
4

Anda dapat melakukan pencarian () yang menempatkan kepala baca Anda ke byte yang ditentukan dalam file. Ini tidak akan membantu Anda kecuali jika Anda tahu persis berapa banyak byte (karakter) yang ditulis dalam file sebelum baris yang ingin Anda baca. Mungkin file Anda benar-benar diformat (setiap baris adalah X jumlah byte?) Atau, Anda dapat menghitung sendiri jumlah karakter (ingat untuk memasukkan karakter yang tidak terlihat seperti jeda baris) jika Anda benar-benar menginginkan peningkatan kecepatan.

Jika tidak, Anda harus membaca setiap baris sebelum baris yang Anda inginkan, sesuai salah satu dari banyak solusi yang sudah diusulkan di sini.

Roma
sumber
3

Jika file teks besar Anda fileterstruktur dengan baik (artinya setiap baris memiliki panjang yang sama l), Anda dapat menggunakan untuk n-th line

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

Penafian Ini hanya berfungsi untuk file dengan panjang yang sama!

Michael Dorner
sumber
2

Bagaimana dengan ini:

>>> 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
sumber
Benar, ini kurang efisien daripada Alok, tapi punyaku menggunakan pernyataan with;)
Hamish Grubijan
2

Jika Anda tidak keberatan mengimpor maka fileinput melakukan apa yang Anda butuhkan (ini adalah Anda dapat membaca nomor baris dari baris saat ini)

ennuikiller
sumber
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

sumber
Roger, cowok favoritku! Ini bisa mendapat manfaat dari pernyataan dengan.
Hamish Grubijan
2

Saya lebih suka pendekatan ini karena lebih umum, yaitu Anda dapat menggunakannya pada file, pada hasil f.readlines(), pada StringIOobjek, apa pun:

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
sumber
2

Inilah 2 sen kecil saya, untuk apa nilainya;)

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
sumber
2

Perubahan yang lebih baik dan kecil untuk jawaban 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()
obat penenang
sumber
1

@OP, Anda bisa menggunakan enumerate

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

Menggunakan pernyataan with, ini membuka file, mencetak baris 26 dan 30, lalu menutup file. Sederhana!

pengguna3901273
sumber
ini bukan jawaban yang valid. setelah panggilan pertama ke readlines()iterator akan habis dan panggilan kedua akan mengembalikan daftar kosong atau membuat kesalahan (tidak ingat yang mana)
Paul H
1

Anda dapat melakukan ini dengan sangat sederhana dengan sintaksis yang telah disebutkan oleh seseorang, tetapi ini adalah cara termudah untuk melakukannya:

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

Untuk mencetak baris # 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

Penulis asli: Frank Hofmann

crazy_daffodils
sumber
1

Cukup cepat dan to the point.

Untuk mencetak baris tertentu dalam file teks. Buat daftar "lines2print" dan kemudian cetak ketika enumerasi "di" daftar lines2print. Untuk menghilangkan '\ n' tambahan gunakan line.strip () atau line.strip ('\ n'). Saya hanya suka "daftar pemahaman" dan coba gunakan ketika saya bisa. Saya suka metode "with" untuk membaca file teks untuk mencegah membiarkan file terbuka karena alasan apa pun.

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]

atau jika daftar kecil ketik saja daftar sebagai daftar ke dalam pemahaman.

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

Untuk mencetak garis yang diinginkan. Untuk mencetak garis di atas / di bawah garis yang diperlukan.

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

jalankan ----> dline ("D: \ dummy.txt", 6) yaitu dline ("path file", line_number, jika Anda ingin baris atas dari garis yang dicari berikan 1 untuk lebih rendah -1 ini adalah nilai default opsional akan diambil 0)

sudhir tataraju
sumber
0

Jika Anda ingin membaca baris tertentu, seperti baris yang dimulai setelah beberapa baris ambang maka Anda dapat menggunakan kode berikut, file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines

Niharranjan Pradhan
sumber
-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
sumber
7
ini sama unpythonic karena mendapat.
SilentGhost
Memberikan hasil yang salah, karena Anda tidak dapat menggunakan readlines dan readline seperti itu (masing-masing mengubah posisi baca saat ini).
Maaf karena mengabaikan kesalahan BESAR dalam kode pertama saya. Kesalahan telah diperbaiki dan kode saat ini harus berfungsi seperti yang diharapkan. Terima kasih telah menunjukkan kesalahan saya, Roger Pate.
inspectorG4dget
-1

Saya pikir ini akan berhasil

 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
sumber
Sudah ada selusin metode readline ketika Anda memposting ini - menambahkan yang lain hanya menambah kekacauan
duhaime