texfile in einen vector



  • ismaild schrieb:

    So in etwa hatte ich mir das gedacht. Da müsste doch was zu optimieren sein oder???

    Möglicherweise ist es schneller, die ganze Datei mit fread oder ifstream::read in den Speicher zu holen und dann zu parsen. Auf jeden Fall kann man den vector optimieren. Durch das push_back muss vecFile immer wieder vergrößert werden. Schneller wird es, wenn zu Anfang mit vector::reserve annähernd genug Speicher für alle Zeilen angefordert wird. Falls du weißt, wie viele Spalten die Datei hat, lässt sich der innere vector durch etwas wie boost::array<char *, ANZAHL_SPALTEN> ersetzen, um teure new[] s zu sparen. Wenn sich die Länge der einzelnen Einträge in Grenzen hält, könnte man _strdup rausnehmen und den char* durch array<char, MAX_LENGTH> ersetzen.
    Wenn man das alles berücksichtigt, benötigt das Programm nur ein einziges new[] am Anfang.

    Um Arbeitsspeicher zu sparen, könnte man die Datei blockweise lesen und die Ergebnisse sofort in die Ausgabedatei schreiben. Wahrscheinlich wird das sowieso nötig sein, weil Windows nicht unbedingt Speicheranforderungen von 1 GB oder mehr zulässt. Ich vermute auch, dass diese Variante sogar schneller wäre, weil vecFile wesentlich kleiner sein müsste, da es wiederverwendet werden könnte ( vector::clear gibt in der Regel noch nicht den Speicher frei).

    Ach ja, mit der Ausgabe könnte man es ähnlich machen und diese puffern, bevor sie binär in die Datei geschrieben wird.



  • Besten Dank für Deine wertvollen Ideen. Also dass mit dem fread habe ich net ganz verstanden. MEinst Du die komplette Datei auf einmal reinzuholen und dann parsen. Wie geht das denn... Leider weis ich net immer von vorne herein wieviele Zeilen oder spalten ich habe, aber die Zahl der Spalten pro Datei ist immer gleich...

    Leider muss ich die Datei erst im Speicher halten um gewisse operationen durchführen zu können....

    HAst Du mal ein Beispiel für den boost::array damit habe ich ja granix am Hut gehabt bis jetzt..



  • So habe mal die ganze sache mit boost gemacht hier der code:

    using namespace std;
    using namespace boost;
    
    int main(int argc, char* argv[])
    {
        const char* fname = "5mio.txt";
        FILE *fp = fopen( fname, "rb" );
    
    std::ofstream datei("cout.txt");
    
    vector<boost::array<char*,10> > vecFile;
    boost::array <char*,10> vecCol;
    
    int i(0);
            char buff[BUFSIZ];
            char* test=0;
            char token[70];token[0]='\0';
             int x = setvbuf(fp, (char *)NULL, _IOLBF, BUFSIZ*10);
             while ( fgets( buff, sizeof buff, fp ) != NULL ) {
    
                    char * ptrCR=strchr(buff,'\r');
                    *ptrCR = '\0';
                    int zu(0);
                    while(buff[zu] != '\0')
                    {
                        char aktZeichen[2];aktZeichen[1]='\0';
                        aktZeichen[0] = buff[zu];
                        if(buff[zu] != ';')
                        {
                            strcat(token,aktZeichen);
                        }
                        else{
                            vecCol.assign(_strdup(token));
                            token[0]='\0';
                        }
                        zu++;
                    }
                    vecFile.push_back(vecCol);
                    vecCol.empty();
             }
            _getch();// ram bei 830mb
    
            vector<boost::array <char*,10 > >::iterator curr(vecFile.begin());
            vector<boost::array <char*,10 > >::iterator end(vecFile.end());
            for( ; curr < end; ++curr)
            {
            datei << (*curr)[9]<< "\n";
            }
    
        return 0;
    }
    

    Kann man das array nicht dynamisch allozieren muss man des vorher bestimmen im zweiten Argument...



  • Du machst das denke ich viel zu kompliziert.. Zeig nochmal genau, was du für ein File hast und wie das genau haben willst.

    Hier einfach mal boost zu benutzen macht das ganze nicht besser.



  • Also habe ein simples textfile:

    feld1(varchar);feld2(varchar);feld3(varchar);......feldn(varchar)

    und das ganze mal 5mio zeilen oder 3mio also immer ziemlich gross..
    und ich experimentiere gerade wie ich das ganze sehr schnell und speichersparend in ein 2d raster(zeile/spalte) bekomme.. das ist alles

    P.S.: besten dank fürs einklinken....



  • Und wie genau willst du das in mehrere Dimensionen unterteilen?
    Und welche Daten von dem File willst du auch speichern?

    Mach mal ein kleines Beispiel mit 2-3 Einträgen, wie ein Inputfile aussieht und wie du das dann gespeichert haben willst.



  • Also:

    feld1;feld2;feld3
    hans;mercedes;new york
    evelyn;trabant;moskau

    Und jetzt will ich das ganze in ein quasi zwei dimensionales array packen

    ich will jede zeile und spalte mit einem index ansprechen können...

    klar genug



  • ismaild schrieb:

    feld1(varchar);feld2(varchar);feld3(varchar);......feldn(varchar)

    Keinerlei Infos über Redundanz, Inhalt, Größe?

    ismaild schrieb:

    sehr schnell und speichersparend

    Speichersparend bei beliebigem Text wird bei 1char pro Zeichen sein ... wenn du keine wilden Sonderzeichen drin hast. Du könntest versuchen längere Textpassagen mit Pack-Algos zu verkleinern (bz2?) was aber Rechenzeit zu en/dekodieren verbraucht also dem "sehr schnell" entgegenspricht.

    Wirklich platzsparend wird das nur gehen wenn in den 5 mio Zeilen * n spalten
    der selbe Text immer wieder kommt ... dann könntest du eine Qmap < QString, int> nehmen (jedem Text eine ID zuweisen) und dann die Ints speichern ... wäre kürzer sofern der meiste Text >> 4 characters hat ...

    Bei der Dateigröße ist mE allerdings ein sequentielles Auslesen besser ...

    5 mio Zeilen, 4 chars pro spalte, 10 values pro Zeile
    = 5.000.000 * 4 * 10
    = 200.000.000 chars (ohne seperatoren) / 1024 / 1024
    = 190 MB Daten



  • So hab mal bischen modifiziert.

    int main(int argc, char* argv[])
    {
        const char *delims = ";";
        const char* fname = "3mio.txt";
        FILE *fp = fopen( fname, "rb" );
    
        fseek(fp, 0, SEEK_END);
        long pos = ftell(fp);
        fseek(fp, 0, SEEK_SET);
    
        char *bytes = (char *)malloc(pos);
        fread(bytes, pos, 1, fp);
    
        fseek(fp, 0, SEEK_SET);
        int lines(0),cols(0);
        int i(0);
        for (; i<=pos; i++)
        {
            if(bytes[i] == *delims && lines < 1)
                cols++;
            if(bytes[i] == '\n')
                lines++;
        }
        free(bytes);
        cols++;
    
        typedef boost::multi_array<char*, 2> array_type;
        typedef array_type::index index;
        array_type meineDaten(boost::extents[lines][cols]);
        std::ofstream datei("cout.txt");
    
        char buff[BUFSIZ]; 
        int x = setvbuf(fp, (char *)NULL, _IOLBF, BUFSIZ*10);
        int imyline(0),imycol(0);
        size_t start = 0;char token[50];
    
        while ( fgets( buff, sizeof buff, fp ) != NULL )
        {
            while (buff[start] != '\0')
            {
                size_t end = strcspn(buff + start, delims);
                strncpy(token, buff + start, end);token[end]= '\0';
                meineDaten[imyline][imycol]=_strdup(token);
                start += (buff[start + end] != '\0') ? end + 1 : end;
                imycol++;
            }
            start = 0;
            imyline++;
            imycol=0;
        }
        //_getch();
        for(index i = 0; i != lines; ++i)
            datei << meineDaten[i][9] << "\n";
        return 0;
    }
    

    Und was meint Ihr...??

    Hat einer einen Vorschlag wie ich in der inneren while-schleife das stringcopy direkt in mein array bekomme sodass ich _strdup() mir sparen könnte..

    Für jede verbesserug bin ich offen.
    Achja habs diesmal mit dem boost gemacht...
    der Speicher bewegt sich bei ca. 800mb aber ist ziemlich schnell..

    P.S.: wusstet Ihr das es schneller ist ein <<"\n"; anzuhängen als <<endl; zu benutzen.....nur mal so in den Raum geschmissen



  • liegt denke ich daran, dass endl nicht nur \n ist sondern auch flush, also den Schreibbuffer in die Datei zwangsentleert statt dann, wenn das OS dazu hat.



  • Ih versuche gerade mal einen natives array zu benutzen, aber irgendwie ist es net so intuitiv wir der von boost.

    Also habe ein

    char* array_2D[lines][cols];
    

    wie muss ich jetzt dem array mein token übergeben....??



  • So habe es jetzt fertig...

    int main(int argc, char* argv[])
    {
        unsigned int startTime = 0;
        unsigned int endTime = 0;
    
        const char *delims = ";";
        const char* fname = "5mio.txt";
        FILE *fp = fopen( fname, "rb" );
    
        fseek(fp, 0, SEEK_END);
        long pos = ftell(fp);
        fseek(fp, 0, SEEK_SET);
    
        char *bytes = (char *)malloc(pos);
        fread(bytes, pos, 1, fp);
    
        fseek(fp, 0, SEEK_SET);
        int lines(0),cols(0);
        int i(0);
        for (; i<=pos; i++)
        {
            if(lines < 1 && bytes[i] == *delims)
                cols++;
            if(bytes[i] == '\n')
                lines++;
        }
        free(bytes);
        cols++;
    
        // Create the native array
        char*** myArray=allocate_2d_array_of_chars(lines,cols);
        std::ofstream datei("cout.txt");
    
        char buff[BUFSIZ];
        int x = setvbuf(fp, (char *)NULL, _IOLBF, BUFSIZ*10);
        int imyline(0),imycol(0);
        while ( fgets( buff, sizeof buff, fp ) != NULL )
        {
            while (buff[start] != '\0')
            {
                size_t end = strcspn(buff + start, delims);
                char token[end];
                char szString[end];
                szString[end]='\0';
                strncpy(token, buff + start, end);
                token[end]='\0';
                if(imycol== (cols-1))
                {
                    int i2 = 0;
                    for (int i = 0; i < end; i++)
                    {
                        char c = token[i];
                        if (c != '\r' && c != '\n')
                        {
                            szString[i2++] = c;
                            szString[i2]='\0';
                        }
                    }
                    myArray[imyline][imycol]=_strdup(szString);
    
                }
                else
                {
                    myArray[imyline][imycol]=_strdup(token);
    
                }
                start += (buff[start + end] != '\0') ? end + 1 : end;
                imycol++;
            }
            start = 0;
            imyline++;
            imycol=0;
        }
    
        for(int i = 0; i < lines; ++i)
        {
            for(int j = 0; j < cols; ++j)
            {
                (j==0)?(datei << myArray[i][j]):(datei << delims << myArray[i][j]);
            }
            datei << "\n";
        }
    
        return 0;
    }
    
    char*** allocate_2d_array_of_chars(size_t rows, size_t columns)
    {
        int i;
        char ***db_array;
    
        db_array = (char ***)malloc ( rows * sizeof *db_array);
    
        if ( db_array == NULL )
        {
            puts ("Speicherreservierung nicht möglich");
            return NULL;
        }
    
        for ( i = 0; i<rows; i++)
        {
            db_array[i] = (char **)malloc ( columns * sizeof *db_array[i]);
            if ( db_array[i] == NULL )
                printf ("Speicherreservierung bei db_array[%d] nicht möglich\n", i);
        }
    
        return db_array;
    }
    

    Hab da mal ne Frage...Wenn ich die Datei einlese habe ich am Ende einer jeden Zeile ein \r\n.. soweit ja kein Problem, aber wenn ich selber versuche diese beim schreiben in die Datei wieder einzusetzen bekomme ich noch eine Leerzeile zwischen den Zeilen....

    Und noch was.. die Datei die ich einlese hat ca. 300mb, wenn die Datei im Array ist habe ich einen Seicherverbrauch bei ca. 900mb, also man müsste meinen die Datei wird redundant gepeichert oder so...



  • Habe nochmal versucht mit memory mapped files die Sache etwas zu beschleunigen.

    hier der Code:

    namespace io = boost::iostreams;
    int main(int argc, char* argv[])
    {
        unsigned int startTime = 0;
        unsigned int endTime = 0;
    
        const char *delims = ";";
        const char* fname = "5mio.txt";
        FILE *fp = fopen( fname, "rb" );
    
        fseek(fp, 0, SEEK_END);
        long pos = ftell(fp);
        fseek(fp, 0, SEEK_SET);
    
        char *bytes = (char *)malloc(pos);
        fread(bytes, pos, 1, fp);
    
        fseek(fp, 0, SEEK_SET);
        int lines(0),cols(0);
        int i(0);
        for (; i<=pos; i++)
        {
            if(lines < 1 && bytes[i] == *delims)
                cols++;
            if(bytes[i] == '\n')
                lines++;
        }
        free(bytes);
        cols++;
    
        io::mapped_file_source myMap("5mio.txt");
        io::stream<io::mapped_file_source> input(myMap);
        // Create the native array
        char*** myArray=allocate_2d_array_of_chars(lines,cols);
        std::ofstream datei("cout.txt");
        std::string line;
    
        char buff[BUFSIZ];
    
        int imyline(0),imycol(0);
    
        while(std::getline(input, line))
        {
            strcpy(buff,line.c_str());
            while (buff[start] != '\0')
            {
                size_t end = strcspn(buff + start, delims);
                char token[end];
                char szString[end];
                szString[end]='\0';
                strncpy(token, buff + start, end);
                token[end]='\0';
                if(imycol== (cols-1))
                {
                    int i2 = 0;
                    for (int i = 0; i < end; i++)
                    {
                        char c = token[i];
                        if (c != '\r' && c != '\n')
                        {
                            szString[i2++] = c;
                            szString[i2]='\0';
                        }
                    }
                    myArray[imyline][imycol]=_strdup(szString);
                }
                else
                {
                    myArray[imyline][imycol]=_strdup(token);
                }
                start += (buff[start + end] != '\0') ? end + 1 : end;
                imycol++;
            }
            start = 0;
            imyline++;
            imycol=0;
        }
    
        for(int i = 0; i < lines; ++i)
        {
            for(int j = 0; j < cols; ++j)
            {
                (j==0)?(datei << myArray[i][j]):(datei << delims << myArray[i][j]);
            }
            datei << "\n";
        }
    
        return 0;
    }
    
    char*** allocate_2d_array_of_chars(size_t rows, size_t columns)
    {
        int i;
        char ***db_array;
    
        db_array = (char ***)malloc ( rows * sizeof *db_array);
    
        if ( db_array == NULL )
        {
            puts ("Speicherreservierung nicht möglich");
            return NULL;
        }
    
        for ( i = 0; i<rows; i++)
        {
            db_array[i] = (char **)malloc ( columns * sizeof *db_array[i]);
            if ( db_array[i] == NULL )
                printf ("Speicherreservierung bei db_array[%d] nicht möglich\n", i);
        }
    
        return db_array;
    }
    

    Die Sache ist das Dingen mit Mapping ist langsamer, ca. 3sek.
    komisch oder....

    P.S.: Bin für jede Verbesserung offen und dankbar...


Anmelden zum Antworten