I have the following code to convert an encrypted ciphertext to a readable hexadecimal format:
std::string convertToReadable(std::string ciphertext)
{
std::stringstream outText;
for(unsigned int i = 0; i < ciphertext.size(); i++ )
outText << std::hex << std::setw(2) << std::setfill('0') << (0xFF & static_cast<byte>(ciphertext[i])) << ":";
return outText.str();
}
The readable result of this function is something as:
56:5e:8b:a8:04:93:e2:f1:5c:20:8b:fd:f5:b7:22:0b:82:42:46:58:9b:d4:c1:8e:ac:62:85:04:ff:7f:c6:d3:
Now I need to do the way back, converting the readable format to the original ciphertext in order to decrypt it:
std::string convertFromReadable(std::string text)
{
std::istringstream cipherStream;
for(unsigned int i = 0; i < text.size(); i++ )
{
if (text.substr(i, 1) == ":")
continue;
std::string str = text.substr(i, 2);
std::istringstream buffer(str);
int value;
buffer >> std::hex >> value;
cipherStream << value;
}
return cipherStream.str();
}
This is not absolutely working, as I´m getting the wrong string back.
How can I fix the convertFromReadable() so that I can have the original ciphertext back ?
Thanks for helping
Here are problems that you should fix before debugging this any further:
cipherStream should be ostringstream, not istringstream
The for loop should stop two characters before the end. Otherwise your substr is going to fail. Make the loop condition i+2 < text.size()
When you read two characters from the input, you need to advance i by two, i.e. add i++ after the std::string str = text.substr(i, 2); line.
Since you want character output, add a cast to char when writing the data to cipherStream, i.e. cipherStream << (char)value
Good you got your code working. Just thought I'd illustrate a slightly simpler, more direct approach using streams without the fiddly index tracking and substr extraction:
std::string convertFromReadable(const std::string& text)
{
std::istringstream iss(text);
std::ostringstream cipherStream;
int n;
while (iss >> std::hex >> n)
{
cipherStream << (char)n;
// if there's another character it better be ':'
char c;
if (iss >> c && c != ':')
throw std::runtime_error("invalid character in cipher");
}
return cipherStream.str();
}
Note that after the last hex value, if there's no colon the if (iss >> c... test will evaluate false as will the while (iss >> ... test, fallingt through to return.
Related
I'm trying to read a string until a ',' character is reached and store what's been read in a new string.
e.g. "5,6"
// Initialise variables.
string coorPair, xCoor, yCoor
// Ask to enter coordinates.
cout << "Input coordinates: ";
// Store coordinates.
cin >> coorPair
// Break coordinates into x and y variables and convert to integers.
// ?
I also need to store the y value in a separate variable.
What is the best way to do this in C++?
Also, is the best way to validate the input to convert to integers and test the values range?
If you have only one comma seperator in string, you can just find where comma first occurs in input and substring input with found position.
Try following:
std::size_t pos = coorPair.find_first_of(","); //Find position of ','
xCoor = coorPair.substr(0, pos); //Substring x position string
yCoor = coorPair.substr(pos + 1); //Substring y position string
int xCoorInt = std::stoi(xCoor); //Convert x pos string to int
int yCoorInt = std::stoi(yCoor); //Convert y pos string to int
The easiest way to do this is to just let operator>> do all the work for you:
int xCoor, yCoor;
char ch;
cout << "Input coordinates: ";
if (cin >> xCoor >> ch >> yCoor)
{
// use coordinates as needed ...
}
else
{
// bad input...
}
You can do this by specifying you delimiter and parsing the string
std::string delimiter = ",";
size_t pos = 0;
std::string token;
while ((pos = coorPair.find(delimiter)) != std::string::npos) {
token = coorPair.substr(0, pos);
std::cout << token << std::endl;
coorPair.erase(0, pos + delimiter.length());
}
std::cout << coorPair << endl;
The last token example in {5,6} the {6} would be in the coorPair.
Another way would be to use std::getline as pointed in the comments:
std::string token;
while (std::getline(coorPair, token, ','))
{
std::cout << token << std::endl;
}
http://www.cplusplus.com/reference/string/string/getline/
I would recommend using getline().
Below is a small example of how I use it. It takes the input from a stream, so you can either use an ifstream as an input, or do what I did below and convert the string to a stream.
// input data
std::string data("4,5,6,7,8,9");
// convert string "data" to a stream
std::istringstream d(data);
// output string of getline()
std::string val;
std::string x;
std::string y;
bool isX = true;
char delim = ',';
// this will read everything up to delim
while (getline(d, val, delim)) {
if (isX) {
x = val;
}
else {
y = val;
}
// alternate between assigning to X and assigning to Y
isX = !isX;
}
How to split the string to two-parts after I assign the operation to math operator? For example 4567*6789 I want to split string into three part
First:4567 Operation:* Second:6789
Input is from textfile
char operation;
while (getline(ifs, line)){
stringstream ss(line.c_str());
char str;
//get string from stringstream
//delimiter here + - * / to split string to two part
while (ss >> str) {
if (ispunct(str)) {
operation = str;
}
}
}
Maybe, just maybe, by thinking this out, we can come up with a solution.
We know that operator>> will stop processing when encounter a character that is not a digit. So we can use this fact.
int multiplier = 0;
ss >> multiplier;
The next characters are not digits, so they could be an operator character.
What happens if we read in a character:
char operation = '?';
ss >> operation;
Oh, I forgot to mention that the operator>> will skip spaces by default.
Lastly, we can input the second number:
int multiplicand = 0;
ss >> multiplicand;
To confirm, let's print out what we have read in:
std::cout << "First Number: " << multiplier << "\n";
std::cout << "Operation : " << operation << "\n";
std::cout << "Second Number: " << multiplicand << "\n";
Using a debugger here will help show what is happening, as each statement is executed, one at at time.
Edit 1: More complicated
You can always get more complicated and use a parser, lexer or write your own. A good method of implementation is to use a state machine.
For example, you would read a single character, then decide what to do with it depending on the state. For example, if the character is a digit, you may want to build a number. For a character (other than white space), convert it to a token and store it somewhere.
There are parse trees and other data structures which can ease the operation of parsing. There are parsing libraries out there too, such as boost::spirit, yacc, bison, flex and lex.
One way is:
char opr;
int firstNumber, SecondNumber;
ss>>firstNumber>>opr>>SecondNumber;
instead of:
while (ss >> str) {
if (ispunct(str)) {
operation = str;
}
}
Or using regex for complex expersions. Here is an example of using regex in math expersions.
If you have a string at hand, you could simply split the string into left and right at the operator position as follows:
char* linePtr = strdup("4567*6789"); // strdup to preserve original value
char* op = strpbrk(linePtr, "+-*");
if (op) {
string opStr(op,1);
*op = 0x0;
string lhs(linePtr);
string rhs(op+1);
cout << lhs << " " << opStr << " " << rhs;
}
A simple solution would be to use sscanf:
int left, right;
char o;
if (sscanf("4567*6789", "%d%c%d", &left, &o, &right) == 3) {
// scan valid...
cout << left << " " << o << " " << right;
}
My proposual is to create to functions:
std::size_t delimiter_pos(const std::string line)
{
std::size_t found = std::string::npos;
(found = line.find('+')) != std::string::npos ||
(found = line.find('-')) != std::string::npos ||
(found = line.find('*')) != std::string::npos ||
(found = line.find('/')) != std::string::npos;
return found;
}
And second function that calculate operands:
void parse(const std::string line)
{
std::string line;
std::size_t pos = delimiter_pos(line);
if (pos != std::string::npos)
{
std::string first = line.substr(0, pos);
char operation = line[pos];
std::string second = line.substr(pos + 1, line.size() - (pos + 1));
}
}
I hope my examples helped you
Hi I'm trying to take a c-string from a user, input it into a queue, parse the data with a single space depending on its contents, and output the kind of data it is (int, float, word NOT string).
E.g. Bobby Joe is 12 in 3.5 months \n
Word: Bobby
Word: Joe
Word: is
Integer: 12
Word: in
Float: 3.5
Word: months
Here's my code so far:
int main()
{
const int maxSize = 100;
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
std::cin.getline(cstring, maxSize, '\n');
//Keyboard Buffer Function
buffer::keyboard_parser(cstring);
return EXIT_SUCCESS;
}
Function:
#include <queue>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <vector>
namespace buffer
{
std::string keyboard_parser(char* input)
{
//Declare Queue
std::queue<std::string> myQueue;
//Declare String
std::string str;
//Declare iStringStream
std::istringstream isstr(input);
//While Loop to Read iStringStream to Queue
while(isstr >> str)
{
//Push onto Queue
myQueue.push(str);
std::string foundDataType = " ";
//Determine if Int, Float, or Word
for(int index = 0; index < str.length(); index++)
{
if(str[index] >= '0' && str[index] <= '9')
{
foundDataType = "Integer";
}
else if(str[index] >= '0' && str[index] <= '9' || str[index] == '.')
{
foundDataType = "Float";
break;
}
else if(!(str[index] >= '0' && str[index] <= '9'))
{
foundDataType = "Word";
}
}
std::cout << "\n" << foundDataType << ": " << myQueue.front();
std::cout << "\n";
//Pop Off of Queue
myQueue.pop();
}
}
}
Right now with this code, it doesn't hit the cout statement, it dumps the core.
I've read about using the find member function and the substr member function, but I'm unsure of how exactly I need to implement it.
Note: This is homework.
Thanks in advance!
UPDATE: Okay everything seems to work! Fixed the float and integer issue with a break statement. Thanks to everyone for all the help!
Your queue is sensible: it contains std::strings. Unfortunately, each of those is initialised by you passing cstring in without any length information and, since you certainly aren't null-terminating the C-strings (in fact, you're going one-off-the-end of each one), that's seriously asking for trouble.
Read directly into a std::string.
std::istreams are very useful for parsing text in C++... often with an initial read of a line from a string, then further parsing from a std::istringstream constructed with the line content.
const char* token_type(const std::string& token)
{
// if I was really doing this, I'd use templates to avoid near-identical code
// but this is an easier-to-understand starting point...
{
std::istringstream iss(token);
int i;
char c;
if (iss >> i && !(iss >> c)) return "Integer";
}
{
std::istringstream iss(token);
float f;
char c; // used to check there's no trailing characters that aren't part
// of the float value... e.g. "1Q" is not a float (rather, "word").
if (iss >> f && !(iss >> c)) return "Float";
}
return "Word";
}
const int maxSize = 100; // Standard C++ won't let you create an array unless const
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
if (std::cin.getline(cstring, maxSize, '\n'))
{
std::istringstream iss(cstring);
std::string token;
while (iss >> token) // by default, streaming into std::string takes a space-...
token_queue.push(token); // ...separated word at a time
for (token_queue::const_iterator i = token_queue.begin();
i != token_queue.end(); ++i)
std::cout << token_type(*i) << ": " << *i << '\n';
}
Given data format as "int,int,...,int,string,int", is it possible to use stringstream (only) to properly decode the fields?
[Code]
int main(int c, char** v)
{
std::string line = "0,1,2,3,4,5,CT_O,6";
char delimiter[7];
int id, ag, lid, cid, fid, did, j = -12345;
char dcontact[4]; // <- The size of <string-field> is known and fixed
std::stringstream ssline(line);
ssline >> id >> delimiter[0]
>> ag >> delimiter[1]
>> lid >> delimiter[2]
>> cid >> delimiter[3]
>> fid >> delimiter[4]
>> did >> delimiter[5] // <- should I do something here?
>> dcontact >> delimiter[6]
>> j;
std::cout << id << ":" << ag << ":" << lid << ":" << cid << ":" << fid << ":" << did << ":";
std::cout << dcontact << "\n";
}
[Output] 0:1:2:3:4:5:CT_6,0:-45689, the bolded part shows the stringstream failed to read 4 char only to dcontact. dcontact actually hold more than 4 chars, leaving j with garbage data.
Yes, there is no specific overload of operator >> (istream&, char[N]) for N and there is for char* so it sees that as the best match. The overload for char* reads to the next whitespace character so it doesn't stop at the comma.
You could wrap your dcontact in a struct and have a specific overload to read into your struct. Else you could use read, albeit it breaks your lovely chain of >> operators.
ssline.read( dcontact, 4 );
will work at that point.
To read up to a delimiter, incidentally, you can use getline. (get will also work but getline free-function writing to a std::string will mean you don't have to guess the length).
(Note that other people have specified to use get rather than read, but this will fail in your case as you do not have an extra byte at the end of your dcontact array for a null terminator. IF you want dcontact to be null-terminated then make it 5 characters and use 'get` and the null will be appended for you).
Slightly more robust (handles the ',' delimiter correctly):
template <char D>
std::istream& delim(std::istream& in)
{
char c;
if (in >> c && c != D) in.setstate(std::ios_base::failbit);
return in;
}
int main()
{
std::string line = "0,1,2,3,4,5,CT_O,6";
int id, ag, lid, cid, fid, did, j = -12345;
char dcontact[5]; // <- The size of <string-field> is known and fixed
std::stringstream ssline(line);
(ssline >> id >> delim<','>
>> ag >> delim<','>
>> lid >> delim<','>
>> cid >> delim<','>
>> fid >> delim<','>
>> did >> delim<','> >> std::ws
).get(dcontact, 5, ',') >> delim<','>
>> j;
std::cout << id << ":" << ag << ":" << lid << ":"
<< cid << ":" << fid << ":" << did << ":";
<< dcontact << "\n";
}
The problem is that the >> operator for a string
(std::string or a C style string) actually implements the
semantics for a word, with a particular definition of word. The
decision is arbitrary (I would have made it a line), but since
a string can represent many different things, they had to choose
something.
The solution, in general, is not to use >> on a string, ever.
Define the class you want (here, probably something like
Symbol), and define an operator >> for it which respects its
semantics. You're code will be a lot clearer for it, and you
can add various invarant controls as appropriate. If you know
that the field is always exactly four characters, you can do
something simple like:
class DContactSymbol
{
char myName[ 4 ];
public:
// ...
friend std::istream&
operator>>( std::istream& source, DContactSymbol& dest );
// ...
};
std::istream&
operator>>( std::istream& source, DContactSymbol& dest )
{
std::sentry guard( source );
if ( source ) {
std::string tmp;
std::streambuf* sb = source.rdbuf();
int ch = sb->sgetc();
while ( source && (isalnum( ch ) || ch == '_') ) {
tmp += static_cast< char >( ch );
if ( tmp.size() > sizeof( dest.myName ) ) {
source.setstate( std::ios_base::failbit );
}
}
if ( ch == source::traits_type::eof() ) {
source.setstate( std::ios_base::eofbit );
}
if ( tmp.size() != sizeof( dest.myName ) ) {
source.setstate( std::ios_base::failbit );
}
if ( source ) {
tmp.copy( dest.myName, sizeof( dest.myName ) );
}
}
return source;
}
(Note that unlike some of the other suggestions, for example
using std::istream::read, this one maintains all of the usual
conventions, like skipping leading white space dependent on the
skipws flag.)
Of course, if you can't guarantee 100% that the symbol will
always be 4 characters, you should use std::string for it, and
modify the >> operator accordingly.
And BTW, you seem to want to read four characters into
dcontact, although it's only large enough for three (since
>> will insert a terminating '\0'). If you read any more
than three into it, you have undefined behavior.
try this
int main(int c, char** v) {
string line = "0,1,2,3,4,5,CT_O,6";
char delimiter[7];
int id, ag, lid, cid, fid, did, j = -12345;
char dcontact[5]; // <- The size of <string-field> is known and fixed
stringstream ssline(line);
ssline >> id >> delimiter[0]
>> ag >> delimiter[1]
>> lid >> delimiter[2]
>> cid >> delimiter[3]
>> fid >> delimiter[4]
>> did >> delimiter[5];
ssline.get(dcontact, 5);
ssline >> delimiter[6]
>> j;
std::cout << id << ":" << ag << ":" << lid << ":" << cid << ":" << fid << ":" << did << ":";
std::cout << dcontact << "\n" << j;
}
Since the length of the string is known you can use std::setw(4), as in
ssline >> std::setw(4) >> dcontact >> delimiter[6];
I've an input formatted like this:
integer multi-word-string integer
I know the maximum lenght of multi-word-string, however I don't know how many words it contains. How can I read it ?
I'd read the line first and convert the first and last word to integers. Loosely:
std::string line;
std::getline(infile, line);
size_t ofs_front = line.find(' ');
size_t ofs_back = line.rfind(' ');
int front = std::strtol(line.substr(0, ofs_front).c_str(), NULL, 0);
int back = std::strtol(line.substr(ofs_back).c_str(), NULL, 0);
std::string text = line.substr(ofs_front, ofs_back - ofs_front);
You'll have to do some modifications to get rid of spaces (e.g. increment the offsets to gobble up all spaces), and you should add lots of error checking.
If you want to normalize all the interior spaces inside the text, then there's another solution using string streams:
std::vector<std::string> tokens;
{
std::istringstream iss(line);
std::string token;
while (iss >> token) tokens.push_back(token);
}
// process tokens.front() and tokens.back() for the integers, as above
std::string text = tokens[1];
for (std::size_t i = 2; i + 1 < tokens.size(); ++i) text += " " + tokens[i];
Read the first integer. Jump to back of the string and skip digits. Then read an int from this point. The part in the middle is the string. May not be 100% correct but:
char buf[256], *t = buf, *p, str[256];
fread(buf, 1, 256, file);
int s,e;
t += sscanf(buf, "%d", &s);
*p = buf + strlen(buf);
while (isdigit(*p)) p--;
sscanf(p, "%d", &e);
strncpy(str, p, p - t);
This is not as efficient as #KerrekSB's solution, but another way to do this is to extract the first integer, then loop through the rest of the string until you find the second integer.
#include <iostream>
#include <sstream>
int main()
{
std::istringstream ss( "100 4 words to discard 200" );
int first, second;
char buf[1000] = {0};
if( !( ss >> first ) ) {
std::cout << "failed to read first int\n";
return 1;
}
while( !( ss >> second ) || !ss.eof() ) {
if( ss.eof() ) {
std::cout << "failed to read second int\n";
return 1;
}
ss.clear();
if( ss.getline( buf, 1000, ' ' ).bad() ) {
std::cout << "error extracting word\n";
return 1;
}
}
std::cout << "first = " << first << "\n";
std::cout << "second = " << second << "\n";
return 0;
}