How can I separate numbers and letters in a C++ String? - c++

I'm having several input strings containing numbers and letters. Sometimes the space is missing. I would like to add an additional Space each time the string changes from numbers to letters or from letters to numbers.
Example inputs:
"30EinsteinStreet"
"548 Roe Drive5500 TestCity"
"44B SaarlouisDrive1234Testtown"
they should become:
"30 EinsteinStreet"
"548 Roe Drive 5500 TestCity"
"44 B SaarlouisDrive 1234 Testtown"
My existing function is not working and I think far to complex. Can anyone provide an easy solution? Preferably using modern C++11 classes but no Boost. Also I'm using GCC so all the regex stuff doesn't work for me.
Thanks
Here is my existing method:
inline string separateAlphanumChunks(const std::string& s)
{
string ret = "";
const int sl = s.length();
//int inserts = 0;
if (sl<=4)
return s;
for (int i=0 ; i< sl ; i++)
{
// cerr << "separateAlphanumChunks: '" << ret << "'" <<endl;
// check if index would go out of range
if (i+4 > sl)
{
ret += s.substr (i,sl-i);
//TODO add the remain to ret
break;
}
// seperate chars
const char c0 = s[i+0];
const char c1 = s[i+1];
// check if 0 and 1 are the same class
const bool c0c = isCharAnInt (c0);
const bool c1c = isCharAnInt (c1);
bool class0 = false;
if (c0c == c1c)
{
class0 = c0c;
}
else
{
ret += c0;
// cerr << "cont1: '" << c0 << "'" <<endl;
continue;
}
// seperate chars
const char c2 = s[i+2];
const char c3 = s[i+3];
// check if 2 and 3 are the same class
const bool c2c = isCharAnInt (c2);
const bool c3c = isCharAnInt (c3);
bool class2 = false;
if (c2c == c3c)
{
class2 = c2c;
}
else
{
ret += c0;
// cerr << "cont2: '" << c0 << "'" <<endl;
continue;
}
// check if the 2 classes are different
if (class0 != class2)
{
// split!
ret += c0+(c1+string(" ")+c2)+c3;
//inserts++;
i+=3;
}
else
{
ret += c0;
// cerr << "cont3: '" << c0 << "'" <<endl;
continue;
}
}
// remove double spaces
//replaceStringInPlace(ret, " "," ");
//cerr << "separateAlphanumChunks: '" << ret << "'" <<endl;
return ret;
}
inline bool isCharAnInt (char c)
{
//TODO might be able to use isdigit() here
int i = c - '0';
return ((i>=0) && (i<=9));
}

I saw various complex answers, and this is the reason to give another answer too.
The answer of your problem is exactly in the problem statement:
"add an additional Space each time the string changes from numbers to letters or from letters to numbers."
So here is exactly what you want ( I used some code from a previous answer ) the compilation should be done using the flag -std=c++11
#include <string>
#include <iostream>
using namespace std;
enum charTypeT{ other, alpha, digit};
charTypeT charType(char c){
if(isdigit(c))return digit;
if(isalpha(c))return alpha;
return other;
}
string separateThem(string inString){
string oString = "";charTypeT st=other;
for(auto c:inString){
if( (st==alpha && charType(c)==digit) || (st==digit && charType(c)==alpha) )
oString.push_back(' ');
oString.push_back(c);st=charType(c);
}
return oString;
}
int main(){
string str1 = "30EinsteinStreet";
string str2 = "548 Roe Drive5500 TestCity";
string str3 = "44B SaarlouisDrive1234Testtown";
cout << separateThem(str1) << endl;
cout << separateThem(str2) << endl;
cout << separateThem(str3) << endl;
}

