Programm unglaublich langsam



  • Ich habe eine Datei in diesem Format:

    Zahl#Text#Zahl#Text#Zahl...
    

    Die Datei ist 500kb groß. Ich lade die Datei in einen AnsiString und habe eine Variable die bei jedem gefundenen # zwischen Zahl und Text umschaltet. Wenn dann die Zahl und der Text komplett ist, also wieder ein # kommt, schreibe ich die Daten in ein StringGrid.

    for(int i=1; i<IdDesc->Text.Length(); i++){
          if(IdDesc->Text[i]=='#'){
           State++;
           if(State==3){
            actid=AnsiReplaceStr(actid,"\n","");
            actid=AnsiReplaceStr(actid,"\r","");
            actval=AnsiReplaceStr(actval,"\n","");
            actval=AnsiReplaceStr(actval,"\r","");
            AddRow(actid);
            SetRow(actid,5,actval);
            State=1;
            actid="";
            actval="";
           }
          } else {
           if(State==1){
             actid+=IdDesc->Text[i];
           } else {
             actval+=IdDesc->Text[i];
           }
          }
         }
    
    void __fastcall TMainForm::SetRow(AnsiString id, int col, AnsiString val)
    {
     for(int i=1; i<ItemGrid->RowCount; i++){
      if(ItemGrid->Cells[0][i]==id){
       ItemGrid->Cells[col][i]=val;
      }
     }
    }
    //---------------------------------------------------------------------------
    void __fastcall TMainForm::AddRow(AnsiString id)
    {
     Caption="Lade Datensatz "+id;
     Application->ProcessMessages();
     if(id!=""){
      ItemGrid->RowCount++;
      ItemGrid->Cells[0][ItemGrid->RowCount-1]=id;
     }
    }
    

    aber der Code ist so schrecklich langsam, er läd ca ein Datensatz, also Zahl und Text in 5 Sekunden. Leider sind es über 2000 Stück.



  • Du hast ja auch mehrere Bremsen in deinem Code:

    1. Du verwendest 4x AnsiReplaceStr.
    Hast du überhaupt '\r' oder '\n' in deinem Text drin.
    Wenn nein, dann entferne die Aufrufe, wenn ja dann prüfe schon beim Einfügen auf diese Zeichen, z.B.

    char c = IdDesc->Text[i];
    if(!isctrl(c)) // aus stdlib.h
      actid += c;
    

    2. Warum durchläufst du alle Zeilen, um die Id zu finden, wenn du sie vorher mit AddRow an die letzte Zeile eingefügt hast?

    3. Application->ProcessMessages() startet bei jeder Zeile die MessageLoop und löst z.B. ein Neuzeichnen etc. aus...



  • In dem Text sind jede menge \n\r das ist hauptsächlich das Problem, denn die Daten stammen nicht von mir.

    Es müssen später noch mehr Werte in die Tabelle, die genau so angeordnet sind. Später sollen die Daten dann per AddRow hinzugefügt werden, eben an die passende stelle. Das mit isctrl werde ich ausprobieren.

    EDIT: isctrl kann ich nicht finden, auch wenn ich die Header dazu einbau. Ok, habs jetzt, heißt iscntrl. Die Hilfe findet auch nichts. ProcessMessage brauch ich, weil die GUI beil laden neu gezeichnet werden soll.

    EDIT2: Also, ich hab jetzt die Chars mit iscntrl aussortiert. Dann hab ich SetRow rausgenommen. ProcessMessages hab ich jetzt nur noch im Aufruf von AddRow. Allerdings dauerts immernoch ca 8 Sekunden bis ein Datensatz angezeigt wird... 😡



  • TStringList* dat = new TStringList();
       //Datei in StringList laden
       dat->LoadFromFile("dat.txt");
       //\n und \r im gesammten Text entfernen
       dat->Text=StringReplace(dat->Text,"\n","",TReplaceFlags() << rfReplaceAll);
       dat->Text=StringReplace(dat->Text,"\r","",TReplaceFlags() << rfReplaceAll);
       //Delimiter setzen (#)
       dat->Delimiter = '#';
       dat->DelimitedText = dat->Text;
       //Anzahl der Rows berechnen (Anzahl Einträge in Liste / 2 Spalten)
       StringGrid1->RowCount = dat->Count / 2;
       //Daten ins Grid schreiben
       for(int i=0;i<dat->Count-1;i+=2)
       {
          StringGrid1->Cells[0][i/2]=dat->Strings [i];
          StringGrid1->Cells[1][i/2]=dat->Strings [i+1];
       }
       delete dat;
    


  • Allerdings würde ich bei extrem großen Dateien dazu neigen, nicht alles ins Grid zu schreiben, sondern nur den sichtbaren Teil (meinetwegen 20-30 Zeilen).
    Dann baust du dir eine Funktion mit der du dann durch die Daten scrollst.



  • also, ich hab deinen Code jetzt gerade getestet, das Laden läuft jetzt schon 5 Minuten CPU Zeit und ist noch nicht beim Einfügen der Daten in die Tabelle.

    So, nach 5:30 Waren die Daten dann geladen und angezeigt, allerdings ist jedes Wort in einer eigenen Zeile, bzw. immer zwei Wörter nebeneinander.

    Es hilft ja auhc nix, denn das laden der Daten dauert so lange, es dauert keine 30 Sekunden die Daten ins StringGrid zu kopieren.

    http://s3.bilder-hosting.de/tbnl/LKDW4.jpg



  • Es hilft ja auhc nix, denn das laden der Daten dauert so lange

    Das laden einer 500 kb Datei dauert bei mir keine Sekunde.



  • So, nach 5:30 Waren die Daten dann geladen und angezeigt, allerdings ist jedes Wort in einer eigenen Zeile, bzw. immer zwei Wörter nebeneinander.

    Eine 600 kb Datei hat bei mir ca 4-5 Sekunden gebraucht, bis alles angezeigt wurde.



  • 😞 kann ich dir mal meine Datei zuschicken und du testest es damit?



  • Kannst du die Datei irgendwo hochladen ? Da ich nur ungern meine email Adressen irgendwo hinterlasse. (Hab schon genug Spam).





  • versuch mal folgendes

    #include <fstream>
    #include <string>
    
    using namespace std;
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    string iBuf;
    AnsiString iBuffer;
    ifstream inf("d:\\test.txt");
    getline(inf, iBuf);
    getline(inf, iBuf);
    int i=1, k;
    while ( !inf.eof() )
    	{
    	iBuf = "";
    	getline(inf, iBuf, '#');
    	iBuffer = iBuf.c_str();
    	iBuffer = StringReplace(iBuffer,"\n","",TReplaceFlags() << rfReplaceAll);
    	iBuffer = StringReplace(iBuffer,"\r","",TReplaceFlags() << rfReplaceAll);
    	if (TryStrToInt(iBuffer, k))
    		StringGrid1->Cells[0][i]=iBuffer;
    	else
    		{
    		StringGrid1->Cells[1][i]=iBuffer;
    		i++;
    		}
    	}
    StringGrid1->RowCount = i;
    inf.close();
    }
    

    noch ein Hinweis: bei StringList->Delimiter wird nicht nur der angegebene Delimiter sondern auch jedes Leerzeichen als Delimiter verwendet, daher kamen die Wörter alle einzeln

    [Edit] die Sonderbehandlung der Kommentare mußt du natürlich noch machen



  • bei deimem code zeigt er nur 501 an. also nur die erste zahl.



  • meinst du so ?

    ifstream in("dat.txt");
       string buff;
       String ansibuff;
       short pos=0;
       while(!in.eof())
       {
          getline(in,buff,'#');
          ansibuff=buff.c_str();
          ansibuff=StringReplace(ansibuff,"\r","",TReplaceFlags() << rfReplaceAll);
          ansibuff=StringReplace(ansibuff,"\n","",TReplaceFlags() << rfReplaceAll);
          StringGrid1->Cells [pos][StringGrid1->RowCount-1]=ansibuff;
          pos++;
          if(pos>1)
          {
             StringGrid1->RowCount++;
             pos=0;
          }
       }
       in.close();
    


  • Hallo,

    Sehe ich das richtig, das es um das entfernen der Zeilenumbrüche geht und das das so lange dauert... wenn ja, versuch mal das hier :

    //Datei in StringList laden
    dat->LoadFromFile("test.txt");
    //\n und \r im gesammten Text entfernen
    AnsiString NeuerText = "";
    for (int i = 0; i < dat->Count; i++)
      NeuerText += dat->Strings[i];
    

    Das geht um Längen schneller als StringReplace.

    Ich weiss ja nun auch nicht wie das Ergebnis (im StringGrid) aussehen soll, aber evtl. so ?

    TStringList* dat = new TStringList();
    AnsiString NeuerText  = "";
    AnsiString NeuerText2 = "";
    // Datei in StringList laden
    dat->LoadFromFile("test.txt");
    // \r\n im gesammten Text entfernen
    for (int i = 0; i < dat->Count; i++)
     NeuerText += dat->Strings[i];
    //Delimiter setzen (#)
    for (int i = 1; i <= NeuerText.Length(); i++)
    {
     if (NeuerText[i] == '#')
       NeuerText2 += "\r\n";
     else
       NeuerText2 += NeuerText[i];
    }  // for (int i = 1; i <= NeuerText.Length(); i++)
    dat->Text = NeuerText2;
    //Anzahl der Rows berechnen (Anzahl Einträge in Liste / 2 Spalten)
    StringGrid1->RowCount = dat->Count / 2;
    //Daten ins Grid schreiben
    for(int i = 0; i < dat->Count - 1; i += 2)
    {
      StringGrid1->Cells[0][i/2]=dat->Strings [i];
      StringGrid1->Cells[1][i/2]=dat->Strings [i+1];
    }  // for(int i = 0; i < dat->Count - 1; i += 2)
    // Das kann später raus, ist nur zum Testen...
    dat->SaveToFile(ExtractFilePath(Application->ExeName) + "neu.txt");
    delete dat;
    

    Das Grid wird dabei in weniger als einer Sekunde geladen...

    Nash



  • ifstream in("test.txt");
    string buff="";
    String ansibuff;
    unsigned i=0;
    short pos=1;
    while(!in.eof())
    {
       getline(in,buff,'#');
       ansibuff=buff.c_str();
       ansibuff=StringReplace(ansibuff,"\r","",TReplaceFlags() <<rfReplaceAll);
       StringGrid1->Cells [pos=!pos][i++/2]=StringReplace(ansibuff,"\n","",TReplaceFlags() <<rfReplaceAll);
    }
    StringGrid1->RowCount = i/2;
    in.close();
    

    Etwas optimiert gehts dann in unter ner halben Sekunde.



  • Ich habe mal Versuchsweise den Code von Nash probiert.
    Funktioniert beim ersten Durchlauf einwandfrei.
    Aber wenn man das mehrmals durchläuft (Hab es dazu mal ins OnClick eines Buttons gesetzt) dann wird es bei mir elendig langsam.
    Nur ich begreife nicht wieso. Hat da jemand ne Erklärung.



  • Ich komme jetzt gerade nicht an den Code (liegt aufem Arbeitsrechner) werde mir das Montag morgen nochmal anschauen.



  • Hallo,

    @ frage_zum_Code :
    Nimm den Code von "holg_er", der is besser...

    Nash



  • Nash schrieb:

    Hallo,

    @ frage_zum_Code :
    Nimm den Code von "holg_er", der is besser...

    Nash

    Aber ich würde gerne verstehen, warum der Code beim zweiten... Durchlauf immer langsamer wird.
    Die StringList wird doch danach komplett wieder mit delete gelöscht. 😕


Anmelden zum Antworten