Grosse Textdateien schnell editieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Siggi2008
User
Beiträge: 6
Registriert: Donnerstag 27. November 2008, 10:13

Grosse Textdateien schnell editieren

Beitragvon Siggi2008 » Mittwoch 3. Dezember 2008, 10:29

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
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Re: Grosse Textdateien schnell editieren

Beitragvon helduel » Mittwoch 3. Dezember 2008, 10:38

Moin,

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.


Die Dateien sind auch relativ groß ;-) .

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
Siggi2008
User
Beiträge: 6
Registriert: Donnerstag 27. November 2008, 10:13

Auszug aus dem Code

Beitragvon Siggi2008 » Mittwoch 3. Dezember 2008, 10:54

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[i], 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
abgdf

Beitragvon abgdf » Mittwoch 3. Dezember 2008, 12:31

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:

Code: Alles auswählen

fh = file("l.txt", "r")
a = fh.readlines()
fh.close()

sieht dann bei mir schon z.B. so aus:

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;
}

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ß
Zuletzt geändert von abgdf am Mittwoch 3. Dezember 2008, 12:42, insgesamt 1-mal geändert.
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Beitragvon Y0Gi » Mittwoch 3. Dezember 2008, 12:41

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.
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Beitragvon helduel » Mittwoch 3. Dezember 2008, 12:45

Code: Alles auswählen

predicate = re.compile('\.')
e = predicate.split(output_fn) # Split the filename by '.'

Das kannst du durch folgendes ersetzen:

Code: Alles auswählen

e = output_fn.split(".")

Aber das wird durch nicht viel Geschwindigkeit bringen.
Das:

Code: Alles auswählen

predicate = re.compile('STRESSES IN.*IN ELEMENT\s+\d+, POINT\s+\d+')
elem_list = predicate.split(filestring)

ist natürlich ein Brocken. Regexes sind allgemein recht langsam. Keine Ahnung, wie die Dateien sonst so aussehen, aber vielleicht kannst du das ganze ohne Regexes basteln?

Gruß,
Manuel
Siggi2008
User
Beiträge: 6
Registriert: Donnerstag 27. November 2008, 10:13

Ergänzung

Beitragvon Siggi2008 » Mittwoch 3. Dezember 2008, 13:09

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.

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


Viele Grüße,
Siggi
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Beitragvon helduel » Mittwoch 3. Dezember 2008, 13:55

Vielleicht könntest du noch einen Auszug aus einer Datei hier posten? Das Parsen könnte man vielleicht noch optimieren?
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Beitragvon HWK » Mittwoch 3. Dezember 2008, 14:19

Was

Code: Alles auswählen

output_fn = output_fn[:len(e)+2]
bewirken soll, verstehe ich nicht.
Es existiert os.path.splitext.
MfG
HWK
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Beitragvon nemomuk » Mittwoch 3. Dezember 2008, 14:25

Hat zwar nichts mit deinem Problem direkt zu tun, aber für was ist das verschachtelte try-Konstrukt?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Mittwoch 3. Dezember 2008, 14:27

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
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Beitragvon cofi » Mittwoch 3. Dezember 2008, 14:47

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.

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)


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
Siggi2008
User
Beiträge: 6
Registriert: Donnerstag 27. November 2008, 10:13

Beitragvon Siggi2008 » Mittwoch 3. Dezember 2008, 15:06

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:
FIB. STRESS
1 -9.68
2 -6.61
3 130.34

etc.

Alte Darstellung:
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


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.

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
maxsz
User
Beiträge: 2
Registriert: Mittwoch 3. Dezember 2008, 16:05

Beitragvon maxsz » Mittwoch 3. Dezember 2008, 16:09

Also geschwindigkeitsrelevant ist vorallem folgende Funktion:

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)



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?
abgdf

Beitragvon abgdf » Mittwoch 3. Dezember 2008, 17:49

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ß

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot], Google [Bot]