I think what you are looking for and what Ajay is hinting at is a finite-state machine to parse strings.
Although this is not a C++11 solution, and you might find more elegant solutions by means of regex, I provided the code sample below.
#include <iostream>
#include <sstream>
bool isDigit(const char c)
{
bool res = true;
switch (c)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
default:
res = false;
break;
}
return res;
}
std::string separateNumbers(const std::string& inputString)
{
const size_t N = inputString.length();
std::ostringstream os;
bool readDigit = false;
for (size_t i = 0; i < N; ++i)
{
if (isDigit(inputString[i]))
{
if ((i > 0) && (i < N) && (! readDigit))
{
if (inputString[i] != ' ')
os << ' ';
}
readDigit = true;
}
else
{
if ((i > 0) && (i < N) && (readDigit))
{
if (inputString[i] != ' ')
os << ' ';
}
readDigit = false;
}
os << inputString[i];
}
return os.str();
}
int main(int argc, char** argv)
{
std::string strings[3] = {
"30EinsteinStreet",
"548 Roe Drive5500 TestCity",
"44B SaarlouisDrive1234Testtown"
};
for (int i = 0; i < 3; ++i)
{
std::cout << "input #" << i << ": " << strings[i] << std::endl;
std::cout << "output #" << i << ": " << separateNumbers(strings[i]) << std::endl;
std::cout << std::endl;
}
return 0;
}

What I would suggest is to go trough a iteration trough string elements.
Something like that will help:
#include <string>
#include <iostream>
using namespace std;
string separateThem(string inString){
string numbers = "1234567890";
string oString = "";
int i;
for(i=0; i<inString.size()-1; i++){
if ((numbers.find(inString[i]) != string::npos) && (numbers.find(inString[i+1]) == string::npos) && !isspace(inString[i+1])){
oString += inString.substr(i,1) + " ";
}
else if ((numbers.find(inString[i]) == string::npos) && (numbers.find(inString[i+1]) != string::npos) && !isspace(inString[i+1])){
oString += inString.substr(i,1) + " ";
}
else oString += inString.substr(i,1);
}
oString += inString.substr(i,1);
return oString;
}
int main(){
string str1 = "30EinsteinStreet";
string str2 = "548 Roe Drive5500 TestCity";
string str3 = "44B SaarlouisDrive1234Testtown";
cout << separateThem(str1) << endl;
cout << separateThem(str2) << endl;
cout << separateThem(str3) << endl;
}
If you execute this the output will be:
30 EinsteinStreet
548 Roe Drive 5500 TestCity
44 B SaarlouisDrive 1234 Testtown
Hope this helps :)

Here is my five cents.
#include <iostream>
#include <string>
#include <cctype>
std::string SeparateAlphanumChunks( const std::string &s )
{
std::string::size_type n = 0;
bool ctype = std::isdigit( s[0] );
for ( char c : s )
{
if ( !ctype != !std::isdigit( c ) )
{
ctype = std::isdigit( c );
if ( !isblank( c ) ) ++n;
}
}
std::string t;
t.reserve( s.size() + n );
ctype = std::isdigit( s[0] );
for ( char c : s )
{
if ( !ctype != !std::isdigit( c ) )
{
ctype = std::isdigit( c );
if ( !isblank( c ) ) t.push_back( ' ');
}
t.push_back( c );
}
return t;
}
int main()
{
for ( const std::string &s : { "30EinsteinStreet",
"548 Roe Drive5500 TestCity",
"44B SaarlouisDrive1234Testtown"
} )
{
std::cout << SeparateAlphanumChunks( s ) << std::endl;
}
return 0;
}
The output is
30 EinsteinStreet
548 Roe Drive 5500 TestCity
44 B SaarlouisDrive 1234 Testtown
You also may change the string "in place". For example
#include <iostream>
#include <string>
#include <cctype>
std::string & SeparateAlphanumChunks( std::string &s )
{
std::string::size_type n = 0;
bool ctype = std::isdigit( s[0] );
for ( char c : s )
{
if ( !ctype != !std::isdigit( c ) )
{
ctype = std::isdigit( c );
if ( !isblank( c ) ) ++n;
}
}
s.reserve( s.size() + n );
ctype = std::isdigit( s[0] );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
if ( !ctype != !std::isdigit( s[i] ) )
{
ctype = std::isdigit( s[i] );
if ( !isblank( s[i] ) )
{
s.insert( i, 1, ' ' );
}
}
}
return s;
}
int main()
{
for ( std::string s : { "30EinsteinStreet",
"548 Roe Drive5500 TestCity",
"44B SaarlouisDrive1234Testtown"
} )
{
std::cout << SeparateAlphanumChunks( s ) << std::endl;
}
return 0;
}

