Wie Anzahl ermitteln?



  • Wie kann man die Anzahl eines in einer Datei mehrfach vorkommenden gleichlautendem Eintrags ermitteln?
    Ich hätte da an sowas wie replace und dann die Differenz zum Original Inhalt berechnen gedacht oder gibts da so eine Funktion, die das komplett machen kann?
    Danke im voraus!



  • hä????



  • Dachte es wäre klar, also nochmal zum mitschreiben.

    Hch hab eine Datei mit dem (sinnlosem)Text:

    xxxx
    jhasjdh
    sjaf
    kasjdfkjw
    xxxx
    kjdf
    lwjkrfhlkwe
    xxxx
    lejkrek
    xxxx
    

    ich will einfach nur wissen wie oft da der String xxxx vorkommt.



  • Hi,

    auf Dateien selbst hat man gar keinen direkten Zugriff (mit Standard-C++). Man kann lediglich streams erzeugen, die mit ihnen verknüpft sind und dann auch diese Streams zugreifen.

    Ergo:

    • ifstream anlegen
    • zeichen-/string-/zeilenweise einlesen (je nachdem, was Du suchst: Identische Zeichen oder strings oder Zeilen),
    • vergleichen und
    • zählen.

    (Für das Zählen braucht man gar keinen verändernden Zugriff)
    Die Implementierung sei dem geneigen Programmierer als einfache Fingerübung überlassen.

    Gruß,

    Simon2.



  • dixidix schrieb:

    ich will einfach nur wissen wie oft da der String xxxx vorkommt.

    Ich würde die gesamte Datei in einen Puffer einlesen und dann mit der Funktion memcmp() mir die Anzahl der Vorkommen holen in einer Schleife holen.

    PS: memcmp() hat C++ von C geerbt.

    😉



  • emmax schrieb:

    dixidix schrieb:

    ich will einfach nur wissen wie oft da der String xxxx vorkommt.

    Ich würde die gesamte Datei in einen Puffer einlesen und dann mit der Funktion memcmp() mir die Anzahl der Vorkommen holen in einer Schleife holen.

    PS: memcmp() hat C++ von C geerbt.

    😉

    Ich würde eine Lösung für sein Problem vorschlagen. Wenn er schon weiß, dass er mit strings arbeiten will ... warum sollte er es dann nicht tun ? 😉

    Gruß,

    Simon2.



  • hmm ...

    std::size_t count_occurrences_of(const std::string& string, const std::string& of)
    {
        std::size_t count = 0;
        for (std::size_t pos = string.find(of); pos != std::string::npos; ++count);
        return count;
    }
    
    bool file_count_occurrences_of(const std::string& filename, const std::string& of, std::size_t& count)
    {
        std::ifstream file_stream(filename.c_str(), std::ios::in);
        if (!file_stream)
            return false;
    
        std::string content;
        std::string line;
        while (std::getline(file_stream, line))
            content += line;
    
        count = count_occurrences_of(content, of);
        return true;
    }
    

    🙂

    benutzen:

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::cout << "Dateiname: " << std::flush;
        std::string filename;
        std::getline(std::cin, filename);
        std::cout << "Suchwort: " << std::flush;
        std::string search;
        std::getline(std::cin, search);
        std::size_t count = 0;
        if (file_count_occurrences_of(filename, search, count) == false)
        {
            std::cerr << "Datei konnte nicht geöffnet werden!" << std::endl;
            return 1;
        }
        std::cout << "\"" << search << "\" wurde " << count << "x in der Datei \"" << filename << "\" gefunden!" << std::endl;
    }
    

    wo war jetzt das Problem bei der Aufgabe?



  • (D)Evil schrieb:

    ...wo war jetzt das Problem bei der Aufgabe?

    Ich würde mal sagen in der Ungenauigkeit der Aufgabenstellung:
    + Sollen die Zeilen genau so aussehen ? (also inkl. whitespaces)
    + Was, wenn der gesuchte String mehrfach oder mitten in einem anderen auftritt ?
    + ...

    Deine Lösung hat bestimmte Eigenschaften (z.B. zählt er auch "Umgebrochenes" mit), bei denen nicht klar ist, ob der OP sie will - außerdem würd ich nicht erst den gesamten Dateiinhalt einlesen, wenn man das nicht braucht.

    Würde nicht ein:

    // return vs. throw Geschmackssache; hier nur als Alternative
    count file_count_occurrences_of(const std::string& filename, const std::string& of)
    {
        std::ifstream file_stream(filename.c_str()); // "std::ios::in" ist bestenfalls irreführend
        if (!file_stream) return throw std::runtime_error("unable to open infile");
    
        std::string line;
        std::size_t count=0;
        while (std::getline(file_stream, line))
            if(line == of) ++count
    
        return count;
    }
    

    reichen ?

    BTW: Warum "count_occurrences_of", wenn es doch std::count_if() gibt ? 😉
    EDIT: Ach, jetzt war Werner doch ein wenig schneller....

    Gruß,

    Simon2.



  • (D)Evil schrieb:

    wo war jetzt das Problem bei der Aufgabe?

    Das Problem liegt darin, die Aufgabe mit weniger Code unter Einsatz der C++-Bibliothek zu lösen. Das hatte dixidix wohl vergessen zu sagen.

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <algorithm>    // count_if
    #include <iterator>     // istream_iterator
    #include <functional>   // bind2nd, equal_to
    
    int main()
    {
        using namespace std;
        ifstream datei( "input.txt" );
        cout << "Der Ausdruck 'xxxx' ist " << count_if( istream_iterator< string >( datei ), istream_iterator< string >(),
            bind2nd( equal_to< string >(), string("xxxx") ) ) << " mal enthalten" << endl;
    
        return 0;
    }
    

    queer_boy schrieb:

    kürzerer code ist meist aussagekräftiger.

    Gruß
    Werner



  • Danke, mit soviel hatte ich garnicht gerechnet.
    Ich muss da aber noch mal was anhängen. Ginge das auch wenn der gesuchte String verschachtelt wäre, etwa so:

    hsdgxxxx
    jhaxxxxsjdh
    sjaf
    kasjdfkjw
    xxxxmsdgh
    kjdf
    lwjkrxxxxfhlkwe
    grfadxxxx
    lejkrek
    xxxxashg
    

    Mit den beiden letzten Funktionen kommt nur 0 als Rückgabe!



  • Ja, ginge auch, du müsstest nur eine andere Vergleichsfunktion an count_if() übergeben:

    struct string_match
    {
      string_match(const string& data) : m_data(data) {}
      bool operator()(const string& wert)
      { return wert.find(m_data) != string::npos; }
    private:
      string m_data;
    };
    
    ...
    cout<<count_if(...,string_match("xxxx"));
    


  • CStoll schrieb:

    Ja, ginge auch, du müsstest nur eine andere Vergleichsfunktion an count_if() übergeben

    Na,ja vor allen darf man keine std::string's also Worte, die durch Whitespace Chars von einander getrennt sind, einlesen. Wenn man den count_if beibehalten möchte, bedarf es einer eigenen Klasse, die solange Zeichenfolgen liest, bis was passendes daher kommt. Etwa so

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <algorithm>    // count_if
    #include <iterator>     // istream_iterator
    #include <functional>   // bind2nd, equal_to
    
    struct Zeichenfolge
    {
        explicit Zeichenfolge( const std::string& txt = std::string() ) : m_txt( txt ) {}
        bool operator==( const Zeichenfolge& b ) const
        {
            return m_txt == b.m_txt;
        }
        friend std::istream& operator>>( std::istream& in, Zeichenfolge& zf )
        {
            const std::ios_base::fmtflags oldFlags = in.flags();
            in >> std::noskipws;
            std::string txt;
            char c = 0;
            for( std::string::iterator iMuster = Zeichenfolge::m_muster.begin()
                ; iMuster != Zeichenfolge::m_muster.end() && in >> c; ++iMuster )
            {
                txt.append( 1, c );
                if( c != *iMuster ) break;
            }
            in.flags( oldFlags );
            if( in ) swap( zf.m_txt, txt );
            return in;
        }
        static void SetMuster( const std::string& muster ) { m_muster = muster; }
    
    private:
        std::string m_txt;
        static std::string m_muster;
    };
    std::string Zeichenfolge::m_muster;
    
    int main()
    {
        using namespace std;
        const string muster = "xxxx";
        ifstream datei( "input.txt" );
        Zeichenfolge::SetMuster( muster );
        cout << "Der Ausdruck '" << muster << "' ist " << count_if( istream_iterator< Zeichenfolge >( datei ), istream_iterator< Zeichenfolge >(),
            bind2nd( equal_to< Zeichenfolge >(), Zeichenfolge( muster ) ) ) << " mal enthalten" << endl;
    
        return 0;
    }
    

    Die Lösung über die statische Variable ist eigentlich nicht so toll; spätestens bei Multithreading funktioniert das nicht mehr. Schöner wäre es, den Muster-String via Manipulatoren und ios_base::pword an den istream zu hängen, aber das bläht den Code weiter auf.

    Ich vermute aber mal, dass das ganze mit boost::regex oder so einfacher zu lösen ist.

    Gruß
    Werner



  • dixidix schrieb:

    ...Mit den beiden letzten Funktionen kommt nur 0 als Rückgabe!

    ... was auch vollkommen klar ist, wen man den Code versteht. 😃

    dixidix schrieb:

    ...

    hsdgxxxx
    jhaxxxxsjdh
    sjaf
    kasjdfkjw
    xxxxmsdgh
    kjdf
    lwjkrxxxxfhlkwe
    grfadxxxx
    lejkrek
    xxxxashg
    

    ...

    sollen eigentlich

    hsdgxxx
    xjha
    

    und

    x
      x
    xx
    

    auch mitgezählt werden ?

    Das ist eben genau das, was ich meinte: Du musst für Dich klären, was genau Du möchtest - das ist eine fachliche Entscheidung. Keine der Varianten ist "besser" als die anderen, sondern höchstens "passender".

    Außerdem würde ich Dir raten, die verschiedenen hier angebotenen Lösungen mal genau zu studieren und zu versuchen sie zu verstehen - dann kannst Du es vielleicht noch "passgenauer" programmieren.

    Gruß,

    Simon2.



  • So wie es jetzt ist also "xxxx" finden/zählen ist schon ok. Mir wäre sicher auch was eingefallen, aber sicher nicht so und anhand von solchen Beispielen lässt sich doch einiges besser nachvollziehen. Insofern wieder mal Danke!! 👍



  • Schade, ich habe gerade festgestellt, dass mein zuletzt geposteter Code nicht für beliebige Mustertexte funktioniert. Z.B. findet er den Ausdruck 'Hallo' in

    irgendwas HalHallo ..

    nicht. Lässt sich zwar reparieren, aber ich habe jetzt keine Zeit ...

    Gruß
    Werner



  • emmax schrieb:

    Ich würde die gesamte Datei in einen Puffer einlesen und dann mit der Funktion memcmp() mir die Anzahl der Vorkommen holen in einer Schleife holen.

    PS: memcmp() hat C++ von C geerbt.
    😉

    Hier mal mein Code

    #include <cstring>
    #include <fstream>
    #include <iostream>
    
    using namespace std; 
    
    int main () 
    {   
        ifstream file ("input.txt",  ios::binary | ios::ate);  
        streamsize len = file.tellg ();
        char* buf = new char [len];
        file.seekg (0, ios::beg);
        file.read (buf, len);
        file.close ();
    
        const char muster[] = "xxxx";
        int zähler = 0;
    
        for (streamsize i = 0; i < len; i++)
        {
            if (!memcmp (buf+i, muster, (sizeof muster)-1))
            {
                zähler++;
                i += (sizeof muster)-2;
            }
        }
    
        delete[] buf;
        cout << muster << " kommt " << zähler << " mal vor." << endl;
    }
     ;)
    


  • dixidix schrieb:

    So wie es jetzt ist also "xxxx" finden/zählen ist schon ok. Mir wäre sicher auch was eingefallen, aber sicher nicht so und anhand von solchen Beispielen lässt sich doch einiges besser nachvollziehen. Insofern wieder mal Danke!! 👍

    Noch was: Was soll bei

    xxxxx
    

    rauskommen ? 0, 1, 2 ?

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Noch was: Was soll bei

    xxxxx
    

    rauskommen ? 0, 1, 2 ?

    Gruß,

    Simon2.

    1

    ...
                zähler++;
                i += (sizeof muster)-2;  // Lesen!
    ...
    

    😉



  • emmax schrieb:

    Simon2 schrieb:

    Noch was: Was soll bei

    xxxxx
    

    rauskommen ? 0, 1, 2 ?

    Gruß,

    Simon2.

    1

    ...
                zähler++;
                i += (sizeof muster)-2;  // Lesen!
    ...
    

    😉

    Du kannst die Frage danach, was fachlich gewünscht ist, nicht mit einer Eigenschaft Deiner spezifischen Implementierung beantworten.

    Gruß,

    Simon2.



  • Öhm ... mein Code soll 0 zurückgeben? Oha ^^ Der macht es sogar möglich, dass der auch erkennt, wenn der Text in der nächsten Zeile weiter geht. Also er lässt das Newline außer acht.


Anmelden zum Antworten