2011-11-23 36 views
7

Trong Python, tôi có luồng tệp và tôi muốn sao chép một phần của tệp đó thành một StringIO. Tôi muốn điều này nhanh nhất có thể, với bản sao tối thiểu.chuyển dữ liệu nhanh từ tệp sang một số StringIO

Nhưng nếu tôi làm:

data = file.read(SIZE) 
stream = StringIO(data) 

Tôi nghĩ 2 bản đã được thực hiện, không có? Một bản sao vào dữ liệu từ tập tin, một bản sao khác bên trong StringIO vào bộ đệm trong. Tôi có thể tránh một trong các bản sao không? Tôi không cần tạm thời data, vì vậy tôi nghĩ rằng một bản sao phải đủ

+0

Bạn sẽ làm gì với 'luồng'? Đọc nó?? –

+0

Bạn đang sử dụng Python 2.x hoặc 3.x? –

+0

@JohnMachin: Tôi cũng muốn đọc và sửa đổi nó. Câu hỏi này nói chung về Python, nếu có sự khác biệt giữa 2.x và 3.x, hãy nói – zaharpopov

Trả lời

8

Tóm lại: bạn không thể tránh 2 bản sao bằng cách sử dụng StringIO.

Một số giả thiết:

  • Bạn đang sử dụng cStringIO, nếu không nó sẽ là ngớ ngẩn để tối ưu hóa này nhiều.
  • Tốc độ và hiệu quả bộ nhớ không phải là sau. Nếu không, hãy xem giải pháp của Jakob Bowyer hoặc sử dụng biến thể bằng cách sử dụng file.read(SOME_BYTE_COUNT) nếu tệp của bạn là nhị phân.
  • Bạn đã nêu điều này trong các nhận xét, nhưng để hoàn chỉnh: bạn thực sự muốn chỉnh sửa nội dung, không chỉ xem nội dung đó.

dài câu trả lời: Kể từ chuỗi python là không thay đổi và bộ đệm StringIO không phải là, một bản sao sẽ phải được thực hiện sớm hay muộn; nếu không bạn sẽ thay đổi một vật thể bất biến! Đối với những gì bạn muốn có thể, đối tượng StringIO sẽ cần phải có một phương thức chuyên dụng đọc trực tiếp từ một đối tượng tệp được đưa ra như một đối số. Không có phương pháp như vậy.

Bên ngoài của StringIO, có các giải pháp tránh sao chép bổ sung. Off đỉnh đầu của tôi, điều này sẽ đọc một tập tin trực tiếp vào một mảng byte sửa đổi, không sao chép thêm:

import numpy as np 
a = np.fromfile("filename.ext", dtype="uint8") 

Nó có thể là cồng kềnh để làm việc với, tùy thuộc vào việc sử dụng bạn có ý định, vì nó là một mảng của giá trị từ 0 đến 255, không phải là một mảng ký tự. Nhưng nó có chức năng tương đương với một đối tượng StringIO, và sử dụng np.fromstring, np.tostring, np.tofile và ký hiệu cắt sẽ đưa bạn đến nơi bạn muốn. Bạn cũng có thể cần np.insert, np.deletenp.append.

Tôi chắc rằng có các mô-đun khác sẽ thực hiện những việc tương tự.

timeit:

là bao nhiêu tất cả điều này thực sự vấn đề? Được rồi để xem. Tôi đã tạo một tệp 100MB, largefile.bin. Sau đó, tôi đọc trong tập tin bằng cách sử dụng cả hai phương pháp và thay đổi byte đầu tiên.

 
$ python -m timeit -s "import numpy as np" "a = np.fromfile('largefile.bin', 'uint8'); a[0] = 1" 
10 loops, best of 3: 132 msec per loop 
$ python -m timeit -s "from cStringIO import StringIO" "a = StringIO(); a.write(open('largefile.bin').read()); a.seek(0); a.write('1')" 
10 loops, best of 3: 203 msec per loop 

Vì vậy, trong trường hợp của tôi, việc sử dụng StringIO chậm hơn 50% so với sử dụng gọn gàng.

Cuối cùng, để so sánh, chỉnh sửa các tập tin trực tiếp:

 
$ python -m timeit "a = open('largefile.bin', 'r+b'); a.seek(0); a.write('1')" 
10000 loops, best of 3: 29.5 usec per loop 

Vì vậy, đó là gần 4500 lần nhanh hơn. Tất nhiên, nó phụ thuộc rất nhiều vào những gì bạn sẽ làm với tập tin. Thay đổi byte đầu tiên hầu như không đại diện. Nhưng bằng cách sử dụng phương pháp này, bạn có một khởi đầu trên hai khác, và vì hầu hết các hệ điều hành có đệm tốt của đĩa, tốc độ có thể rất tốt quá.

