Python: Apa cara cepat untuk membaca dan membagi file?

Saya perlu membaca file dan membaginya menjadi beberapa baris, dan juga membagi baris tersebut menjadi dua berdasarkan karakter tab, serta menghilangkan semua tanda ucapan. Saat ini saya memiliki fungsi yang berfungsi. Namun, ini agak lambat:

temp = []
fp = open(fName, "r")
for line in fp:
    temp.append(line.replace("\"","").rstrip("\n").split("\t"))
print temp

Ini membagi file menjadi daftar daftar. Ini sebenarnya hanya bisa berupa satu daftar, karena akan sangat mudah untuk membaginya kembali menjadi pasangan-pasangan nanti selama pesanannya tetap dipertahankan.

Harus ada cara yang lebih cepat untuk melakukan hal ini. Adakah yang bisa menempatkan saya di jalur yang benar?

Terima kasih!

[sunting] File yang saya kerjakan sangat besar, tapi saya akan menambahkan sesuatu seperti itu. (Apakah ada cara untuk mengunggah file di stack overflow?)

"CARMILLA"  "35"
"JONATHAN R"    "AA2"
"M" "3"
"EMMA"  "350"
"OLD"   "AA"

harus kembali:

["CARMILLA", "35", "JONATHON R", "AA2", "M", "3", "EMMA", "350", "OLD", "AA"]

Meskipun kode saya mengembalikannya sebagai daftar daftar 2 string, itu juga baik-baik saja.

Maaf, saya mungkin seharusnya mencatat bahwa pernyataan print menggantikan pernyataan return - karena saya mengeluarkan ini dari suatu fungsi, saya mengubahnya menjadi print sehingga lebih masuk akal di sini.


person false_azure    schedule 21.05.2013    source sumber
comment
contoh file dan keluaran akan membantu kami membuat jawaban (untuk pengujian)   -  person HennyH    schedule 21.05.2013
comment
Yang pasti, saya akan menambahkan satu.   -  person false_azure    schedule 21.05.2013
comment
Jika yang Anda inginkan hanyalah hasil cetakan, Anda cukup mencetak di loop for alih-alih menambahkan ke daftar.   -  person Gurgeh    schedule 21.05.2013
comment
Apakah Anda mencari csv? Tapi saya tidak yakin dengan kinerjanya.   -  person neuront    schedule 21.05.2013
comment
Saya yakin Anda bisa melakukannya lebih cepat, tapi apa gunanya? Jika ini terlalu lambat bagi Anda, maka Anda terlalu sering mengeksekusi kode ini - coba simpan hasilnya dalam cache.   -  person maxy    schedule 21.05.2013
comment
Atas dasar apa Anda mendasarkan asumsi bahwa membaca dan memisahkan agak lambat? Bagaimana Anda mengukurnya?   -  person interjay    schedule 21.05.2013
comment
Saya mengukurnya menggunakan time.time(), mengurangi waktu mulai (sebelum pemanggilan fungsi) dari waktu berakhir (setelah panggilan)   -  person false_azure    schedule 21.05.2013
comment
Jika besar, lakukan di C++, standar C++11 membuatnya mudah dan bisa selesai dalam 30 menit, tentu saja jika kecepatan benar-benar penting, jika tidak, tetap gunakan python dan gunakan pemahaman daftar seperti yang dikatakan HennyH di bawah ini dan gunakan juga apa yang dikatakan Janne Karila, Anda pasti akan mendapatkan peningkatan kinerja.   -  person Paul    schedule 21.05.2013


Jawaban (7)


Menurut saya pemahaman daftar akan lebih cepat daripada memanggil .append untuk setiap baris

from itertools import chain
with open('file.txt') as f:
    lines = chain.from_iterable([l.replace(r'"','').rstrip('\n').split('\t',1) for l in f])

EDIT: sehingga menghasilkan daftar yang rata

>>> 
['CARMILLA', '35', 'JONATHAN R', 'AA2', 'M', '3', 'EMMA', '350', 'OLD', 'AA']

