views:

882

answers:

5

So the teacher has posed this assignment:

You have been hired by the United Network Command for Law Enforcement, and you have been given files containing null cyphers you have to decrypt.

So for the first file given (as an example), every other letter is correct (ie: 'hielqlpo' is hello (assuming you start with the first letter). My first question is, how do I read in a file? The document is on my desktop in a folder and the file is named document01.cry. I'm not sure the command I need to put that file into the program.

I'm also not overly sure how to grab a letter and skip a letter, but honestly I want to tinker with that before I post that question! So for now...my question is as stated in the title: How do you grab a file for reading in C++?

If it makes a difference (As I'm sure it does), I'm using Visual C++ 2008 Express Edition (because it's free and I like it! I've also attached what I have so far, please keep in mind it's -very- basic...and I added the getchar(); at the end so when it does run properly, the window stays open so I can see it (as Visual Express tends to close the window as soon as it's done running.)

The code so far:

#include<iostream>

using namespace std;

int main()
{
    while (! cin.eof())
    {
        int c = cin.get() ;
        cout.put(c) ;
    }
    getchar();
}

PS: I realize that this code grabs and puts out every character. For now that's fine, once I can read in the file I think I can tinker with it from there. I'm also poking at a book or two I have on C++ to see it anything pops up and screams "Pick me!" Thanks again!

EDIT:: Also curious, is there a way to input the file you want? (I.e.:

char filename;
cout << "Please make sure the document is in the same file as the program, thank you!" << endl << "Please input document name: " ;
cin >> filename;
cout << endl;
ifstream infile(filename, ios::in);

This code doesn't work. It shoots back an error saying the char can't be converted to a const char *. How can this problem be fixed?

EDIT 2: Never mind about said part 2, I found it out! Thanks again for the assistance!

+3  A: 

C++: Input/Output with files

Niyaz
+9  A: 

To do file operations, you need the correct include:

#include <fstream>

Then, in your main function, you can open a file stream:

ifstream inFile( "filename.txt", ios::in );

or, for output:

ofstream outFile( "filename.txt", ios::out );

You can then use inFile as you would use cin, and outFile as you would use cout. To close the file when you are done:

inFile.close();
outFile.close();
Colin
Thanks much! The site that Niyaz posted showed me that as well, however, what if the file is in a folder (ie: C:\Documents and Settings\J\Desktop\College work\CSIS 296\asst4\Documents)? Granted I'm asking before I test, but am testing as soon as I hit "add comment"!
Jeff
Found out it works if you use "filename.txt" if it's in the same folder as the .cpp file of the program...but what if it's in a different file? In case what I said above seemed a bit confusing
Jeff
You can always pass the filename as a parameter to the programfoobar.exe c:\myFile.txtAnd then check for that argument to determine what file to open
Scott
Afraid I don't understand what you mean by that, could you explain a bit more?
Jeff
main(char*, int ) function takes two arguments, a list of strings and the number of strings passed. While running your exe from the command prompt you can pass these arguments like this: foobar.exe c:\a.txt (with the full path if it is in a different directory). Open this file from your code.
Naveen
instead of "filename.txt", use: "C:/firstfolder/secondfolder/filename.txt". Make sure to use forward slashes.
Colin
@Naveen - Maybe C++ is somehow different from C, but I'm pretty sure it's "int main(int, char *)" not "int main(char *, int)".
Chris Lutz
@Colin - That may actually be it, as I used backslashes (as in windows, shame on me, I know), will give that a whirl!
Jeff
Yea, it's main(int, char *).
John T
@Colin - Yep! Was the case! One issue down!
Jeff
@Jeff - You can use backslashes if you want to, but you have to double them up! You can either use: "C:/SomeFolder/SomeOtherFolder/file.txt", try "C:\\SomeFolder\\SomeOtherFolder\\file.txt". Windows doesn't care; Personally, I prefer the single, forward slash.
Colin
@Colin - Makes sense@Scott (in case you are looking here) - I'm afraid while I can understand a bit of the code you wrote, there's a bit there I don't understand. Perhaps I should clarify my knowledge. Is my first C++ class, we are about...4-6 weeks into it.
Jeff
@Chris - You are right..can't believe I did a mistake in that..
Naveen
And yes, I realize I said it looks awesome...that's because it does! I can usually grasp what the code is doing, despite not understanding the commands (ie I still have no understanding of the asterick, or the argc in the main parenthesis)
Jeff
+3  A: 

[EDIT] include support for command line arguments [EDIT] Fixed possible memory leak [EDIT] Fixed a missing reference

#include <fstream>
#include <iostream>

using namespace std;

int main(int argc, char *argv){
    ifstream infh;  // our file stream
    char *buffer;

    for(int c = 1; c < argc; c++){
        infh.open(argv[c]);

        //Error out if the file is not open
        if(!infh){
            cerr << "Could not open file: "<< argv[c] << endl;
            continue;
        }

        //Get the length of the file 
        infh.seekg(0, ios::end);
        int length = infh.tellg();

        //reset the file pointer to the beginning
        is.seekg(0, ios::beg);

        //Create our buffer
        buffer = new char[length];

        // Read the entire file into the buffer
        infd.read(buffer, length);

        //Cycle through the buffer, outputting every other char
        for(int i=0; i < length; i+= 2){
            cout << buffer[i]; 
        }
        infh.close();
    }
    //Clean up
    delete[] buffer;
    return 0;
}

Should do the trick. If the file is extremely large, you probably shouldn't load the entire file into the buffer.

Scott
Looks awesome! Thank you! :D But that still relies on the file being in the same directory, what if the file is in another directory?
Jeff
Now we have support for command line arguments and reading multiple files. such asfoobar.exe myfile.txt myfile1.txt myfile2.txt
Scott
I think, while yes indeed awesome, this may be beyond what I've learned in my class so far. I say that because while it does indeed look awesome and I can make out it's processes (a bit), I don't understand alot of it. I'm not really quite sure where to start pointing at my lack of understanding..
Jeff
I do understand the infh.seekg (looks for the length of the file, starting at the '0'th spot and going till the end of file (ios::end)), but things like buffer = new char[length] or the use of argc and *argc in the main parenthesis...yea not so much sadly.
Jeff
The delete[] buffer should be inside the outer for loop.
jmucchiello
+1  A: 

I figured it out! To be honest no one answer helped, it was a combination of the three, plus the comments from them. Thank you all very much! I've attached the code I used, as well as a copy of the document. The program reads in every character, then spits out the deciphered text. (IE: 1h.e0l/lqo is hello) The next step (extra credit) is to set it up so the user inputs how many characters to skip before reading in the next character to input.

This step I intend to do on my own, but again, thank you very much for all the assistance everyone! Proving once again how awesome this site is, one line of code at a time!

EDIT:: Code adjusted to accept user input, as well as allow for multiple uses without recompiling (I realize it looks like a huge sloppy mess, but that's why it's EXTREMELY commented...because in my mind it looks nice and neat)

#include<iostream>
#include<fstream>   //used for reading/writing to files, ifstream could have been used, but used fstream for that 'just in case' feeling.
#include<string>    //needed for the filename.
#include<stdio.h>   //for goto statement

using namespace std;

int main()
{
    program:
    char choice;  //lets user choose whether to do another document or not.
    char letter;  //used to track each character in the document.
    int x = 1;    //first counter for tracking correct letter.
    int y = 1;   //second counter (1 is used instead of 0 for ease of reading, 1 being the "first character").
    int z;    //third counter (used as a check to see if the first two counters are equal).
    string filename; //allows for user to input the filename they wish to use.
    cout << "Please make sure the document is in the same file as the program, thank you!" << endl << "Please input document name: " ;
    cin >> filename; //getline(cin, filename);
    cout << endl;
    cout << "'Every nth character is good', what number is n?: ";
    cin >> z;   //user inputs the number at which the character is good. IE: every 5th character is good, they would input 5.
    cout << endl;
    z = z - 1;  //by subtracting 1, you now have the number of characters you will be skipping, the one after those is the letter you want.
    ifstream infile(filename.c_str()); //gets the filename provided, see below for incorrect input.
    if(infile.is_open()) //checks to see if the file is opened.
    {
     while(!infile.eof())    //continues looping until the end of the file.
     { 
       infile.get(letter);  //gets the letters in the order that that they are in the file.
       if (x == y)          //checks to see if the counters match...
       {
        x++;             //...if they do, adds 1 to the x counter.
       }
       else
       {
        if((x - y) == z)   //for every nth character that is good, x - y = nth - 1.
        {
         cout << letter;   //...if they don't, that means that character is one you want, so it prints that character.
         y = x;     //sets both counters equal to restart the process of counting.
        }
        else      //only used when more than every other letter is garbage, continues adding 1 to the first
        {       //counter until the first and second counters are equal.
         x++;
        }
       }
     }
     cout << endl << "Decryption complete...if unreadable, please check to see if your input key was correct then try again." << endl;
     infile.close();
     cout << "Do you wish to try again? Please press y then enter if yes (case senstive).";
     cin >> choice;
     if(choice == 'y')
     {
      goto program;
     }
    }
    else  //this prints out and program is skipped in case an incorrect file name is used.
    {
     cout << "Unable to open file, please make sure the filename is correct and that you typed in the extension" << endl;
     cout << "IE:" << "     filename.txt" << endl;
     cout << "You input: " << filename << endl;
     cout << "Do you wish to try again? Please press y then enter if yes (case senstive)." ;
     cin >> choice;
     if(choice == 'y')
     {
      goto program;
     }
    }
    getchar();  //because I use visual C++ express.
}

EDIT:::

I tried inserting the text, but I couldn't get it to come out right, it kept treating some of the characters like coding (ie an apostrophe apparently is the equivalent of the bold command), but you could just try putting in "0h1e.l9lao" without the parenthesis into a .txt and it should give the same outcome.

Thanks again everyone for the help!

Jeff
I recommend doing this without the "goto" statement. Use of "goto" is almost never necessary, and makes programs harder to read, debug, and maintain. It is a good exercise to figure out how to use another loop instead of a goto here. (You may want to use the "break" or "continue" keywords)
Colin
I decided against the goto statement a few hours after posting, made it a while loop instead, looks much cleaner without it, thank you for the tip!
Jeff
Never use "!eof" as the condition for a loop when reading from an istream because, in case of an error, eof may never be reached, putting your program in an endless loop. Moreover, the stream's state should be checked after the input operation, and before using its results. In the case of your program, this means that `while(!infile.eof()){ infile.get(letter);...` should be replaced with `while(infile.get(letter)){...`.
Éric Malenfant
+1  A: 

Although your queston has been answered, two little tips: 1) Instead of counting x and y to see if you are on an odd or even character you can do the following:

int count=0;
while(!infile.eof())
{       
    infile.get(letter);
    count++;
    if(count%2==0)
    {
        cout<<letter;
    }
}

% essentially means 'remainder when divided by,' so 11%3 is two. in the above it gives odd or even.

2) Assuming you only need to run it on windows:

system("pause");

will make the window stay open when its finished running so you don't need the last getchar() call (you may have to #include<Windows.h> for that to work).

DaedalusFall
that system pause command is awesome, sadly though, while I may be using visual C++ express, the teacher is using buffy (or whatever he calls it, it's some form of linux in any case), so I don't think I can use anything truly windows based in the program.
Jeff
However, that remainder 2 thing is pretty neat, I'll keep that in mind! I've been tweaking the program, at this point I have it set where you can do multiple files without recompiling, now currently working on making it not only decrypt, but encrypt as well, that's gonna be the tricky part I think!
Jeff
Ahhh, just had a random thought, I don't think the remainder would work. While my example showed every other character, that's only one part, it has to be able to work for different numbers (IE every 537th letter is good), current setup allows me to adjust, I don't think that one does, or does it?
Jeff
Yes, if you wanted every 537th letter you would want if(count%537==0){cout<<letter;}; (or you can use count%interval where interval is an int read from cin for example). This would print out chars 0,537,1074...
DaedalusFall