Upgrade to GCC 4.9 (whose first release was back in April) and use a simple regex:
#include <regex>
#include <iostream>
std::string fix(const std::string& in)
{
return std::regex_replace(
in,
std::regex("(?:([a-zA-Z])([0-9]))|(?:([0-9])([a-zA-Z]))"),
"\\1\\3 \\2\\4",
std::regex_constants::format_sed
);
}
int main()
{
const std::string in[] = {
"30EinsteinStreet",
"548 Roe Drive5500 TestCity",
"44B SaarlouisDrive1234Testtown"
};
for (auto el : in)
std::cout << fix(el) << '\n';
}
/*
"30 EinsteinStreet"
"548 Roe Drive 5500 TestCity"
"44 B SaarlouisDrive 1234 Testtown"
*/
(live demo)

I would suggest you to iterator the string as raw-string (i.e. string::c_str()), and generate a new string altogether. This would be my algorithm (not very complete):
For each character, check if it is a digit. If no, just append to new string.
If yes, check if it is first character - if yes, just append to new string.
If the digit is last character, then append to new string.
If digit is falling in between, check if last appended character was space. If no space was there, put a space, and then put digit.
If last inserted character was a digit, and this is also a digit, insert.
However, if last was digit, but this is not a digit (and not a space), then insert a space.
You may need to tweak it further.
What if string is like this:
"enter 144 code here 123 "
?

Related

Infinite loop using string.substr(post,len)

Could you figure out why it keeps looping infinitely in the console? The programmer's supposed to list out each character of a user-inserted string and next to each unique character, in brackets, it's supposed to display the number of times that character occurs in the string... no idea why.
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main () {
string input;
cout << "input string: " , cin >> input;
sort (input.begin() , input.end());
while (!input.empty()) {
int j{1}, i{0};
while (input.at(i) == input.at(i+1)) {
j++;
i++;
}
cout << input.at(i) << " (" << j << "), ";
input.substr(i);
}
return 0;
}
This statement
input.substr(i);
does not change the object input itself.
So, either you will have an infinite loop if for some index i input.at(i) is not equal to input.at(i+1), or you can have an exception out of range because i + 1 can be equal to input.size().
From the description of the member function at
Throws: out_of_range if pos >= size().
The program can be implemented in different ways. For example the following way
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
std::cout << "input string: ";
std::string input;
std::cin >> input;
std::sort( input.begin() , input.end() );
for ( size_t i = 0; i < input.size(); )
{
size_t j = input.find_first_not_of( input[i], i );
if ( j == std::string::npos ) j = i + 1;
if ( i != 0 ) std::cout << ", ";
std::cout << input[i] << " (" << j - i << ")";
i = j;
}
std::cout << '\n';
return 0;
}
The program output is
input string: Hello
H (1), e (1), l (2), o (1)
Or you can use the standard container std::map or std::unordered_map as for example
#include <iostream>
#include <string>
#include <map>
int main()
{
std::cout << "input string: ";
std::string input;
std::cin >> input;
std::map<char, size_t> m;
for ( const auto &c : input )
{
++m[c];
}
bool first = true;
for ( const auto &p : m )
{
if ( !first ) std::cout << ", ";
std::cout << p.first << " (" << p.second << ")";
first = false;
}
std::cout << '\n';
return 0;
}
If you want that characters of the inputted string were output in the order in which they are present in the string then the program can look like
#include <iostream>
#include <string>
#include <map>
int main()
{
std::cout << "input string: ";
std::string input;
std::cin >> input;
auto less = [&input]( const auto &c1, const auto &c2 )
{
return input.find( c1 ) < input.find( c2 );
};
std::map<char, size_t, decltype( less )> m( less );
for ( const auto &c : input )
{
++m[c];
}
bool first = true;
for ( const auto &p : m )
{
if ( !first ) std::cout << ", ";
std::cout << p.first << " (" << p.second << ")";
first = false;
}
std::cout << '\n';
return 0;
}
Or without changing the original string and without using an additional container the program can look the following way.
#include <iostream>
#include <string>
int main()
{
std::cout << "input string: ";
std::string input;
std::cin >> input;
for ( size_t i = 0; i < input.size(); i++ )
{
size_t j = 0;
while ( j != i && input[j] != input[i] ) j++;
if ( j == i )
{
size_t count = 1;
while ( ++j < input.size() )
{
if ( input[j] == input[i] ) ++count;
}
if ( i != 0 ) std::cout << ", ";
std::cout << input[i] << " (" << count << ")";
}
}
std::cout << '\n';
return 0;
}
The program output might look like
input string: elephant
e (2), l (1), p (1), h (1), a (1), n (1), t (1)

