2009-04-07 12 views
1

Ich konvertiere Code aus einer anderen Sprache in Python. Dieser Code liest eine ziemlich große Datei in einen String und manipuliert sie dann von Array-Indizierung wie:String-Manipulation in Python

str[i] = 'e' 

Dies gilt nicht direkt in Python arbeitet aufgrund der Strings unveränderlich zu sein. Was ist der bevorzugte Weg, dies in Python zu tun?

Ich habe die string.replace() Funktion gesehen, aber es gibt eine Kopie der Zeichenfolge zurück, die nicht sehr optimal klingt, da die Zeichenfolge in diesem Fall eine ganze Datei ist.

+0

Wie groß ist die Zeichenkette/Datei? – SilentGhost

+0

ersetzen Sie immer die gleiche Spalte, oder Sie suchen und ersetzen? – vartec

+0

was ersetzt wird, hängt vom Inhalt der Datei ab – Zitrax

Antwort

9
l = list(str) 
l[i] = 'e' 
str = ''.join(l) 
+0

Sieht gut aus aber wird es mit einer riesigen Datei funktionieren? – theycallmemorty

+0

@theycallmemorty: es verbraucht doppelt so viel Speicher wie C, aber ansonsten kann ich keinen Grund sehen, warum es nicht funktionieren sollte. –

+0

In der Tat, wenn es viele solcher Manipulationen gibt, ist es wahrscheinlich am besten, die Strings als Listen von Zeichen zu behalten. –

12

Angenommen, Sie sind nicht mit variabler Länge Textcodierung wie UTF-8, verwenden Sie array.array verwenden können:

>>> import array 
>>> a = array.array('c', 'foo') 
>>> a[1] = 'e' 
>>> a 
array('c', 'feo') 
>>> a.tostring() 
'feo' 

Aber da Sie mit dem Inhalt einer Datei zu tun hat, mmap sollte effizienter sein:

>>> f = open('foo', 'r+') 
>>> import mmap 
>>> m = mmap.mmap(f.fileno(), 0) 
>>> m[:] 
'foo\n' 
>>> m[1] = 'e' 
>>> m[:] 
'feo\n' 
>>> exit() 
% cat foo 
feo 

Hier ist ein schneller Benchmarking-Skript (Sie werden mit etwas anderes für nicht-Unix-Betriebssysteme dd ersetzen müssen):

import os, time, array, mmap 

def modify(s): 
    for i in xrange(len(s)): 
     s[i] = 'q' 

def measure(func): 
    start = time.time() 
    func(open('foo', 'r+')) 
    print func.func_name, time.time() - start 

def do_split(f): 
    l = list(f.read()) 
    modify(l) 
    return ''.join(l) 

def do_array(f): 
    a = array.array('c', f.read()) 
    modify(a) 
    return a.tostring() 

def do_mmap(f): 
    m = mmap.mmap(f.fileno(), 0) 
    modify(m) 

os.system('dd if=/dev/random of=foo bs=1m count=5') 

measure(do_mmap) 
measure(do_array) 
measure(do_split) 

Output Ich habe auf meinem mehrjährigen Laptop paßt meine Intuition:

5+0 records in 
5+0 records out 
5242880 bytes transferred in 0.710966 secs (7374304 bytes/sec) 
do_mmap 1.00865888596 
do_array 1.09792494774 
do_split 1.20163106918 

So Mmap etwas schneller ist, aber keine der vorgeschlagenen Lösungen ist besonders unterschiedlich. Wenn Sie einen großen Unterschied sehen, versuchen Sie cProfile zu verwenden, um zu sehen, was die Zeit kostet.

+0

Ich erinnere mich, dass mmap Linux-only ist, so dass Portabilitätsprobleme auftreten können. –

+0

Nein, es funktioniert unter Unix und Windows (http://docs.python.org/library/mmap.html). Es gibt einige kleinere API-Unterschiede, aber nichts, was diesen Anwendungsfall betrifft. Eigentlich ein größerer Unterschied zu Windows: do_mmap 0.65700006485; do_array 1.0150001049; do_split 0.827999830246. –

+0

Danke für den Tipp über cProfile, es hat mich auf das Problem hingewiesen. Der for loops used range() hat viel Overhead verursacht. Ich wechselte zu While-Loops und nun ist die Performance gut. – Zitrax

0

Versuchen:

sl = list(s) 
sl[i] = 'e' 
s = ''.join(sl) 
1

Andere den String-Manipulation Teil Ihrer Frage beantwortet haben, aber ich denke, Sie sollten darüber nachdenken, ob es besser wäre, die Datei zu analysieren und die Datenstruktur der Text repräsentiert modifiziert eher als den Text direkt zu manipulieren.