von fstream in structs



  • Hi

    Wie kann ich aus einer Datei auslesen und in eine struct schreiben?

    struct Weapon
        {
            unsigned int id;
            unsigned short type;
            unsigned short level;
            unsigned short strength;
            unsigned short speed;
            unsigned short weight;
            unsigned short range;
            unsigned int price;
            std::string name;
        };
    

    File:

    # ID Type Level Name Strength Speed Weight Price Range
    # Swords        
    1011 0 1 Sword 2 3 2 200 0
    1021 0 2 Longsword 3 4 2.3 700 0
    1031 0 3 Claymore 5 6 2.5 1200 0
    # Axes        
    1111 1 1 Axe 3 2 2.5 200 0
    1121 1 2 Poleaxe 4 3 2.8 700 0
    1131 1 3 Battleaxe 6 5 3.2 1200 0
    

    Hab schon getline gefunden, aber dann ist ja alles in einem string, wie kann ich das "wortweise", also jeweils bis zum Leerzeichen zerpflücken?

    Danke im voraus 🙂



  • Oder besser gefragt,
    warum geht

    void test_streams()
    {
            ifstream items( "items" );
            if( items.is_open() )
            {
                cout << "File open" << endl;
                while( items.good() )
                {
                    cout << "File good" << endl;
                    stringstream sstr;
                    items.get( *sstr.rdbuf() );
                    std::cout << sstr << std::endl;
                }
            }
    }
    

    nicht?
    Gibt mir nur pointer Addressen aus.. 😞



  • nimm doch einfach den >> operator ...



  • (D)Evil schrieb:

    nimm doch einfach den >> operator ...

    Hm, tja so einfach gehts..
    Aber wie kann ich eine Zeile überspringen bei '#' ?



  • lies einfach mit get ein zeichen am zeilenanfang, und wenns kein # ist, tus mit put zurück in den stream, ansonsten kannste die Zeile mit getline überspringen.



  • Eldoran schrieb:

    Hab schon getline gefunden, aber dann ist ja alles in einem string, wie kann ich das "wortweise", also jeweils bis zum Leerzeichen zerpflücken?

    getline ist nur dann sinnvoll, wenn das Zeilenende zur Formatierung der Daten notwendig ist. Das ist aber bei Dir so gar nicht der Fall.

    Eldoran schrieb:

    (D)Evil schrieb:

    nimm doch einfach den >> operator ...

    Hm, tja so einfach gehts..
    Aber wie kann ich eine Zeile überspringen bei '#' ?

    Genau; der operator>> tut genau das was notwendig ist. Und für das Überspringen der Kommentare sorgt ein sogenannter Manipulator. Alles zusammen sieht so aus:

    #include <string>
    #include <vector>
    #include <fstream>
    #include <cmath>        // std::floor für korrektes runden
    #include <iterator>     // istream_iterator
    
    // --   Kommentar überlesen
    std::istream& comment( std::istream& in )
    {
        typedef std::istream::traits_type traits_type;
        in >> std::ws;  // ggf. Leerzeichen überlesen
        for( int m; !traits_type::eq_int_type( m = in.peek(), traits_type::eof() )
            && traits_type::to_char_type( m ) == '#'; )
        {
            in.ignore( 9999, traits_type::to_int_type( '\n' ) );
        }
        return in;
    }
    
    struct Weapon
    {
        unsigned int id;
        unsigned short type;
        unsigned short level;
        unsigned short strength;
        unsigned short speed;
        unsigned short weight;
        unsigned short range;
        unsigned int price;
        std::string name;
    
        // --   Weapon einlesen
        friend std::istream& operator>>( std::istream& in, Weapon& w_ )
        {
            // # ID Type Level Name Strength Speed Weight/10 Price Range
            Weapon w;
            double weight_;
            if( in >> comment >> w.id >> w.type >> w.level >> w.name
                >> w.strength >> w.speed >> weight_ >> w.price >> w.range )
            {
                w.weight = std::floor( weight_ * 10.0 + 0.5 );
                w_ = w;
            }
            return in;
        }
    };
    
    int main()
    { 
        using namespace std;
        ifstream items( "items" );
        vector< Weapon > weapons( (istream_iterator< Weapon >( items )), istream_iterator< Weapon >() );
        // .. das war's
        return 0;
    }
    

    Bem.: ich habe mal angenommen, dass das Gewicht mit Faktor 10 in der struct Weapon abgelegt ist.

    Gruß
    Werner



  • Ui 😮
    Danke!
    Werd das mal durchgehen, ob ich's kapier.

    Bem.: ich habe mal angenommen, dass das Gewicht mit Faktor 10 in der struct Weapon abgelegt ist.

    Das hab ich vorhin auch gemerkt. Wird dann wohl eher ein double, als ein short werden.

    Aber mir stellt sich schon die nöchste Frage.
    Mein File sieht im Moment so aus:

    # Weapons        
    # ID Type Level Name Strength Speed Weight Price Range
    # Swords        
    1011 0 1 Sword 2 3 2 200 0
    1021 0 2 Longsword 3 4 2.3 700 0
    1031 0 3 Claymore 5 6 2.5 1200 0
    # Axes        
    1111 1 1 Axe 3 2 2.5 200 0
    1121 1 2 Poleaxe 4 3 2.8 700 0
    1131 1 3 Battleaxe 6 5 3.2 1200 0
    # Armour        
    # ID Type Level Name Absorb Handicap Weight Price 
    # Helms        
    2011 0 1 Lether 3 2 1.6 500 
    2021 0 2 Chain 5 3 2.4 1000 
    2031 0 3 Massive 8 4 3.2 1500 
    # Cuirass        
    2111 1 1 Lether 3 2 0.9 700 
    2121 1 2 Chain 5 3 1.2 1400 
    2131 1 3 Massive 8 4 1.8 2100
    

    Und es kommen wohl noch weitere Arten von Items dazu.
    Die Waffen und Rüstungen sollen aber in verschiedene sets, da ich keine polymorphen Objekte machen will. Hab ich da ne Chance das auf deine elegante Art zu machen? Oder soll ich lieber verschiedene Files machen?

    Danke nochmal 👍



  • Eldoran schrieb:

    Hab ich da ne Chance das auf deine elegante Art zu machen?

    im Prinzip schon. Die Frage ist zunächst mal wo in der Datei die Waffen aufhören und die Rüstungen anfangen. Mal angenommen es sind immer genau 6, dann ist es einfach. Du erstellst eine struct Armour nebst operator>> nach dem gleichen Muster wie Weapon und im Hauptprogramm liest Du 6 Waffen und 6 Rüstungen

    int main()
    { 
        using namespace std;
        vector< Weapon > weapons;
        vector< Armour > armours;
        ifstream items( "items" );
        Weapon w;
        for( int n = 0; n<6 && items >> w; ++n )
            weapons.push_back( w );
    
        // usw. für die Rüstungen
        return 0;
    }
    

    Falls Du eine dynamische Anzahl haben möchtest, kannst Du in der Datei entweder die Anzahl davor schreiben (einfach und sicher) oder Dir eine Markierung ausdenken, die das Ende der Waffen angibt und dabei natürlich von einem Kommentar und einer Zahl eindeutig zu unterscheiden ist. Letzteres ist nur zu empfehlen, wenn die Datei von Hand editiert werden soll, weil dann die Angabe der Anzahl redundant und daher fehleranfällig ist. Das Einlesen ist dann aber etwas schwieriger zu programmieren.

    Gruß
    Werner



  • So, habs nun hinbekommen, dynamisch mit Steuerzeichen.
    Wohl nicht sehr elegant, aber es tut was es soll.

    typedef std::istream::traits_type traits_type;
    
    // überspringe Kommentarzeilen "#"
        std::istream& skip_comment( std::istream& in )
        {
            in >> std::ws;
            for( traits_type::int_type m; 
                !traits_type::eq_int_type( m = in.peek(), traits_type::eof() )
                 && traits_type::to_char_type( m ) == '#'; )
            {
                in.ignore( 9999, traits_type::to_int_type( '\n' ) );
            }
            return in;
        }
    
    // überspringe Zeilen mit Steuerungsbefehl "!"
        std::istream& skip_command( std::istream& in )
        {
            in >> std::ws;
            for( traits_type::int_type m; 
                !traits_type::eq_int_type( m = in.peek(), traits_type::eof() )
                 && traits_type::to_char_type( m ) == '!'; )
            {
                in.ignore( 9999, traits_type::to_int_type( '\n' ) );
            }
            return in;
        }
    
    // lade Werte aus Datei
        void BattleSystem::load_values()
        {
            enum Section
            {
                NONE = 0,
                WEAPON = 1,
                ARMOR = 2
            };
    
            Section section = NONE;
            ifstream items( "items" );
            if( items.is_open() )
            {
                while( items.good() )
                {
                    if( '!' == traits_type::to_char_type( items.peek() ) )
                    {
                        section = static_cast<Section>(section + 1);
                        items >> skip_command;    // Nach Bearbeitung des Befehls Position weiterschieben
                    }
                    switch( section )
                    {
                        case WEAPON:
                        {
                            Weapon weapon;
                            items >> weapon;    // Entsprechende operator>> überladung s.o.
                            m_weapons.insert( weapon );
                            break;
                        }
                        case ARMOR:
                        {
                            Armor armor;
                            items >> armor;    // dito
                            m_armors.insert( armor );
                            break;
                        }
                        default:
                            break;
                    }
                    items.ignore();    // ohne dies lädt er nur die Waffen :confused:
                }
            }
            items.close();
        }
    

    Und das File sieht so aus:

    !
    #Weapons
    # ID Type Level Name Strength Speed Weight Price Range
    # Swords        
    1011 0 1 Sword 2 3 2 200 0
    1021 0 2 Longsword 3 4 2.3 700 0
    1031 0 3 Claymore 5 6 2.5 1200 0
    # Axes        
    1111 1 1 Axe 3 2 2.5 200 0
    1121 1 2 Poleaxe 4 3 2.8 700 0
    1131 1 3 Battleaxe 6 5 3.2 1200 0
    !
    #Armor#
    # ID Type Level Name Absorb Handicap Weight Price 
    # Helms        
    2011 0 1 Lether 3 2 1.6 500 
    2021 0 2 Chain 5 3 2.4 1000 
    2031 0 3 Massive 8 4 3.2 1500 
    # Cuirass        
    2111 1 1 Lether 3 2 0.9 700 
    2121 1 2 Chain 5 3 1.2 1400 
    2131 1 3 Massive 8 4 1.8 2100
    ! #EOF
    

    Danke nochmal vielmals euch allen, vor allem Werner 👍
    Gruss


Anmelden zum Antworten