Thread: File IOStreams

  1. #1
    Registered User
    Join Date
    Sep 2001
    Posts
    25

    Unhappy File IOStreams

    Hello,

    I am new to your boards but am a little experienced in C++. I am having troubles grasping the concept of File IO, if anyone has a link to a good tutorial or can explain it to me I will be forever greatful. My main concert is saving floatpoint integers and character strings into a file and later restoring my defined objects with the data(serialization on a toned down scale).

    Any help will be appreciated.

    Here's some of my code to show how I am saving the data and to what it is saved:

    CCharacter.h

    #include "CDice.hpp"
    #include <fstream.h>

    #if !defined(CCHARACTER)
    #define CCHARACTER
    enum STATUS{ALIVE, DEAD};
    class CCharacter{
    public:
    CCharacter();
    CCharacter(char* pName, CDice* PDICE, int FLAG);
    CCharacter(const char* pName, CDice* PDICE);
    CCharacter(const char* pName, int* pArray);
    ~CCharacter(){;}
    int GetMovement(){return Movement;}
    int GetWeaponSkill(){return WeaponSkill;}
    int GetBallisticSkill(){return BallisticSkill;}
    int GetStrength(){return Strength;}
    int GetToughness(){return Toughness;}
    int GetWounds(){return Wounds;}
    int GetInitiative(){return Initiative;}
    int GetAttacks(){return Attacks;}
    int GetDexterity(){return Dexterity;}
    int GetLeadership(){return Leadership;}
    int GetIntelligence(){return Intelligence;}
    int GetCool(){return Cool;}
    int GetWillPower(){return WillPower;}
    int GetFellowship(){return Fellowship;}
    char* GetName(){return NAME;}
    STATUS GetStatus(){return Status;}
    Wounded(int damageValue);//Damages the character by the value passed
    Save(char* pFile);
    protected:
    CDice* pDice;//global CDice object
    // CWeapon* pCurrentWeapon;
    // CArmor* pCurrentArmor;
    int Movement, WeaponSkill, BallisticSkill, Strength;
    int Toughness, Wounds, Initiative, Attacks;
    int Dexterity, Leadership, Intelligence, Cool;
    int WillPower, Fellowship;
    int aValues[14];
    char NAME[30];
    STATUS Status;//Status of character(ALIVE or DEAD)
    int CharToInt(char letter);
    };

    #endif

    CCharacter.cpp

    #include <fstream.h>
    #include "CCharacter.h"
    #include <string.h>

    CCharacter::CCharacter()
    {
    pDice = 0;
    Movement = 1;
    WeaponSkill = 1;
    BallisticSkill = 1;
    Strength = 1;
    Toughness = 1;
    Wounds = 1;
    Initiative = 1;
    Attacks = 1;
    Dexterity = 1;
    Leadership = 1;
    Intelligence = 1;
    Cool = 1;
    WillPower = 1;
    Fellowship = 1;
    Status = ALIVE;
    }

    CCharacter::CCharacter(char* pName, CDice* PDICE, int FLAG)
    {
    if(!FLAG == 0)
    {
    char str[30];
    ofstream output(pName,ios:ut);
    output<<str;
    strncpy(NAME, str, sizeof(NAME));
    NAME[sizeof(NAME) - 1] = '\n';
    output<<str;
    cout<<str;
    cout<<"\n name is "<<this->GetName();
    }
    else
    {
    cerr<<"Error has occured while opening file: "
    <<pName;
    return;
    }
    }
    CCharacter::CCharacter(const char* pName, CDice* PDICE)
    {
    strncpy(NAME, pName, sizeof(NAME));
    NAME[sizeof(NAME) - 1] = '\n';
    pDice = PDICE;
    Movement = pDice->Roll(3,1,2);
    WeaponSkill = pDice->Roll(10,2,20);
    BallisticSkill = pDice->Roll(10,2,20);
    Strength = pDice->Roll(3,1,1);
    Toughness = pDice->Roll(3,1,1);
    Wounds = pDice->Roll(3,1,4);
    Initiative = pDice->Roll(10,2,20);
    Attacks = 1;
    Dexterity = pDice->Roll(10,2,20);
    Leadership = pDice->Roll(10,2,20);
    Intelligence = pDice->Roll(10,2,20);
    Cool = pDice->Roll(10,2,20);
    WillPower = pDice->Roll(10,2,20);
    Fellowship = pDice->Roll(10,2,20);
    Status = ALIVE;
    }


    CCharacter::CCharacter(const char* pName, int* pArray)
    {
    strncpy(NAME, pName, sizeof(NAME));
    NAME[sizeof(NAME) - 1] = '\n';
    pDice = new CDice();
    aValues[0] = Movement = pArray[0];
    aValues[1] = WeaponSkill = pArray[1];
    aValues[2] = BallisticSkill = pArray[2];
    aValues[3] = Strength = pArray[3];
    aValues[4] = Toughness = pArray[4];
    aValues[5] = Wounds = pArray[5];
    aValues[6] = Initiative = pArray[6];
    aValues[7] = Attacks = pArray[7];
    aValues[8] = Dexterity = pArray[8];
    aValues[9] = Leadership = pArray[9];
    aValues[10] = Intelligence = pArray[10];
    aValues[11] = Cool = pArray[11];
    aValues[12] = WillPower = pArray[12];
    aValues[13] = Fellowship = pArray[13];
    Status = ALIVE;
    }

    CCharacter::Wounded(int damageValue)
    {
    this->Wounds -= damageValue;
    if(this->Wounds <= 0)
    {
    Status = DEAD;
    }
    }

    CCharacter::Save(char* pFile)
    {
    ofstream inp(pFile, 0);
    inp<<this->GetName()<<'\n';
    inp<<this->GetMovement()<<'\n';
    inp<<this->GetWeaponSkill()<<'\n';
    inp<<this->GetBallisticSkill()<<'\n';
    inp<<this->GetStrength()<<'\n';
    inp<<this->GetToughness()<<'\n';
    inp<<this->GetWounds()<<'\n';
    inp<<this->GetInitiative()<<'\n';
    inp<<this->GetAttacks()<<'\n';
    inp<<this->GetDexterity()<<'\n';
    inp<<this->GetLeadership()<<'\n';
    inp<<this->GetIntelligence()<<'\n';
    inp<<this->GetCool()<<'\n';
    inp<<this->GetWillPower()<<'\n';
    inp<<this->GetFellowship()<<'\n';
    }

    CCharacter::CharToInt(char letter)
    {
    return ((int)letter - 48);
    }

    CCdice.h

    #if !defined(CDICE)
    #define CDICE
    /************************************************** ******************/
    //This is the dice class used for generating random numbers based on
    //the values the user inputs. The function Roll(sides,times,modifier
    //, and flag are used to specify the range of values generated.
    //Sides is equal to the maximum number allowed. Times is equal to the
    //number of times a number is generated and added to the rolling total
    //modifier is equal to a number to be added to the number generated.
    //And finally flag is used to determine if the number is the lowest
    //or highest out of the times the number is generated from the range
    //allowed. NONE is the default, this setting keeps a rolling total.
    /************************************************** ******************/
    #include <fstream.h>

    enum FLAG{NONE, LARGEST, SMALLEST};


    class CDice {
    public:
    CDice();
    ~CDice(){};
    virtual int Roll(int sides, int times = 1, int modifier = 0,
    FLAG flag = NONE);
    int GetGenerated(); //Returns the Generated value.
    protected:
    void SetGenerated(int test){Generated = test;}
    int Generated; //Used to flag if the srand function has been
    //called.
    virtual int GetRandom(int sides);
    };

    #endif

    CDice.cpp

    #include "CDice.hpp"
    #include <stdlib.h>
    #include <time.h>
    #include <fstream.h>

    //Main function used to generated random numbers.

    CDice::Roll(int sides, int times,int modifier,FLAG flag)
    {
    int total =0;
    int* parray = new int[times];
    switch(flag)
    {
    case NONE:
    {
    for(int i=0;i<times;i++)
    {
    total += this->GetRandom(sides);
    }
    total += modifier;
    delete [] parray;
    break;
    }
    case SMALLEST:
    {
    int i = 0;
    for(i=0;i<times;i++)
    {
    parray[i] = this->GetRandom(sides);
    }
    total = parray[0];
    for(i=0;i<times;i++)
    {
    if(total > parray[i])
    total = parray[i];
    }
    total += modifier;
    delete [] parray;
    break;
    }
    case LARGEST:
    {
    int i = 0;
    for(i=0;i<times;i++)
    {
    parray[i] = this->GetRandom(sides);
    }
    for(i=0;i<times;i++)
    {
    if(total < parray[i])
    total = parray[i];
    }
    total += modifier;
    delete [] parray;
    break;
    }
    default:
    {
    total = 1010;
    delete [] parray;
    break;
    }
    }
    return total;
    }

    CDice::GetRandom(int sides)
    {

    if(!Generated)
    {
    srand( (unsigned)time( NULL ) );
    this->SetGenerated(true);
    }
    int total = 0, temp = 0;
    temp = rand();
    temp = (temp % sides) +1;
    total +=temp;
    return total;
    }
    //Returns the value of Generated.

    CDice::GetGenerated()
    {
    return Generated;
    }

    //Default constructor.
    CDice::CDice():Generated(false)
    {}

    /************************************************** ******************/

    Test.cpp(Main function for testing)
    include "CDice.hpp"
    #include "CCharacter.h"


    int CharToInt(char);

    int main()
    {
    CDice* PDICE = new CDice();
    CCharacter Jeramy("Jeramy", PDICE, 1);
    Jeramy.Save(Jeramy.GetName());

    /*
    int Num;
    char Char[]="34";
    Num = ((int)Char[1] - 48);
    cout<<Num<<'\n';
    Num = CharToInt(Char[0]);
    cout<<Num<<'\n';
    */
    return 0;
    }

    int CharToInt(char letter)
    {
    return ((int)letter - 48);
    }
    Last edited by Maghappy; 09-17-2001 at 11:54 PM.

  2. #2
    Registered User Strider's Avatar
    Join Date
    Aug 2001
    Posts
    149
    It depends on what you are looking for. If you are not worried
    about having a printable text file, then the easiest method would
    be to use the binary or random-access method for reading and
    writing a struct or class:

    Code:
    #include <fstream.h>
    
    CCharacter myCharacter;  
    ofstream outFile("filename.dat", ios::binary);
    
    // error check
    if ( !outFile)
    {
        cout << "File could not be opened." << endl;
        exit(1);
    }
    
    // Populate your structure if it has not been done.  
    // If there is more than one character to be saved, 
    // then create an array of structs and loop the 
    // process while (!outFile.eof).
    
    // C++ style binary output
    outFile.write( reinterpret_cast<const char*>( &myCharacter ), sizeof( CCharacter ) );
    
    // C style binary output
    outFile.write( (char* )&myCharacter, sizeof( CCharacter ) );
    
    outFile.close();
    
    // Reading in from a file.  Pretty much the same thing:
    
    ifstream inFile("filename.dat", ios::binary);
    
    // error check
    
    // C++ style binary input
    inFile.read( reinterpret_cast<char*>( &myCharacter ), sizeof( CCharacter ) );
    
    // C style binary input
    inFile.read( (char* )&myCharacter, sizeof( CCharacter ) );
    
    inFile.close();
    If text output or a sequential file is desired, then it gets a
    bit more involved. You will need to output the data individually.

    Code:
    #include <fstream.h>
    
    ofstream outFile("filename.txt", ios::out);
    
    // error check
    
    // use your own desired delimiter
    // I have used spaces here and newline to go to the next record.
    // Again, this can be looped if you have more than one character.
    outFile << myCharacter.GetMovement() << ' ' 
                << myCharacter.WeaponSkill() << '\n'; // and so on
    
    outFile.close();
    
    // or
    
    ifstream inFile("filename.txt", ios::in);
    
    int movement;
    int weaponSkill;
    // and so on
    
    // error check
    
    // you will need to create mutator functions in your class for this
    // if the data is being accessesed outside of the class
    while (inFile >> movement >> weaponSkill) // etc...
    {
        myCharacter.SetMovement(movement);
        myCharacter.SetWeaponSkill(weaponSkill);
    }
    
    inFile.close()
    As I have stated, the binary method is the easiest but the file
    generated is not readable text. The biggest problem with using
    sequential access is if you attempt to write or read in character
    strings containing spaces as spaces and newlines are automatically
    used as delimiters. You would have to read in these strings
    separately using something like the getline function:

    Code:
    // this will read in either 25 characters or until newline is reached.
    inFile.getline(myString, 25);
    The code is not tailored to your specific design. Use the methods
    how you see fit. I hope this has givin you a better understanding of
    how it can be used though.

    David
    One Ring to rule them all, One Ring to find them,
    One Ring to bring them all and in the darkness bind them
    In the Land of Mordor where the Shadows lie.

  3. #3
    Registered User
    Join Date
    Sep 2001
    Posts
    25
    OK, now that I know how to write and read the class from a file where should I go from there? I don't need to be able to print it so text mode isn't neccessary. Can I use the data returned from a read to invoke a copy constructor?

    example:

    CCharacter::Save(char* pFile)
    {
    char Temp[]={*pFile,".dat"};
    const char* pTemp = Temp;
    ofstream inp(pTemp, ios::binary);
    if ( !inp)
    {
    cerr << "File could not be opened." << endl;
    return 0;
    }
    inp.write(reinterpret_cast<const char*>( this ), sizeof( CCharacter ) );
    }

    Class is now stored in the file using binary format.

    Now how do I initilize a Character object with the data stored in the file?

  4. #4
    Registered User
    Join Date
    Sep 2001
    Posts
    25
    bump from the dead

  5. #5
    Registered User Strider's Avatar
    Join Date
    Aug 2001
    Posts
    149
    I apologize for not responding immediately. The network at work has been down for a couple of days now and I usually check on the status of any posts from there.

    Can I use the data returned from a read to invoke a copy constructor?
    It would be easier to create a new CCharacter object and read the data into it or use dynamic allocation to clear the existing data for reuse.

    Code:
    CCharacter myCharacter;
    inFile.read(reinterpret_cast<char*>(&myCharacter), sizeof(CCharacter));
    or
    Code:
    if (myCharacter != NULL)
    {
        delete myCharacter;
        myCharacter = 0;
    }
    
    myCharacter = new CCharacter;
    inFile.read(reinterpret_cast<char*>(myCharacter), sizeof(CCharacter));
    The other possibility is probably closer to what you are looking for. Try creating a member Read function for the CCharacter class that returns type CCharacter.
    Code:
    CCharacter *CCharacter::Read(char * fileName)
    {
        static CCharacter Temp;
        ifstream inFile(*(fileName), ios::binary);
    
        inFile.read(reinterpret_cast<char*>(&Temp), sizeof(CCharacter);
        
        if (!inFile)
        {
            cerr << "File could not be opened." << endl; 
            exit 1; 
        }
        inFile.close();  // don't forget to close the file
        return &Temp;
    }
    
    // elsewhere in code...
    CCharacter newCharacter(*(myCharacter.Read(fileName)));
    Or something similar to that. It can be done without the pointer return type. The pointer just saves on unnecessary copies of the data.

    char Temp[]={*pFile,".dat"};
    If you are trying to create one character array for the full filename, then try using concatenation. The above method will not work.

    David

    BTW -- if I have made an error, please forgive me. It is late where I am right now.
    Last edited by Strider; 09-20-2001 at 10:06 PM.
    One Ring to rule them all, One Ring to find them,
    One Ring to bring them all and in the darkness bind them
    In the Land of Mordor where the Shadows lie.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. sequential file program
    By needhelpbad in forum C Programming
    Replies: 80
    Last Post: 06-08-2008, 01:04 PM
  2. Encryption program
    By zeiffelz in forum C Programming
    Replies: 1
    Last Post: 06-15-2005, 03:39 AM
  3. System
    By drdroid in forum C++ Programming
    Replies: 3
    Last Post: 06-28-2002, 10:12 PM
  4. Hmm....help me take a look at this: File Encryptor
    By heljy in forum C Programming
    Replies: 3
    Last Post: 03-23-2002, 10:57 AM
  5. Need a suggestion on a school project..
    By Screwz Luse in forum C Programming
    Replies: 5
    Last Post: 11-27-2001, 02:58 AM