(Nếu bạn không được phép chỉnh sửa tệp và vì vậy muốn tránh chi phí tạo bản sao làm việc, có một vài cách để tăng tốc độ. Nếu bạn có thể chọn hệ thống tệp, Btrfscopy-on-write hoạt động sao chép tệp - thực hiện hành động lấy bản sao của tệp hầu như tức thì. Có thể đạt được hiệu ứng tương tự bằng ảnh chụp LVM của bất kỳ hệ thống tệp nào.)

+0

là có nghĩa là không có gumpy, tức là trong stdlib? có lẽ bytearray cho hiệu ứng tương tự? – zaharpopov

+0

Không phải là tôi biết, không. Bytearray dường như không chấp nhận các đối tượng tập tin như một đối số. –

+0

nghe có vẻ như một sự xấu hổ, do đó, cách duy nhất để đọc bộ đệm có thể sửa đổi được từ tập tin nhanh chóng là: ( – zaharpopov

-1
stream = StringIO() 
for line in file: 
    stream.write(line + "\n") 
+1

cách này nhanh hơn? thêm bản sao cũng được thực hiện – zaharpopov

+0

Tệp có thể là nhị phân mặc dù ... – mac

6

Không, không có thêm bản sao nào được thực hiện. Bộ đệm được sử dụng để lưu trữ dữ liệu giống nhau. Cả hai data và thuộc tính bên trong có thể truy cập được bằng cách sử dụng StringIO.getvalue() là các tên khác nhau cho cùng một dữ liệu.

Python 2.7 (r27:82500, Jul 30 2010, 07:39:35) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import StringIO 
>>> data = open("/dev/zero").read(1024) 
>>> hex(id(data)) 
'0xea516f0' 
>>> stream = StringIO.StringIO(data) 
>>> hex(id(stream.getvalue())) 
'0xea516f0' 

Một béo nhanh chóng thông qua the source cho thấy cStringIO không tạo một bản sao xây dựng một trong hai, nhưng nó làm cho một bản sao trên gọi cStringIO.getvalue(), vì vậy tôi không thể lặp lại các cuộc biểu tình trên.

+0

Vì nội dung của 'dữ liệu' là không thay đổi và nội dung của' dòng' không phải là, bản sao bổ sung nhất định sẽ được thực hiện ngay sau khi đối tượng StringIO được sửa đổi , nếu không phải trước đây. Câu hỏi vẫn còn. –

+0

Đó là một câu hỏi khác. Nếu bạn muốn biết StringIO hoạt động như thế nào, điều tốt nhất cần làm là đọc 'StringIO.py'. –

+0

@MichaelHoffman: cảm ơn bạn, nhưng tôi đặc biệt quan tâm đến bản sao đó được thực hiện khi sửa đổi được thực hiện. Tôi biết rằng StringIO làm điều đó, câu hỏi của tôi là làm thế nào để tránh id. Làm thế nào để đọc dữ liệu trực tiếp từ tập tin để sửa đổi StringIO? – zaharpopov

2

Có lẽ những gì bạn đang tìm kiếm là một buffer/memoryview:

>>> data = file.read(SIZE) 
>>> buf = buffer(data, 0, len(data)) 

Bằng cách này bạn có thể truy cập vào một lát dữ liệu gốc mà không cần sao chép nó. Tuy nhiên, bạn phải quan tâm đến việc truy cập dữ liệu đó chỉ theo định dạng byte định hướng vì đó là những gì mà giao thức đệm cung cấp.

Bạn có thể tìm thêm thông tin trong số này có liên quan question.

Chỉnh sửa: Trong blog post này tôi tìm thấy qua reddit, một số thông tin được đưa ra liên quan đến cùng một vấn đề:

>>> f = open.(filename, 'rb') 
>>> data = bytearray(os.path.getsize(filename)) 
>>> f.readinto(data) 

Theo tác giả không sao chép thêm được tạo ra và dữ liệu có thể được sửa đổi kể từ bytearray là có thể thay đổi.

+0

tôi có thể sửa đổi nó như thế này không? – zaharpopov

+0

Nó phụ thuộc vào đối tượng được truy cập. Trong tài liệu [memoryview] (http://docs.python.org/library/stdtypes.html#memoryview) có một ví dụ thay đổi một giá trị trong đối tượng 'bytearray' (không thay đổi kích thước của nó). Tuy nhiên, trong ví dụ của bạn, 'file.read' sẽ trả về một chuỗi không thể sửa được, vì vậy bạn sẽ không thể thực hiện điều đó trên đối tượng đó. – jcollado

+0

Tôi vừa thấy [this] (http: //eli.thegreenplace.net/2011/11/28/less-copy-in-python-với-the-buffer-protocol-và-memoryviews /) trong reddit và có vẻ như giải quyết vấn đề để lấy dữ liệu vào một 'bytearray' bằng cách sử dụng' file .readinto'. – jcollado

Các vấn đề liên quan