Sorting a string and removing characters

#include <iostream>
const int SIZE = 100;
using namespace std;
int main()
{
char *pStr, str[SIZE] = "", newStr[SIZE] = "", ch;
int count = 0, i = 0, j = 0;
cout << "Enter a number of mixed characters: ";
cin.getline(str, SIZE);
pStr = str;
while (*pStr != '\0')
{
if (isalnum(*pStr))
ch = toupper(*pStr);
newStr[i++] = ch;
if (*pStr = ' ')
count++;
pStr++;
}
newStr[i] = '\0';
cout << strlen(str) - strlen(newStr) << " characters were filtered out, "
<< " out of which " << count << " whitespaces were encountered.\n";
int temp;
for (i = 0; i < strlen(newStr) - 1; i++);
{
for (j = i + 1; j < strlen(newStr); j++);
{
if (newStr[j] < newStr[i]) // sorts in alphabetical
{ // and numerical order
temp = newStr[i];
newStr[i] = newStr[j];
newStr[j] = temp;
}
}
}
cout << "New sorted string: " << newStr << endl;
return 0;
}
I have a code here which is supposed to take an input string and print it out in a certain order and remove other characters as well as spaces. Numbers and letters are supposed to be sorted in numerical and alphabetical order. So if you take for instance the input "khff &%/321", the output should be "123FFHK".
However, when I try the code with said input string, the output I get is "KHFFFFFF32". I was hoping for some tips on what parts of the code I need to take a closer look at to solve the problem.
You can simply use this code to sort the string as you would like, then use the erase function to strip out non-alphanumeric characters:
#include <iostream>
#include <algorithm>
#include <string>
int main() {
std::string word = "khff &%/123";
word.erase(std::remove_if(word.begin(), word.end(), [](char ch){ return !::isalnum(ch); }), word.end());
std::sort(word.begin(), word.end());
std::cout << word << '\n';
return 0;
}
I would also like to point out
if (isalnum(*pStr))
ch = toupper(*pStr);
newStr[i++] = ch;
the list line in this is not covered under the if condition,and for every special character read(&,%,/) you would be appending them to your newStr,hence you are getting additional Fs in your output.You must do something like:
if (isalnum(*pStr))
{ ch = toupper(*pStr);
newStr[i++] = ch;
}
which will check whether your character is alnum or not and only append when if condition is satisfied.
Here is another take on how the program could be written to use the standard library:
#include <iostream>
#include <sstream>
#include <algorithm>
#include <string>
#include <cstdio>
#define USER_TYPES_INPUT // comment this line out to use own test stream
#ifdef USER_TYPES_INPUT
auto& my_cin = std::cin;
#else
std::stringstream my_cin{R"(khff&%/32 1)"};
#endif
int main()
{
std::string line;
std::cout << "Enter a number of mixed characters: \n";
std::getline(my_cin, line);
auto original_size = line.size();
auto space_count = std::count_if(line.begin(), line.end(), [](auto c){return ::isblank(c); });
line.erase(std::remove_if(line.begin(), line.end(), [](char c){ return !::isalnum(c); }), line.end());
std::transform(line.begin(), line.end(), line.begin(), [](auto& c){return ::toupper(c); });
std::sort(line.begin(), line.end());
std::cout << original_size - line.size() << " characters were filtered out, out of which "
<< space_count << " whitespace" << (space_count == 1 ? " was" : "s were") << " encountered.\n";
std::cout << "New sorted string: " << line << '\n';
return 0;
}

