Thread: simple i/o error that I cannot solve

  1. #1
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937

    simple i/o error that I cannot solve

    So, I am writing my very own binary xor-based encryptor. Grand. I am running into a very strange problem with ifstream.
    I'll open the file without error, and then use istream::getline(). Getline() functions normally, but subsequent calls to fail() (on the file stream) are true. Subsequent calls to get() (on the file stream) return -1. Strangely, subsequent calls to getline() (even after fail() is true) function normally. What is going on? Additionally, the end of the file has NOT been reached. Here's a sample of the problematic code:
    Code:
     ios::iostate xor_file::xor_open(string input_path)
    {
     char* buffer = new char[25];//DEBUG
     string sample;
     encoded = decoded = xor_open_switch = false;
                              
     reader.open(input_path.c_str(),ios::binary);    //open the file
     if(reader.fail())                               //check for failure
     {
      reader.close(); 
      return ios::failbit;
     }         
                       //Didn't fail....
     xor_open_switch = true;                         //we're open for business (until close()is called)
     
     if(reader.fail()) cout << "something's wrong BEFORE FIRST GETLINE. xor_open()" << endl; //DEBUG
     reader.getline(buffer,25,'@');                    //sample for c:\windows\desktop\primes.txt
     if(reader.fail()) cout << "something's wrong AFTER FIRST GETLINE. xor_open()" << endl; //DEBUG
     sample = buffer;                                  //make it a string DEBUG
     cout << "open()'s \"sample\": " << sample << endl;      //DEBUG
    . . . . . . . (in same function)
    Code:
    cout << "Some get(): ";
     for(int z = 0; z < 10; z++) cout << reader.get();    //DEBUG
       cout << boolalpha << "reader.eof(): " << reader.eof() << endl; //DEBUG
    The output of this code is:
    something's wrong AFTER FIRST GETLINE. xor_open()
    open()'s "sample": 25 characters of good health
    Some get(): -1-1-1-1-1-1-1-1-1-1-1-1
    reader.eof(): false
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  2. #2
    Registered User
    Join Date
    Dec 2006
    Location
    Scranton, Pa
    Posts
    252
    I do it differently, but that probably doesn't matter by struct. I'm not familar with ios::iostate (though probably should be?) but I do always include ios:in whenever reading from a file, bin or otherwise.

  3. #3
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    >> Strangely, subsequent calls to getline() (even after fail() is true) function normally. What is going on? Additionally, the end of the file has NOT been reached.

    Can you show this? What is in the file that you are reading. These results would be expected if your file contained

    Code:
    25 characters of good health
    That way, all 25 chars would be read into the buffer, no @ would be encountered, EOF would be set, fail would then return true, and get would subsequently return -1's and of course eof() would be true also.

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    So what exactly is this function about???

    One funny thing is that the string you read from the file is actually 28 characters long. May-be it's something to do with not having a 0-terminator on buffer?

    Since it is about xor encryption, may-be you shouldn't use getline: some characters might get excrypted into newline delimiters. May-be try read?

    get seems to return -1 if the end of the file has been reached. You should rather use the return values of the input functions, rather than eof().

    And to check if a function was successful, it's probably easier to return a bool.

    By the way, if you failed to open a file, do you think you can close it?

  5. #5
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    "25 characters of good health" was a noncontextual example.my bad. Allow me to elaborate:
    class xor_file is an object meant to control i/o, encryption, decryption and storage of passwords, keys, etc.
    xor_file::xor_open() would be the first function called by an xor_file object.
    1. Open file
    2. See if the file has already been encrypted by the program
    2a. If it has been encoded already, read in data regarding the password and key so that it might be decoded later.
    2b. If not, simply set the necessary flags to denote an unencrypted file.
    3. Return

    Now, I had found that after calling the function, I was getting trouble in another function xor_file::xor_encode(). I found that the issue went all the way back to xor_open(), and so I pasted some code from the former function to see what was up. In my code, //DEBUG is beside any line that was added for debugging.
    Here is the whole function:
    Code:
    ios::iostate xor_file::xor_open(string input_path)
    {
     char* buffer = new char[25];
     string sample;
     encoded = decoded = xor_open_switch = false;   //member switches
                              
     reader.open(input_path.c_str(),ios::binary);    //open the file
     if(reader.fail())                               //check for failure
     {                                                  //yes, I could have just done if(!reader)
      reader.close(); 
      return ios::failbit;
     }         
                       //Didn't fail....
     xor_open_switch = true;                         
     
     if(reader.fail()) cout << "something's wrong BEFORE FIRST GETLINE. xor_open()" << endl; //DEBUG
     reader.getline(buffer,25,'@');                    //sample for c:\windows\desktop\primes.txt
     if(reader.fail()) cout << "something's wrong AFTER FIRST GETLINE. xor_open()" << endl; //DEBUG
     sample = buffer;                                  //make it a string DEBUG
     cout << "open()'s \"sample\": " << sample << endl;      //DEBUG
     
     if(sample == "XOR_CODEC_ENCRYPTED_FILE")   //This file is already encrtypted
     {
      encoded = true;
      encoded_path = input_path;                             //set the xor_file path to the valid path
      
      getline(reader,sample,'@');                   //sample for password
      cout << "Password (encrypted): " << sample << endl;        //DEBUG
      for(int x = 0; x < sample.size(); x++) sample[x] -= static_cast<char>(1); //TODO: use better algorithm
      cout << "Password (decrypted): " << sample << endl;        //DEBUG
      password = sample;
      
      getline(reader,sample,'@');                   //sample for key
      cout << "Key: " << sample << endl;
      key = sample;
     }
     else                                       //This file is not encrypted
     {
      decoded = true;
      decoded_path = input_path;                             //set the xor_file path to the valid path
      //reader.seekg(1,ios::beg);                              //reset reader for encoding later
       for(int z = 0; z < 10; z++) cout << reader.get();    //DEBUG
       cout << boolalpha << "reader.eof(): " << reader.eof() << endl; //DEBUG
     }
    Here is the input file (text):

    This is a text-only test just to see if the damn
    thing works. Of course, as I write this, I haven't
    even compiled all of my new functions. It is likely
    that I will get tons of error and not even use
    this test file for quite a while. Still, I'm not
    discouraged. (quite more than 25 chars)

    and the output:

    Please input path to file.
    c:\windows\desktop\input.txt
    something's wrong AFTER FIRST GETLINE. xor_open()
    open()'s "sample": This is a text-only test
    -1-1-1-1-1-1-1-1-1-1reader.eof(): false

    Thank you for your help.
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  6. #6
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Well, it's strange, but the getline function does set the stream in the error state for some reason after the first read. (The string getline works well.) Once the file stream is in the error state, the next reads won't make any sense, so don't worry about the get() calls. The problem is with the first getline().

    (An easier way would probably be to read in the whole file at once, and then parse the "metadata".)

    Edit: here's my test (DevC++ 4.9.9.2 & MingW)
    Code:
    #include <iostream>
    #include <fstream>
    #include <string>
    
    #define STRINGVERSION 1 //use 0 to see char[] version
    
    int main()
    {
        std::ifstream file("test.txt", std::ios::binary);
        #if STRINGVERSION
            std::string buffer;
        #else
            char buffer[25];
        #endif
        if (file) {
            #if STRINGVERSION
                while (std::getline(file, buffer, '@'))
            #else
                while (file.getline(buffer, 25, '@'))
            #endif
            {
                std::cout << buffer << '\n' << "Good: " << file.good()
                          << '\n' << "Fail: " << file.fail()
                          << '\n' << "Bad: " << file.bad()
                          << '\n' << "Eof: " << file.eof() << std::endl;
            }
        }
        else {
            std::cout << "Couldn't open file" << '\n';
        }
        std::cin.get();
    }
    
    /*(test.txt) =
    This is a text-only test@just to see if the damn@
    thing works. Of course, as I write this, I haven't
    even compiled all of my new functions. It is likely
    that I will get tons of error and not even use
    this test file for quite a while. Still, I'm not
    discouraged. (quite more than 25 chars)
    */
    The string version works properly: it reads the file in 3 chunks and discards the delimiters.
    In char version the filestream stays good until it meets the delimiter in the 25 characters, otherwise getline makes !file and the loop exits (first 2 chunks are printed in the loop).

    I've never used getline with char* buffer before, but I'd rather go with the string version.
    Last edited by anon; 12-17-2006 at 06:14 AM.

  7. #7
    The larch
    Join Date
    May 2006
    Posts
    3,573
    OK, found out what's the problem in this thread. See post #4.

    Code:
    #include <iostream>
    #include <fstream>
    const char BUFFER_SIZE = 25;
    int main()
    {
        std::ifstream file("test.txt", std::ios::binary);
        char buffer[BUFFER_SIZE];
        if (!file) return 1;
        
        //while any characters successfully read (gcount())
        while (file.getline(buffer, BUFFER_SIZE, '@').gcount()) {
            //clear the fail bit
            file.clear(file.rdstate() & ~std::ios::failbit);
            std::cout << buffer << '\n' << "Good: " << file.good()
                      << '\n' << "Fail: " << file.fail()
                      << '\n' << "Bad: " << file.bad()
                      << '\n' << "Eof: " << file.eof() << std::endl;
            /*clear the buffer
            It may not be filled entirely and that would leave
            parts of it from previous getline()s.
            */ 
            for (int i = 0; i < BUFFER_SIZE; i++)
                buffer[i] = 0;
        }
        std::cin.get();
    }
    Also note that file.getline(buffer, n, delimiter) reads n-1 characters to append the zero-terminator.
    Last edited by anon; 12-17-2006 at 06:47 AM.

  8. #8
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    Thank you very much for your help. However, I will choose not to read in the entire file and then work with it in memory, because (this application being an encryptor) I might be dealing with obscenely large files. Anyways, thanks again.
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  9. #9
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    Oh, one more thing. Suppose I want to read data into a string as usual:
    Code:
    istream::getline(file_stream,std_string_obj,delim_char);
    But I also wanted to place a cap on the number of chars to be read. So far I haven't found such a version of the function. Does one exist?
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

  10. #10
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Not sure. In any case I wouldn't use getline with encrypted files, because it discards delimiters. With XOR encryption you have little control, which character might end up being converted to the delimiter.

    Anyway, if all you want to do with the files is encrypting/decrypting you could use char arrays just as well (and use read() to read the chunks). Just don't use any string functions (e.g strlen) and don't forget to find out how many characters you actually read from the file (gcount()) which gives you the real string length.

    Of course, someone might know how to do it with strings.

  11. #11
    Kiss the monkey. CodeMonkey's Avatar
    Join Date
    Sep 2001
    Posts
    937
    Yes, for the acctual encryption/decryption everything would be cstrings. I just wanted to use std::strings for the file header stuff to make it easier. Easier it is not.
    "If you tell the truth, you don't have to remember anything"
    -Mark Twain

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Simple Socialising Chat Bots
    By bengreenwood in forum C++ Programming
    Replies: 10
    Last Post: 11-28-2007, 08:42 AM
  2. simple file I/O question
    By loopshot in forum C Programming
    Replies: 2
    Last Post: 11-30-2006, 02:59 PM
  3. Simple File I/O
    By KittyzPuppy in forum C++ Programming
    Replies: 2
    Last Post: 03-29-2003, 07:40 AM
  4. Very simple question, problem in my Code.
    By Vber in forum C Programming
    Replies: 7
    Last Post: 11-16-2002, 03:57 PM
  5. Simple Compile Time Problem - HELP!
    By kamikazeecows in forum Windows Programming
    Replies: 2
    Last Post: 12-02-2001, 01:30 PM