Cush
Cush

Reputation: 67

How do I read a string from a text file and stop at a certain character? C++

I'm decrypting an encrypted file that was made with my encryption program. I'll try and explain the whole scenario so everyone gets what exactly my program is doing.

My encryption program had the user input a string and then shifted the characters to the right by a random number between 8 and 15. It then sent the encrypted file to a destination called secret.dat

What it read in the encrypted file was

10rovvys1237123&5#10

It tells you first what number the characters were shifted over as (10), then the encrypted string (hello 123 123), and the spaces used the character prior to the space and shited that character over by 4 (the 'y' and the '7'), and the '&' character shows when the encryption is terminated. After the '&' character there are numbers seperated by the '#' character showing where the spaces are located.

So first with the decryption program I used ifstream to open the file and read an integer to use as my shift to the left to encrypt it and then is where I run into the problem. I can't figure out how to read the characters into a string up to the '&' character so that I can shift those characters back to normal. I found one way to get them but I can't figure out how to put them into a string.

This is what I did that I was able to grab the characters up to the '&' character:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main()
{
    //Declare Variables
    int shift;
    char character;
    char endOfFile = '&';
    ifstream inData;

    //Open file
    inData.open("secret.dat");

    //Begin program
    inData >> shift;
    cout << shift << endl;

    inData.get(character);

    while(character != endOfFile)
    {
        if (character >= 'a' && character <= 'z')
        {
            character = ((character - 'a' - shift) % 26) + 'a';
        }
        if (character >= '0' && character <= '9')
        {
            character = ((character - '0' - shift) & 10) + '0';
        }
        cout << character;
        inData.get(character);
    }
    return 0;
}

My second problem is my formula that I'm using to shift the characters back as it doesn't seem to work properly as I figured all I needed to do was change my encrypt formula that just added shift to subtracting shift. But that's a different problem, main problem is grabbing the characters and putting them into a string so that I can use them later. Right now the loop is just grabbing each character and putting it out but I can't access the characters besides the first one outside of the loop.

Any help would be greatly appreciated as I can't seem to find my solution anywhere else.

Upvotes: 0

Views: 2526

Answers (2)

Julian
Julian

Reputation: 1736

You can use std::getline to read from an input stream into a string, up to a certain character/delimiter :)

// Assume your inData stream contains "10rovvys1237123&5#10"

int shift = 0;
std::string encrypted;

inData >> shift;
std::getline(inData, encrypted, '&');

// Here
// shift = 10
// encrypted = "rovvys1237123"

As for shifting back, as long as you're simply adding an integer value to each character to shift your string, you should be able to do the opposite by looping through each character and subtracting the same value. Something like:

std::string decrypted = "";
for (auto& character : encrypted) {
    decrypted += character - shift;
}

Upvotes: 1

NGI
NGI

Reputation: 902

I shall introduce another method based on boost::spirit as it will answer most of your needs. I discovered spirit while searching a parser. The documentation is good and comprehensive but there may exist a cost to get used to ( I am just beginning so receive my answer as a feasability study and not as a real reference of what can be done with spirit )

The action is to make a call to the function parse_encr.

int main()
{
    int my_shift;
    string encrypted_str("10rovvys1237123&5#10");

    bool success = parse_encr(encrypted_str.begin(),encrypted_str.end(),my_shift);
    cout    << "Parse Succeed ? "
            << (success ? "Yes":"no")
            << endl << "shift:" << endl
            << (success ? my_shift:0)
            << endl;

    cout << "Encrypted data are: " << str_buf.str() << endl;
    //...
}

Result is

Parse Succeed ? Yes
shift:
10
Encrypted data are: rovvys1237123

How to achieve this ? With Spirit ( here with single call to qi::phrase_parse ):

template <typename Iterator>
bool parse_encr(Iterator first, Iterator last,int & shift)
{
    bool r = qi::phrase_parse(
            first,
            last,
            qi::int_[boost::phoenix::ref(shift) = _1] >>
            qi::lexeme[ *qi::char_("a-zA-Z_ 0-9")[&cat_char<char>] ] >>
            qi::char_('&') >>
            qi::int_ >>
            qi::char_('#') >>
            qi::int_,
            qi::space
        );
    if (first != last) // fail if we did not get a full match
        return false;
    return r;
}

I will let you discover spirit by yourself but quickly explained:

You specify the fact that you are waiting for a int by employing the rule qi::int_. If you are waiting for the character '&' you use qi::char('&'). You do not need qi::lexeme but I let it for this example ( turn off whitespace skipping )

The star * means one or more...

If some rule is successfull you may perform an action. The action is comprised between square brackets [ ]

For example the first one call another aspect of spirit call phoenix that allow you to bind to a parameter. The second one is the call to the function cat_char

stringstream str_buf;

template <typename T>void cat_char(T & item)
{
    str_buf << item;
}

This method is much more powerfull because you check your syntax will getting the different fields of it.

The bunch of includes... so that you can replay the code on your side ( you will need to install boost or boost headers at least... )

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>


using namespace std;
using namespace boost::spirit;

Upvotes: 0

Related Questions