C++ Search for a unique word and assign it a number

I am looking at various examples of reading an input file and counting the occurrences of a word. Then give it a variable to count.
Lets say we have an input file and you want to look for how many times the word "account" or word "like" shows up and give it the variable "1.2". So when you find the word, count how many times it occurs and then times it by 1.2 .
How would you go about doing this?
EDIT: This is the only way I know how. However, this pre-searches the word versus letting the user search it
#include <iostream>
#include <fstream>
using namespace std;
int main(void)
{
int option=0; //option number
ifstream inputFile;
string filename;
cout << "Welcome\n" << endl;
//Getting the input file
cout << "Enter input data file name:";
cin >> filename;
cout << endl;
fin.open(filename.c_str()); // change to C-string
if (!inputFile) {// makes sure file exist
cout << "Unable to open " << filename << endl;
cin.get();
return 1;
}
do {
cout << "5- Count frequency of the following three words individually: I, like, is" << endl;
cout << "6 - To quit";
cout << endl;
cin >> option;
int iWord = 0;
int likeWord = 0;
int isWord = 0;
if (option == 5) {
string word;
do {
inputFile >> word;
if (word == "I") iWord++;
else if (word== "like") likeWord++;
else if (word == "is") isWord++;
}while (inputFile.good());
cout << "The word I is repeated " << iWord << " times" << endl;
cout << "The word is has been repeated " << isWord << " times" << endl;
cout << "The word like is repeated " << likeWord << " times" << endl << endl;
}
inputFile.clear(); // clear fail bit or seekg won't work!
inputFile.seekg (0, ios::beg);
}while (option != 6);
return 0;
}
Not the most efficient way (especially for one word), but maybe this could give you direction:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include <unordered_map>
void tokenize( const std::string & line, std::unordered_map< std::string, size_t > & map_count )
{
for( std::string::size_type pos_start = 0, pos_end = 0; ( pos_end != std::string::npos ) && ( pos_start = line.find_first_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", pos_end ) ) != std::string::npos; )
{
pos_end = line.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", pos_start );
std::string current_word = ( pos_end == std::string::npos ) ? line.substr( pos_start ) : line.substr( pos_start, pos_end - pos_start );
std::unordered_map< std::string, size_t >::iterator iter( map_count.find( current_word ) );
if( iter == map_count.end() )
map_count.insert( std::pair< std::string, size_t >( current_word, 1 ) );
else
++( iter->second );
}
}
void countWordsInFile( const std::string & filename, std::unordered_map< std::string, size_t > & map_count )
{
std::ifstream in( filename );
std::string line;
size_t count = 0;
if( in.is_open() )
while( std::getline( in, line ).good() )
tokenize( line, map_count );
}
double countAndWeigh( const std::string & filename, const std::string & word, double weight )
{
std::unordered_map< std::string, size_t > map_count;
countWordsInFile( filename, map_count );
std::unordered_map< std::string, size_t >::const_iterator iter( map_count.find( word ) );
return ( iter != map_count.end() ) ? iter->second * weight : 0;
}
int main( int argc, char ** argv )
{
if( argc == 4 )
std::cout << countAndWeigh( argv[1], argv[2], strtod( argv[3], nullptr ) ) << std::endl;
return 0;
}

Recursion of an index

Hello fellow programmers, I have a question about recursion that I do not understand, being new to C++ and all. So for this exercise I am completing, I need to: 1. Ask user for a string 2. Ask user for a string to search in the first string entered. 3. report and index of the string if it finds it. For instance the user enters the string "Search me", string to search for is "me", and the index would return "7". I am throwing my hands up at this point for some help on how to complete it, any help would be appreciated. Here is my code thus far. The stuff that is in the for loop isn't a complete thought, just FYI.
int index_of(string stringToSearchIn, string stringToSearchFor, int index)
{
if (stringToSearchIn.length() < stringToSearchFor.length())
{
cout << "Your string cannot be found." << endl;
system("PAUSE");
return OUTLIER;
}
else
{
bool found = true;
for (int i = ZERO; i < stringToSearchFor.length; i++)
{
found = found && stringToSearchIn(i) == stringToSearchFor(i);
}
return index;
}
return index_of(stringToSearchIn.substr(INCREASE), stringToSearchFor, index++);
}
int main()
{
//Initializing values
string userString;
string userSearch;
int userIndex = 0;
//Asking for user input for string
cout << "This program will find the occurence of one string inside of another." << endl;
cout << "Enter the string to be searched: " << userString;
//Getting the string
getline(cin, userString);
//Asking for user input for search input
cout << "Now enter the string you want to search for: " << userSearch;
//Getting the string
getline(cin, userSearch);
//Displaying results
cout << "The index of the substring is = " << index_of(userString, userSearch, userIndex);
//Keeping console window open until key press
system("PAUSE");
return ZERO;
}
Catch.:)
#include <iostream>
size_t index_of( const char *s, const char *t )
{
const char *p = s;
const char *q = t;
while ( *q && *p == *q ) ++p, ++q;
if ( *q == '\0' )
{
return 0;
}
else if ( *p == '\0' )
{
return -1;
}
else
{
size_t n = 1 + index_of( s + 1, t );
return n == 0 ? -1 : n;
}
}
int main()
{
const char *s = "Search me";
const char *t = "me";
size_t n = index_of( s, t );
if ( n != -1 )
{
std::cout << "string " << t << " is found at position " << n << std::endl;
}
else
{
std::cout << "string " << t << " is not found" << std::endl;
}
return 0;
}
The output is
string me is found at position 7
For other strings I did not test the function.:)
For objects of type std::string you can call it like
size_t n = index_of( s.c_str(), t.c_str() );
Otherwise if you want to write a similar recursive function for objects of type std::string then you need either to add a static local variable that to keep the current position inside the source string or add one more parameter for index or use member function substr.
Also my advice is do not use manifest constant ZERO for zero. In my opinion it is a bad style of programming. It only confuses readers because ZERO can be anything including some user-defined class.
For example (without testing)
#include <iostream>
#include <string>
std::string::size_type index_of( const std::string &s, const std::string &t )
{
static std::string::size_type pos;
if ( s.size() < t.size() ) return std::string::npos;
if ( s.compare( pos, t.size(), t ) == 0 ) return 0;
++pos;
std::string::size_type n = index_of( s, t );
--pos;
return n == std::string::npos ? std::string::npos : n + 1;
}
int main()
{
std::string s = "Search me";
std::string t = "me";
std::string::size_type n = index_of( s, t );
if ( n != std::string::npos )
{
std::cout << "string " << t << " is found at position " << n << std::endl;
}
else
{
std::cout << "string " << t << " is not found" << std::endl;
}
return 0;
}
As for your function realization then it can look for example the following way
#include <iostream>
#include <string>
std::string::size_type index_of( const std::string &stringToSearchIn,
const std::string &stringToSearchFor,
std::string::size_type index )
{
if ( stringToSearchIn.length() < stringToSearchFor.length() + index )
{
return std::string::npos;
}
else if ( stringToSearchIn.compare( index,
stringToSearchFor.length(),
stringToSearchFor ) == 0 )
{
return index;
}
else
{
std::string::size_type n =
index_of( stringToSearchIn, stringToSearchFor, ++index );
return n == std::string::npos ? std::string::npos : n;
}
}
int main()
{
std::string s = "Search me";
std::string t = "me";
std::string::size_type n = index_of( s, t, 0 );
if ( n != std::string::npos )
{
std::cout << "string " << t << " is found at position " << n << std::endl;
}
else
{
std::cout << "string " << t << " is not found" << std::endl;
}
return 0;
}