Versi yang tidak merata:

with open('file.txt') as f:
    lines = [l.replace(r'"','').rstrip('\n').split('\t',1) for l in f]

Dan beberapa waktu, ternyata OP yang tercepat?

import timeit
print("chain, list",timeit.timeit(r"""
with open('file.txt') as f:
    lines = chain.from_iterable([l.replace(r'"','').rstrip('\n').split('\t',1) for l in f])""",setup="from itertools import chain",number=1000))
print("flat       ",timeit.timeit(r"""
with open('file.txt') as f:
    lines = [l.replace(r'"','').rstrip('\n').split('\t',1) for l in f]""",setup="from itertools import chain",number=1000))
print("op's       ",timeit.timeit(r"""temp = []
fp = open('file.txt', "r")
for line in fp:
    temp.append(line.replace("\"","").rstrip("\n").split("\t"))
""",number=1000))
print("jamlyks    ",timeit.timeit(r"""
with open('file.txt', 'rb') as f:
    r = csv.reader(f, delimiter=' ', skipinitialspace=True)
    list(chain.from_iterable(r))""",setup="from itertools import chain; import csv",number=1000))
print("lennart    ",timeit.timeit(r"""
    list(csv.reader(open('file.txt'), delimiter='\t', quotechar='"'))""",setup="from itertools import chain; import csv",number=1000))

Hasil

C:\Users\Henry\Desktop>k.py
('chain, list', 0.04725674146159321)
('my flat    ', 0.04629905135295972)
("op's       ", 0.04391255644624917)
('jamlyks    ', 0.048360870934994915)
('lennart    ', 0.04569112379085424)
person HennyH    schedule 21.05.2013
comment
chain.from_iterable dan ekspresi generator menghemat beberapa tanda baca - lines = chain.from_iterable(l.replace('"', '')... for l in f). Selain itu, tidak perlu menggunakan string mentah - tidak ada bedanya dengan string yang tidak memiliki ``. - person lvc; 21.05.2013
comment
chain mengembalikan iterator. list() disekitarnya akan membuat daftar. - person Janne Karila; 21.05.2013

Dengan mengganti temp.append dengan temp.extend, Anda mendapatkan daftar lapisan tunggal, bukan daftar daftar.

person chenaren    schedule 21.05.2013
comment
Saya ingin memposting ini hanya sebagai komentar tetapi tidak memerlukan hak istimewa. - person chenaren; 21.05.2013
comment
aku akan memberimu hak istimewa ini - person Dmitry Zagorulkin; 21.05.2013
comment
Terima kasih, saya akan mencobanya. - person false_azure; 21.05.2013

Jika Anda tahu hanya ada satu \t di setiap baris, Anda dapat menggunakan split("\t",1) atau rsplit("\t",1) untuk menghindari pemindaian seluruh baris untuk mencari tab.

strip('"') setelah split merupakan alternatif yang memungkinkan untuk replace("\"","") sebelum split. Coba jika lebih cepat.

Namun apakah Anda sudah menghitung berapa lama waktu yang dibutuhkan untuk membaca file hanya menggunakan file.read()? Apakah waktu yang dihabiskan untuk pemisahan sangat signifikan dibandingkan dengan itu?

person Janne Karila    schedule 21.05.2013
comment
Terima kasih! Seharusnya sudah membaca dokumentasi; Aku bahkan tidak tahu kamu bisa melakukan ini. - person false_azure; 21.05.2013
comment
Itu poin yang bagus, terima kasih. Mungkin perpisahan itu bukan masalahku. - person false_azure; 21.05.2013

Anda harus terlebih dahulu mencari tahu apa hambatan Anda yang sebenarnya. Cukup baca file tanpa membuat daftar hasil. Cetak saja setiap baris ketika dipisah, tetapi tidak ke konsol (dengan lambat) tetapi ke dalam file baru. Saya berani bertaruh bahwa ini sudah JAUH lebih cepat. Jadi menurut saya (tidak dapat menguji tanpa hari nyata) masalah Anda bukan pada bagian membaca dan memisahkan. Itulah yang Anda lakukan setelahnya. Cobalah. Cara mengoptimalkan lebih lanjut bergantung pada kasus penggunaan Anda.

