Grosse Textdateien schnell editieren
Hallo zusammen!
Ich muss grosse Textdateien (bis zu 50 MB) auslesen und editieren. Bislang habe ich dazu die 'Split'-Funktion verwendet. Allerdings dauert die Operation relativ lang.
Da ich eine Vielzahl von grossen Textdateien bearbeiten muss, würde ich gern wissen, ob es eine bessere (d.h. vor allen Dingen schnellere) Möglichkeit gibt.
Ich bin Euch sehr dankbar für Eure Hinweise!
Viele Grüße,
Siggi
Ich muss grosse Textdateien (bis zu 50 MB) auslesen und editieren. Bislang habe ich dazu die 'Split'-Funktion verwendet. Allerdings dauert die Operation relativ lang.
Da ich eine Vielzahl von grossen Textdateien bearbeiten muss, würde ich gern wissen, ob es eine bessere (d.h. vor allen Dingen schnellere) Möglichkeit gibt.
Ich bin Euch sehr dankbar für Eure Hinweise!
Viele Grüße,
Siggi
Moin,
Was machst du denn genau? Ums Einlesen und Schreiben der Datei kommst du nicht herum. Wenn das alleine lange dauert, dann kauf dir ne schnellere Platte. Aber vielleicht ist dein "Editieren" ja das Problem? Zeig mal etwas Code, dann kann man dir besser helfen.
Gruß,
Manuel
Die Dateien sind auch relativ groß .Siggi2008 hat geschrieben:Ich muss grosse Textdateien (bis zu 50 MB) auslesen und editieren. Bislang habe ich dazu die 'Split'-Funktion verwendet. Allerdings dauert die Operation relativ lang.
Was machst du denn genau? Ums Einlesen und Schreiben der Datei kommst du nicht herum. Wenn das alleine lange dauert, dann kauf dir ne schnellere Platte. Aber vielleicht ist dein "Editieren" ja das Problem? Zeig mal etwas Code, dann kann man dir besser helfen.
Gruß,
Manuel
Hallo helduel,
anbei ein Auszug aus dem Code, den ich bislang verwende:
+++++++++++++++++++++++++++++++++++++++++++++++
CODE:
+++++++++++++++++++++++++++++++++++++++++++++++
def write_beam_elems(filestring, elems, output_filename):
# Generate the correct filename from output_filename
output_path = os.path.dirname(output_filename) # Get the path name
output_fn = os.path.basename(output_filename) # Get the filename
predicate = re.compile('\.')
e = predicate.split(output_fn) # Split the filename by '.'
output_file_extension = e[-1] # Set the extension to the last string after the '.'
output_fn = output_fn[:len(e)+2]
# Split the *.out file
predicate = re.compile('STRESSES IN.*IN ELEMENT\s+\d+, POINT\s+\d+')
elem_list = predicate.split(filestring)
i = 1
j = 1
while i <= len(elem_list)-2:
try:
filename = "%s/%s%i.%s" % (output_path, output_fn, j, output_file_extension)
f = open(filename, "w")
beam_list = sort_fibres(elem_list, elem_list[i+1])
f.write(beam_list2str(beam_list))
f.close
tem_output_filename = "%s/%s%i.%s" % (output_path, output_fn, j, '.tem')
create_tem(tem_output_filename, elems[j-1], beam_list)
i += 2
j += 1
except IOError:
print "Error opening file %s. Does the path '%s/' exist?" % (filename, output_path)
break
+++++++++++++++++++++++++++++++++++++++++++++++
Vielen Dank im Voraus!
Siggi
anbei ein Auszug aus dem Code, den ich bislang verwende:
+++++++++++++++++++++++++++++++++++++++++++++++
CODE:
+++++++++++++++++++++++++++++++++++++++++++++++
def write_beam_elems(filestring, elems, output_filename):
# Generate the correct filename from output_filename
output_path = os.path.dirname(output_filename) # Get the path name
output_fn = os.path.basename(output_filename) # Get the filename
predicate = re.compile('\.')
e = predicate.split(output_fn) # Split the filename by '.'
output_file_extension = e[-1] # Set the extension to the last string after the '.'
output_fn = output_fn[:len(e)+2]
# Split the *.out file
predicate = re.compile('STRESSES IN.*IN ELEMENT\s+\d+, POINT\s+\d+')
elem_list = predicate.split(filestring)
i = 1
j = 1
while i <= len(elem_list)-2:
try:
filename = "%s/%s%i.%s" % (output_path, output_fn, j, output_file_extension)
f = open(filename, "w")
beam_list = sort_fibres(elem_list, elem_list[i+1])
f.write(beam_list2str(beam_list))
f.close
tem_output_filename = "%s/%s%i.%s" % (output_path, output_fn, j, '.tem')
create_tem(tem_output_filename, elems[j-1], beam_list)
i += 2
j += 1
except IOError:
print "Error opening file %s. Does the path '%s/' exist?" % (filename, output_path)
break
+++++++++++++++++++++++++++++++++++++++++++++++
Vielen Dank im Voraus!
Siggi
Wenn Dir ".split()"-Operationen zu langsam sind, ist Dir Code in Sprachen wie Python insgesamt zu langsam.
Du müßtest das dann in C auf Byte-Ebene angehen. Ist aber sehr mühsam.
Allein sowas:
sieht dann bei mir schon z.B. so aus:
Wie lang und schwierig der Rest dann wird, kannst Du Dir vielleicht denken. Nicht umsonst hat man diese Ebene in Befehlen wie z.B. "sed" gekapselt.
Gruß
Du müßtest das dann in C auf Byte-Ebene angehen. Ist aber sehr mühsam.
Allein sowas:
Code: Alles auswählen
fh = file("l.txt", "r")
a = fh.readlines()
fh.close()
Code: Alles auswählen
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *readFile(const char *filename);
int writeFile(const char *filename, char *a);
char *strMalloc(unsigned int s);
char *strRealloc(char *a, unsigned int s);
int main(void)
{
char *a = readFile("l.txt");
puts(a);
return 0;
}
char *readFile(const char *filename)
{
/* Open a text-file mostly regardless of its size,
store it in memory and return a char-pointer pointing
to the memory-adress. */
FILE *fp;
char *a;
char buff[1000];
int i;
int size;
size = 0;
buff[999] = '\0';
a = strMalloc(1000);
if ((fp = fopen(filename, "r")) == NULL)
{
puts("Error opening file; probably file not found.");
exit(1);
}
while (!feof(fp))
{
for (i = 0; i < 999; i++)
{
buff[i] = fgetc(fp);
size++;
if (buff[i] == EOF)
{
buff[i] = '\0';
break;
}
}
size++;
a = strRealloc(a, size);
strcat(a, buff);
}
fclose(fp);
return a;
}
int writeFile(const char *filename, char *a)
{
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
{
puts("Error writing file.");
return 1;
}
fprintf(fp, "%s", a);
fclose(fp);
return 0;
}
char *strMalloc(unsigned int s)
{
/* Allocates memory for a string, testing if allocation has
been successfull. */
char *a;
if ((a = (char *) malloc(s * sizeof(char))) == NULL)
{
puts("Error allocating memory.");
exit(1);
}
return a;
}
char *strRealloc(char *a, unsigned int s)
{
/* Reallocates memory of a string, testing if reallocation has
been successfull. */
if ((a = (char *) realloc(a, s * sizeof(char))) == NULL)
{
puts("Error reallocating memory.");
exit(1);
}
return a;
}
Gruß
Zuletzt geändert von abgdf am Mittwoch 3. Dezember 2008, 12:42, insgesamt 1-mal geändert.
Reguläre Ausdrücke sind meines Wissens nicht gerade die schnellsten. Du könntest versuchen, dir mit der Standard-`split()`-Methode von Strings zu helfen. Ist vielleicht etwas umständlicher und unschöner, dürfte aber schneller sein.
Weiterhin scheint mir das Argument `filestring` schon das Problem zu verdeutlichen. Zeig' doch auch den Code zum Einlesen. Dabei bietet sich zeilenweises Einlesen und die Verwendung eines Generators sehr an, um nicht *alle* Zeilen zeitgleich im Speicher halten zu müssen. Sofern die zu lesenden Datensätze auf Zeilen aufgeteilt sind, kannst du diese separat verarbeiten.
Python-Code kannst du übrigens in die Tags `[code=py]` und `[/code]` einschließen, damit er formatiert und eingefärbt dargestellt wird.
Weiterhin scheint mir das Argument `filestring` schon das Problem zu verdeutlichen. Zeig' doch auch den Code zum Einlesen. Dabei bietet sich zeilenweises Einlesen und die Verwendung eines Generators sehr an, um nicht *alle* Zeilen zeitgleich im Speicher halten zu müssen. Sofern die zu lesenden Datensätze auf Zeilen aufgeteilt sind, kannst du diese separat verarbeiten.
Python-Code kannst du übrigens in die Tags `[code=py]` und `[/code]` einschließen, damit er formatiert und eingefärbt dargestellt wird.
Code: Alles auswählen
predicate = re.compile('\.')
e = predicate.split(output_fn) # Split the filename by '.'
Code: Alles auswählen
e = output_fn.split(".")
Das:
Code: Alles auswählen
predicate = re.compile('STRESSES IN.*IN ELEMENT\s+\d+, POINT\s+\d+')
elem_list = predicate.split(filestring)
Gruß,
Manuel
Hallo!
Vielen Dank schon mal an alle für die schnelle Hilfe! @Y0Gi: Nachstehend habe ich noch mal den Code für das Einlesen der Dateien angegeben. Vielleicht kann man da ja tatsächlich noch die Geschwindigkeit erheblich steigern.
Viele Grüße,
Siggi
Vielen Dank schon mal an alle für die schnelle Hilfe! @Y0Gi: Nachstehend habe ich noch mal den Code für das Einlesen der Dateien angegeben. Vielleicht kann man da ja tatsächlich noch die Geschwindigkeit erheblich steigern.
Code: Alles auswählen
import sys
# Open, read and close a file. Return the string, containing the file.
def read_file(filename):
try:
fsock = open(filename)
try:
string = fsock.read()
finally:
fsock.close()
except IOError:
print "Error opening file %s. Does the file exist?" % filename
sys.exit(-1)
return string
# Print the status of a operation "x of y elements done (x.y%)"
def print_status(current_elem, length):
status = "%i of %i elements done (%.1f%%)" % (current_elem, length, (current_elem/float(length))*100.0)
sys.stdout.write(status+'\r')
sys.stdout.flush()
# Print a status message (msg). Attribute = 'r' means overwrite the last line (replace), 'n' means, use new line
def print_status_string(msg, attribute):
if attribute == 'r':
sys.stdout.write(' \r')
sys.stdout.flush()
sys.stdout.write(msg+'\r')
sys.stdout.flush()
elif attribute == 'n':
print msg
Siggi
Wasbewirken soll, verstehe ich nicht.
Es existiert os.path.splitext.
MfG
HWK
Code: Alles auswählen
output_fn = output_fn[:len(e)+2]
Es existiert os.path.splitext.
MfG
HWK
Der gezeigte reguläre Ausdruck mit dem "STRESSES IN" scheint auf einzelnen Zeilen zu arbeiten. Ich sehe daher keine Notwendigkeit, in "filestring" alle 50 MB zu speichern, sondern würde Zeile für Zeile vorgehen. Dateien wie ein Array zu verarbeiten und nicht als Strom von Daten, ist manchmal einfacher, aber selten eine gute Idee.
Stefan
Stefan
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
Wenn die Datei nicht existiert, kann sie in finally auch nicht geschlossen werdn, darum kein flaches try-except-finally.
Allerdings existiert with was doch sauberer ist als der innere try Block.
Gibt es für den großen Einsatz von sys.stdout eigtl einen besonderen Grund? Dass print existiert, scheinst du ja zu wissen.
Edit: Snippet ergänzt
Allerdings existiert with was doch sauberer ist als der innere try Block.
Code: Alles auswählen
try:
with open(filename) as fsock:
string = fsock.read()
except IOError:
print "Error opening file %s. Does the file exist?" % filename
sys.exit(-1)
Edit: Snippet ergänzt
Der Großteil der Dateioperation besteht darin, Daten aus einer vorhanden Textdatei auszulesen, neu zu ordnen und in eine neue Textdatei einzutragen. Ein Beispiel für den Auslesevorgang ist der unten dargestellte Block. Dieser muss zunächst in eine neue Form gebracht werden:
Neue Darstellung:
Alte Darstellung:
Vielleicht fällt Euch ja noch etwas sein, wie der Code optimiert werden kann. Ich bin im Internet darauf gestossen, dass die Datei in binärer Form wesentlich schneller verarbeitet werden kann.
Könnte das eine Option sein?
Vielen Dank für Eure Hilfe,
Siggi
Neue Darstellung:
etc.FIB. STRESS
1 -9.68
2 -6.61
3 130.34
Alte Darstellung:
Der Block ist nur ein Auszug. Tatsächlich gibt es bis zu 5.000 von diesen Wertepaaren pro Datei (und es sind viele Dateien!), wobei ich diese sehr oft auslesen und in einer anderen Datei eintragen muss.FIB. STRESS FIB. STRESS FIB. STRESS FIB. STRESS FIB. STRESS
1 -9.68 2 -6.61 3 130.34 4 127.28 5 130.34
6 127.28 7 -9.68 8 -6.61 9 -0.79 10 121.46
11 127.07 12 -6.40 13 124.84 14 126.20 15 -5.53
16 -4.17 17 126.20 18 124.84 19 -5.53 20 -4.17
Vielleicht fällt Euch ja noch etwas sein, wie der Code optimiert werden kann. Ich bin im Internet darauf gestossen, dass die Datei in binärer Form wesentlich schneller verarbeitet werden kann.
Könnte das eine Option sein?
Vielen Dank für Eure Hilfe,
Siggi
Also geschwindigkeitsrelevant ist vorallem folgende Funktion:
Hier ist das Problem, dass so eine Datei gut mal 1.4 mio Zeilen haben kann, relevant sind allerdings grob nur die ersten 2500. Hier muss in jeder Zeile ein Wert durch einen bestimmten anderen Wert (jede Zeile ein anderer Wert) ersetzt werden. Die Frage ist nun, gibt es eine Möglichkeit nur diese Zeilen direkt in der Datei zu ersetzen, ohne den Rest beachten zu müssen?
Code: Alles auswählen
def create_tem(tem_output_filename, filename, beam_list):
shutil.copy2(filename, tem_output_filename)
p = re.compile('0\.000000E\+00')
i = 0
try:
for line in fileinput.input(tem_output_filename, inplace=1):
if p.search(line[-14:]):
line = line[:-14] + '%E\n' % (beam_list[i][3] * 10**6)
i += 1
print line,
fileinput.close()
except IOError:
print "Error opening file %s. Does the file exist?" % filename
sys.exit(-1)
Also, ich hoffe und gehe mal davon aus, Du kommst so zurecht. Ansonsten wäre es auch möglich, das Hauptprogramm in Python zu schreiben und einzelne rechenintensive Funktionen in C:
http://superjared.com/entry/anatomy-python-c-module/
http://www.python.org/doc/2.5.2/ext/intro.html
Gruß
http://superjared.com/entry/anatomy-python-c-module/
http://www.python.org/doc/2.5.2/ext/intro.html
Gruß
Ja, in Deinem Fall schon, da immer 13 Zeichen durch 13 andere ersetzt werden. In etwa so:maxsz hat geschrieben:Die Frage ist nun, gibt es eine Möglichkeit nur diese Zeilen direkt in der Datei zu ersetzen, ohne den Rest beachten zu müssen?
Code: Alles auswählen
test = open("/tmp/foo", "r+")
SEARCH = '0.000000E+00'
line = test.readline()
while line:
if line[-13:].startswith(SEARCH):
test.seek(test.tell() - 13)
test.write('1.234567E+00\n')
line = test.readline()
test.close()
Manuel
Der Block hat aber nicht viel mit deinem Code zu tun. Du splitest nach "STRESSES IN.*IN ELEMENT\s+\d+, POINT\s+\d+", aber in deinem Beispiel kommt sowas gar nicht vor.Siggi2008 hat geschrieben:Der Block ist nur ein Auszug.
So wie ich das sehe, reicht es, wenn du schaust, ob die Zeile mit "FIB. STRESS" beginnt (line.startswith("FIB")), oder auch nur mit "FIB". Dann kannst du entsprechend reagieren.
Aber durch die Datei durchjagen und eine andere schreiben - da musst du durch . Interessant wäre jetzt, wie du die Zeilen parst und neu sortierst. Da ist, denke ich, mehr Spielraum für Optimierungen.
Also das hat schon einiges gebracht. Vielen Dank!
Also immer zwei Blöcke zwischen "STRESSES..." gehören zusammen. Aber vlt sagt der Code mehr als tausend Worte. Eventuell sind ja noch weitere Optimierungen möglich...
Also immer zwei Blöcke zwischen "STRESSES..." gehören zusammen. Aber vlt sagt der Code mehr als tausend Worte. Eventuell sind ja noch weitere Optimierungen möglich...
Code: Alles auswählen
def sort_fibres(string1, string2):
predicate = re.compile('-?\d+\.\d+')
elem_list1 = predicate.findall(string1)
elem_list2 = predicate.findall(string2)
# Create new list
beam_list = []
for i in range(0, len(elem_list1)):
beam_list.append([i+1,
elem_list1[i],
elem_list2[i],
calculate_mean(float(elem_list1[i]), float(elem_list2[i]))])
return beam_list
def write_beam_elems(filestring, elems, output_filename):
# Generate the correct filename from output_filename
output_fn = os.path.splitext(output_filename)[0]
output_file_extension = os.path.splitext(output_filename)[1]
# Split the *.out file
predicate = re.compile('STRESSES IN.*IN BEAM ELEMENT\s+\d+, POINT\s+\d+')
elem_list = predicate.split(filestring)
i = 1
j = 1
length = (len(elem_list)-1)/2
while i <= len(elem_list)-2:
try:
beam_list = sort_fibres(elem_list[i], elem_list[i+1])
filename = "%s%i%s" % (output_fn, j, output_file_extension)
f = open(filename, "w")
f.write(beam_list2str(beam_list))
f.close()
tem_output_filename = "%s%i.%s" % (output_fn, j, 'tem')
create_tem(tem_output_filename, elems[j-1], beam_list)
print_status(j, length)
i += 2
j += 1
except IOError:
print "Error opening file %s. Does the path '%s/' exist?" % (filename, output_fn)
break
Aus Zeile 7-16 kannst du eine Zeile machen (List Comprehension). Aus wird (ungetestet)
Code: Alles auswählen
# Create new list
beam_list = []
for i in range(0, len(elem_list1)):
beam_list.append([i+1,
elem_list1[i],
elem_list2[i],
calculate_mean(float(elem_list1[i]), float(elem_list2[i]))])
return beam_list
Code: Alles auswählen
beam_list = [[index+1,
elem_list1[index],
elem_list2[index],
calculate_mean(float(elem_list1[i]),
float(elem_list2[i]))] for index, value in enumerate(elem_list1)]
- with-statement einsetzen
- sinnvolle Variablen benutzen
FYI: Die gemeinsame Benutzung von `except` und `finally` ist IIRC erst ab einer bestimmten Python-Version (2.4? 2.5?) möglich.SchneiderWeisse hat geschrieben:Hat zwar nichts mit deinem Problem direkt zu tun, aber für was ist das verschachtelte try-Konstrukt?
Im vorliegenden Fall hätte ich es anders herum verschachtelt, *wenn* ich nicht sowieso `with` benutzen würde.