Vigenère Cipher code

How would I go about modifying this code to accept input from the user rather than using a predetermined string? Specifically, I need the program to require exactly two command line arguments. The first will either be the code "-e" or "-d" to indicate encoding or decoding of a message (this determines adding or subtracting you shift values) and the second parameter will be a single word that will be the keyword that you use for the encryption or decryption.
#include <iostream>
#include <cstring>
#include <algorithm>
// Vigenere Cipher Methods:
// Note: assumes that both strings as arguments have length > 0, and that
// the key only contains letters of the alphabet from [A-Z]
void vigenere_encrypt(std::string& s, std::string key)
{
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
std::transform(key.begin(), key.end(), key.begin(), ::toupper);
unsigned int j = 0;
for (int i = 0; i < s.length(); i++)
{
if (isalpha(s[i]))
{
s[i] += key[j] - 'A';
if (s[i] > 'Z') s[i] += -'Z' + 'A' - 1;
}
j = j + 1 == key.length() ? 0 : j + 1;
}
}
void vigenere_decrypt(std::string& s, std::string key)
{
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
std::transform(key.begin(), key.end(), key.begin(), ::toupper);
unsigned int j = 0;
for (int i = 0; i < s.length(); i++)
{
if (isalpha(s[i]))
{
s[i] = s[i] >= key[j] ?
s[i] - key[j] + 'A' :
'A' + ('Z' - key[j] + s[i] - 'A') + 1;
}
j = j + 1 == key.length() ? 0 : j + 1;
}
}
int main(void)
{
std::string s("AceInfinity's Example");
std::string key("Passkey");
vigenere_encrypt(s, key);
std::cout << "Encrypted: " << s << std::endl;
vigenere_decrypt(s, key);
std::cout << "Decrypted: " << s << std::endl;
return 0;
}
Update
Thanks to your input and a few other sources I have re-developed my main code as is shown below. I am having trouble getting the program to decrypt and encrypt strings properly and I am not sure if the error is somewhere in the code itself, or operator error. Does anything look out of the ordinary here and how could I get this code functional to where it can encrypt or decrypt given user input?
#include <iostream>
#include <string>
int usage( const char* exe_name )
{
std::cerr << "Usage: " << exe_name << " -e <text to encrypt>\n"
<< " " << exe_name << " -d <text to decrypt>\n" ;
return 1 ;
}
int main( int argc, char* argv[] )
{
if (argc < 3 ) return usage( argv[0] ) ;
const std::string option = argv[1];
std::string text = argv[2];
// cat the remaining cmd line args
for( int i = 3 ; i < argc ; ++i ) { text += ' ' ; text += argv[i] ; }
const std::string key("Passkey");
if ( option== "-e" )
std::cout << "Encrypt: '" << text << "'\n" ;
else if ( option == "-d" )
std::cout << "Decrypt: '" << text << "'\n" ;
else
{
std::cout << "Unrecognised command line option '" << option << "'\n";
return usage( argv[0] ) ;
}
}
If you want command line arguments, you'll need to change the prototype of your main function a little bit and use the standard argv array:
int main(int argc, const char** argv)
{
std::string s("AceInfinity's Example");
if (argc != 3)
{
std::cout << "Usage: -e text\n" << " -d text\n";
return 0;
}
std::string arg1 = argv[1];
std::string arg2 = argv[2];
if (arg1 == "-e")
{
vigenere_encrypt(s, arg2);
std::cout << "Encrypted: " << s << std::endl;
}
else if (arg1 == "-d")
{
vigenere_decrypt(s, arg2);
std::cout << "Decrypted: " << s << std::endl;
}
else
std::cout << "Unrecognised command line option " << arg1 << "\n";
return 0;
}
Minimal effort made for a quick example, code probably works, e&oe, caveat emptor, etc.
Of course, you'd really be best off using a proper command line argument parser, like getopt, and you'll still need some way to supply the plaintext for encryption or ciphertext for decription, but that's left as an exercise for the reader. Reading from stdin by using std::cin is one way of doing so, for example.
Use cin to accept input from the user and input it into a string. Parse the string to obtain the -e/-d flags and the keyword. If the input is not what you want, prompt the user to try again.