#1
  1. The bad and the ugly...
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2007
    Location
    Oz... No??? Neverland then?
    Posts
    142
    Rep Power
    0

    C++ binary file reader


    Hello again dev-shed users,

    I was tasked with writing a binary file viewer for homework. I've got my code running, and everything works great... but it seems to read/print out past the end of the file.

    Basically, it displays 20 lines at a time, with 16 bits of hex followed by 16 bits of ASCII. After the 20 lines have printed, it pauses and waits for you to hit enter before printing the following 20 lines. Once it reaches the end of the file it quits.

    Code:
    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <fstream>
    #include <ios>
    
    //
    //function prototype
    void PressEnterToContinue();
    
    
    using namespace std;
    
    
    void main(int argc, char *argv[])
    {
    	int n = 0; //used for prompting input file name
    	int r;
    	int x = 0, m = 0, a = 0;
    	ifstream filename; //start filestream
    	int size; //for file size
    	char *file_buffer; //buffer for storing file
    
    	//if user calls program from cmd prompt incorrectly or with "/?"
    		if((argc < 2) || (strcmp(argv[1],"/?") == 0)) 
    			{
    				cout << "This program reads a file and prints each character as a hexadecimal\n";
    				cout << "\n";
    				cout << "Usage: hw5 [input filename]\n";
    				exit(0);
    			}
    	
    		//
    		//prompt user for file name and continue to do so until a valid file name is given
    		do
    		{	
    			filename.open(argv[1], ios::binary | ios::ate); //open file in binary mode
    			if ( !filename )
    				{
    					cout << "Please enter a valid file name.\n";
    					cin >> argv[1]; 
    				}
    			else	
    				{
    					n = 1; //end while loop since file is open
    				}
    		} while (n != 1);
    		
    		
    		filename.seekg(0, ios::end); //seek EOF
    		size = filename.tellg(); //return pointer at that position
    		file_buffer = new char[size]; 
    		filename.seekg(0, ios::beg); //seek beginning of file
    		filename.read(file_buffer, size); //read file, up to int size, into file_buffer
    		
    		
    		//print 20 lines in binary followed by ASCII
    		do
    		{
    			for (int g = 0; g <= 20; g++)
    			{
    				
    				for (x; x < 16 + a; x++)
    					{
    						r = file_buffer[x]; //caste cout to type int, so 'hex' can work
    						cout << hex << setw(2) << uppercase << r << " ";
    					}
    				
    				cout << "\t"; //align ASCII portion on right	
    				
    				for (m; m < 16 + a; m++)
    					{
    					//catch control character (endet/new line) and print '.' in it's place
    						if (iscntrl(file_buffer[m]))
    							cout << "."; 
    						else	
    							cout << file_buffer[m];
    					}
    				
    				//increment a to print the next 16 bits
    				a+=16;
    				cout << endl;
    			}
    			
    			//pause and wait for 'Enter' to continue
    			PressEnterToContinue();
    
    		//EOF is an int. file_buffer only stores characters. 
    		//can't use file_buffer[x]		
    		} while (x <= size ); 
    		
    		
    		//
    		//close file at program exit
    		filename.close();
    }
    
    void PressEnterToContinue( )
     {
    	cout << "Press ENTER to continue... ";
    	cin.ignore( std::numeric_limits <std::streamsize> ::max(), '\n' );
     }

    Phew, so everything works great except when it reach the end of the file. Say there's not a full 20 lines left to print, it runs over and displays gobledeegook before exiting.

    I think I'm going to need an if/else statement in my for loops to catch an EOF, but I'm having trouble wrapping my head around what flag to actually use.

    file buffer is a type char * array, so it's an array of type string.

    EOF is an int, in my case -1... so its going to stop at it, but not read it into the array. so i can't use:
    Code:
    file_buffer[x] != EOF
    since there is no -1 in my buffer.

    help??



    Thanks
    ~Tim
    "Life is not a journey with the intent on arriving at the finish line in a pretty and well preserved body. But rather to skid in broadside, totally worn out, thoroughly used up and loudly proclaiming, "Wow! What a ride!" -Anonymous
    Halo! || Diablo 2 LOD Modding || OLGA's BACK!
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    71
    Rep Power
    2
    Are you saving the amount of bytes read with each filename.read(file_buffer, size) ? You need this value when you display your buffer so that you only display the values read.

    Actually my bad, you have to call gcount() to find the number of characters read. Something like below:

    Code:
    #include <iostream>
    #include <fstream>
    
    int main(int argc, char** argv)
    {
        char buffer[100];
        
        std::ifstream fin ("testfile", std::ifstream::binary);
        
        fin.read(buffer, 100);
        
        std::cout << fin.gcount() << std::endl;
    	
    	for (size_t i = 0; i < fin.gcount(); ++i)
    		std::cout << (unsigned long)buffer[i] << std::endl;
        
        fin.close();
        return 0;
    }
  4. #3
  5. The bad and the ugly...
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2007
    Location
    Oz... No??? Neverland then?
    Posts
    142
    Rep Power
    0
    that would work. I just ended up using the same closing statement as my do/while loop in an if/else inside my for loops.

    only issue now is spacing for that last line. say i only write out 6 hex instead of 16, that
    Code:
    cout << "/t";
    is no longer enough to get the ASCII portion to line up with the right side of the screen.
    "Life is not a journey with the intent on arriving at the finish line in a pretty and well preserved body. But rather to skid in broadside, totally worn out, thoroughly used up and loudly proclaiming, "Wow! What a ride!" -Anonymous
    Halo! || Diablo 2 LOD Modding || OLGA's BACK!
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    1
    Rep Power
    0

    Use a check inside the 2nd for loop.


    Originally Posted by ChokolAwt
    Hello again dev-shed users,

    I was tasked with writing a binary file viewer for homework. I've got my code running, and everything works great... but it seems to read/print out past the end of the file.

    Basically, it displays 20 lines at a time, with 16 bits of hex followed by 16 bits of ASCII. After the 20 lines have printed, it pauses and waits for you to hit enter before printing the following 20 lines. Once it reaches the end of the file it quits.

    Code:
    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <fstream>
    #include <ios>
    
    //
    //function prototype
    void PressEnterToContinue();
    
    
    using namespace std;
    
    
    void main(int argc, char *argv[])
    {
    	int n = 0; //used for prompting input file name
    	int r;
    	int x = 0, m = 0, a = 0;
    	ifstream filename; //start filestream
    	int size; //for file size
    	char *file_buffer; //buffer for storing file
    
    	//if user calls program from cmd prompt incorrectly or with "/?"
    		if((argc < 2) || (strcmp(argv[1],"/?") == 0)) 
    			{
    				cout << "This program reads a file and prints each character as a hexadecimal\n";
    				cout << "\n";
    				cout << "Usage: hw5 [input filename]\n";
    				exit(0);
    			}
    	
    		//
    		//prompt user for file name and continue to do so until a valid file name is given
    		do
    		{	
    			filename.open(argv[1], ios::binary | ios::ate); //open file in binary mode
    			if ( !filename )
    				{
    					cout << "Please enter a valid file name.\n";
    					cin >> argv[1]; 
    				}
    			else	
    				{
    					n = 1; //end while loop since file is open
    				}
    		} while (n != 1);
    		
    		
    		filename.seekg(0, ios::end); //seek EOF
    		size = filename.tellg(); //return pointer at that position
    		file_buffer = new char[size]; 
    		filename.seekg(0, ios::beg); //seek beginning of file
    		filename.read(file_buffer, size); //read file, up to int size, into file_buffer
    		
    		
    		//print 20 lines in binary followed by ASCII
    		do
    		{
    			for (int g = 0; g <= 20; g++)
    			{
    				
    				for (x; x < 16 + a; x++)
    					{
    						r = file_buffer[x]; //caste cout to type int, so 'hex' can work
    						cout << hex << setw(2) << uppercase << r << " ";
    					}
    				
    				cout << "\t"; //align ASCII portion on right	
    				
    				for (m; m < 16 + a; m++)
    					{
    					//catch control character (endet/new line) and print '.' in it's place
    						if (iscntrl(file_buffer[m]))
    							cout << "."; 
    						else	
    							cout << file_buffer[m];
    					}
    				
    				//increment a to print the next 16 bits
    				a+=16;
    				cout << endl;
    			}
    			
    			//pause and wait for 'Enter' to continue
    			PressEnterToContinue();
    
    		//EOF is an int. file_buffer only stores characters. 
    		//can't use file_buffer[x]		
    		} while (x <= size ); 
    		
    		
    		//
    		//close file at program exit
    		filename.close();
    }
    
    void PressEnterToContinue( )
     {
    	cout << "Press ENTER to continue... ";
    	cin.ignore( std::numeric_limits <std::streamsize> ::max(), '\n' );
     }

    Phew, so everything works great except when it reach the end of the file. Say there's not a full 20 lines left to print, it runs over and displays gobledeegook before exiting.

    I think I'm going to need an if/else statement in my for loops to catch an EOF, but I'm having trouble wrapping my head around what flag to actually use.

    file buffer is a type char * array, so it's an array of type string.

    EOF is an int, in my case -1... so its going to stop at it, but not read it into the array. so i can't use:
    Code:
    file_buffer[x] != EOF
    since there is no -1 in my buffer.

    help??



    Thanks
    ~Tim
    You can put a check inside the 2nd for loop. Like this:
    if (x>size) {return 0;}..Here is what you can try for the following part of your code and it should work:

    Code:
    while (x <= size )
    		{
    			for (int g = 0; g <= 20; g++)
    			{
    				
    				for (x; x < 16 + a; x++)
    					{
                                             if (x>size) {
                                                return 0;
                                              }
                                             else{
    						r = file_buffer[x]; //caste cout to type int, so 'hex' can work
    						cout << hex << setw(2) << uppercase << r << " ";
                                           }
    					}
    				
    				cout << "\t"; //align ASCII portion on right	
    				
    				for (m; m < 16 + a; m++)
    					{
    					//catch control character (endet/new line) and print '.' in it's place
    						if (iscntrl(file_buffer[m]))
    							cout << "."; 
    						else	
    							cout << file_buffer[m];
    					}
    				
    				//increment a to print the next 16 bits
    				a+=16;
    				cout << endl;
    			}
    			
    			//pause and wait for 'Enter' to continue
    			PressEnterToContinue();
    
    		//EOF is an int. file_buffer only stores characters. 
    		//can't use file_buffer[x]		
    		}

IMN logo majestic logo threadwatch logo seochat tools logo