Memperbarui:

Berdasarkan contoh data Anda, Anda dapat mencoba yang ini:

import itertools
print list(itertools.chain(
    *( line.strip().split('\t') for line in file('sample.txt') )
))

Ini menghasilkan generator untuk data Anda. print list(...) hanya untuk dicetak dan konsisten dengan contoh Anda. Di aplikasi dunia nyata Anda mungkin tidak akan membuat daftarnya. Alih-alih, tulis data ke tempat yang seharusnya atau proses lebih jauh.

Pembaruan2:

Jika Anda ingin menghilangkan tanda kutip dan YAKIN setiap bagian memiliki tanda kutip, Anda cukup menggunakan x[1:-1]. Atau Anda bisa menggunakan x.strip('"'), jika ingin yakin. Tapi tidak perlu menggunakan regex.

person Achim    schedule 21.05.2013

Seperti ini misalnya:

>>> import csv
>>> reader = csv.reader(open('testfile'), delimiter='\t', quotechar='"')
>>> list(reader)
[['CARMILLA', '35'], ['JONATHAN R', 'AA2'], ['M', '3'], ['EMMA', '350'], ['OLD', 'AA']]
person Lennart Regebro    schedule 21.05.2013
comment
@HennyH: Mengutip OP: Meskipun kode saya mengembalikannya sebagai daftar daftar 2 string, itu juga bagus. Jadi tidak, tidak perlu diratakan. - person Lennart Regebro; 21.05.2013

Menggunakan regex dan pemahaman daftar:

import re
with open("abc") as f:
    lis = [x.group(1) for line in f for x in \
                             re.finditer(r'"([a-zA-Z0-9\s]+)"', line) ]
    print lis

keluaran:

['CARMILLA', '35', 'JONATHAN R', 'AA2', 'M', '3', 'EMMA', '350', 'OLD', 'AA']

Jika jumlah nilai yang dipisahkan tab tidak banyak, gunakan re.findall():

lis =  [y for line in f for y in re.findall(r'"([a-zA-Z0-9\s]+)"', line)]

atau menggunakan itertools.chain:

lis =  list(chain(*(re.findall(r'"([a-zA-Z0-9\s]+)"', line) for line in f)))
person Ashwini Chaudhary    schedule 21.05.2013
comment
Jika Anda akan menggunakan seluruh iterator, versi daftar re.findall akan lebih cepat - person jamylak; 21.05.2013
comment
@jamylak Ya, tapi itu akan membuat seluruh daftar di memori terlebih dahulu. - person Ashwini Chaudhary; 21.05.2013
comment
Ya tapi antreannya pendek, ini hanya akan menimbulkan banyak overhead - person jamylak; 21.05.2013

person    schedule
comment
splitlines() akan membuat seluruh daftar di memori terlebih dahulu, bukan hemat memori. - person Ashwini Chaudhary; 21.05.2013
comment
Anda harus membuat daftar lengkap di memori, yang membutuhkan waktu dan ... menghabiskan banyak memori. Mengapa harus lebih cepat? - person Achim; 21.05.2013
comment
saat ini dia menambahkannya ke daftar dan mencetak daftar itu. - person robert king; 21.05.2013
comment
Saya akan melakukan beberapa benchmark. Terakhir kali saya memeriksanya, itu yang tercepat - person robert king; 21.05.2013
comment
@robertking intinya bukanlah daftar yang dibuat oleh pemahaman daftar, tetapi daftar sementara (dan memang string sementara) yang dibuat oleh file_pointer.read().split_lines(). - person lvc; 21.05.2013
comment
Saya mencoba menggunakan read() yang akan membaca semuanya sekaligus yang lebih cepat karena menggunakan buffer yang lebih besar. Saya telah menemukan read().splitlines() menjadi lebih cepat di masa lalu. - person robert king; 21.05.2013