separate sequence of same numbers from string - c++

I'm trying to do a little task which asks to convert numbers to letters of phone keypad, for example if input is 222 it means phone button "2" ( http://upload.wikimedia.org/wikipedia/commons/7/7d/Telephone-keypad.png ) is pushed 3 times and output should be "C" and etc.
So first thing what i should do is to separate all the sequences, for example 22255-444 into 222 , 55 , - , 444 and then i think figure everything out, but now problem is that my function can't read last sequence
#include <iostream>
#include <fstream>
using namespace std;
//-------------------------------------------------------------------------
void encode(string text, string &result, int &i)
{
char keyboard[10][4] = {
{' ',' ',' ',' '},
{'.','?','!','.'},
{'a','b','c','a'},
{'d','e','f','d'},
{'g','h','i','g'},
{'j','k','l','j'},
{'m','n','o','m'},
{'p','r','q','s'},
{'t','u','v','t'},
{'w','x','y','z'}
};
int j;
for(j = i; j<text.size();j++)
{
if(text[i] != text[j] || j == text.size())
{
result = text.substr(i, j-i);
i = j-1;
break;
}
}
cout << result << endl;
}
int main()
{
ifstream fd("sms.in");
string text;
string result;
getline(fd, text);
for(int i = 0; i<text.size();i++)
{
encode(text, result, i);
}
return 0;
}
as a test now im using this input : 5552-22-27777 , output should be 555 2 - 22 - 2 7777, but for me its 555 2 - 22 - 2 2 2 2 2.

In this if statement:
if(text[i] != text[j] || j == text.size())
The second condition (j == text.size()) will never be true because the loop will terminate before that. So when you reach the end of the string, the values of result and i will not be updated correctly.
What you can do is remove the termination condition from the loop (it's not necessary to have one because you will break out of the loop anyway). And you will need to reverse the order of the conditions in the if so that you don't read past the end of the string:
for(j = i; ;j++)
{
if (j == text.size() || text[i] != text[j])
...

I'd pull that last bit out of the for loop, it doesn't really belong in there because it's a special case. I've also eliminated some other things that seemed unnecessary and fixed the inside of the loop up. See if this all makes sense to you.
I pulled the keymap declaration out because it was not used in your example, and we value what are known as "minimal working examples" here as a way to isolate and discuss problems cogently.
Note that I've tried to move all the encoding work into a single function and out of the main() function. This frees up main to handle high-level parts of the program. On the lines I've marked "Decode here" you could call to yet another function that translates the string of numbers into a character, if that's what you're going for.
I've marked the special case and explicitly moved it out of the for-loop so that everything within the for-loop can be handled the same. This is better than trying to make the for-loop do everything.
I didn't agree with the logic that i=j-1, so I changed that (I did test this).
I've also switched to read input directly from the command line so that it's quicker to test.
I added the #include <string> directive at the beginning so that the program would compile.
Putting all the encoding work inside a single function simplified that functions declaration to a single argument.
I switched the brackets to conform to my own understanding of the One True Style.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void encode(string text) {
int j=0,i=0; //Declare j here so we can use in special case
for(j=0; j<text.size(); j++){
if(text[j] != text[i]){
cout << text.substr(i, j-i) << endl; //Decode here
i = j;
}
}
cout << text.substr(i,j-1) << endl; //Decode here. Special case
}
int main(){
string text;
getline(cin, text);
encode(text, result);
return 0;
}

Related

C++ Code keeps crashing after a validation

I have written a piece of code to validate a keyword, it validates and makes sure that the word is 5 letters long and it is all letters with no numbers in it. However, when I run it, the code seems to stop working at all and doesn't prompt me for the next question, I've tested it without this code and this part of the code is the problem, as it works fine without it.
The code:
cout<<name1<<", please enter the keyword (5 characters): "<<endl;
cin>>key;
for(int i = 0; i < keylength; i++){
if(isalpha(key[i]) == 1){
validnum += 1;
}
}
if(validnum == keylength && key.length() == keylength){
validated = true;
}
else{
validated = false;
}
As mentioned in the comments, there is no need for any loops to determine if the key is 5 characters and is all alphabetic.
Using std::all_of will test if all the characters are alphabetic:
#include <algorithm>
#include <string>
#include <cctype>
#include <iostream>
bool isValidData(const std::string& key)
{
return key.size() == 5 && std::all_of(key.begin(), key.end(), ::isalpha);
}
int main()
{
//Test data
std::string testKeys[] = {"1abcdef", "abcde", "abcdefghijkl", "a", "xyzab"};
for (size_t i = 0; i < std::size(testKeys); ++i)
{
// Output results
std::cout << testKeys[i] << " " << (isValidData(testKeys[i])?"OK":"BAD") << "\n";
}
}
Output:
1abcdef BAD
abcde OK
abcdefghijkl BAD
a BAD
xyzab OK
Also, if you took a look at your code, it is not clear what the goal of the code is without running the code. Compare that to the function isValidData: if you say what that function has in it, it reads almost as your description:
"The key size must be 5, and all of the characters must be alpha".
Before the for loop you need to check that key.length() is equal to keyLength. Otherwise the loop can invoke undefined behavior when the user enters a string with the length less than keyLength.
Also the function isalpha does not necessary returns 1. It can return any positive value.
Change your code something like the following
validated = key.length() == keyLength;
if ( validated )
{
size_t i = 0;
while ( i < keyLength && isalpha( ( unsigned char )key[i] ) ) ++i;
validated = i == keyLength;
}

I'm trying to change every letter in a given string with the letter following it in the alphabet using custom input

#include<iostream>
#include<string>
using namespace std;
string pass(string a){
int i=0;
string c[100];
char d;
while(a[i]!='\0'){
d = a[i];
if(d>='a'&& d<='z'){
d++;
c[i]=d;
}
else if(d>='A' && d<='Z'){
d++;
c[i]=d;
}
else{
c[i]=d;
}
i++;
}
for(int k=0; k<i; k++){
cout<<c[k];
}
}
int main(){
string x;
getline(cin,x);
pass(x);
return 0;
}
This is my solution.
I was looking for this kind of problem for a while but all I got was for pre-defined inputs. So, I passed a string from the main function.
I used a while loop to store every letter with the following letter (EX "a -> b") in another array "c". I then print the copied array using a loop.
Can we make it shorter?
You don't need to create a separate array called c. You can create an output string and iterate through it and increment the characters as shown below:
int main()
{
std::string input;
std::getline(std::cin, input);
std::string output(input);
for(char &c: output)
{
++c;
}
std::cout<<"input was: "<<input<<std::endl;
std::cout<<"changed string is: "<<output<<std::endl;
}
This is probably a better (and shorter) implementation for your code:
#include<iostream>
#include<string>
char NextAlpha(char character)
{
if (character == 'Z') return 'A';
else if (character == 'z') return 'a';
return character + 1; // Can be replaced by 'char((int)character + 1);'
}
int main() {
std::string input;
getline(std::cin, input);
for (int i = 0; i < input.size(); i++)
{
input[i] = NextAlpha(input[i]);
}
std::cout << input;
return 0;
}
The NextAlpha function returns the next alphabet by adding 1 to the character, but a more understandable version of it will be firstly converting the given character into an int as such:
(int)character
..which basically means getting the ascii value of that character. Now we add 1 to the int:
(int)character + 1
..and then convert it back to char
char((int)character + 1)
..but here I've not used this way because ++character looks a lot more cleaner.
The exceptions are defined before the return statement.
In the main function, we have a loop that iterates through all of the characters in the given string, and for each character, it does the following:
// Set the character at index 'i' of string 'input' to the next character in the alphabet.
input[i] = NextAlpha(input[i]);
Also, consider not using the following in your code:
using namespace std;
..as it's considered as bad practice.
First, you have an error in your code: your pass function is declared as returning a string but it doesn't return anything. Actually, you don't need to return anything – just pass the string by reference and make any required changes to its content "in place".
Second, you should be aware that the C++ Standard does not require that the Latin letters (lower- and upper-case) be represented by contiguous, sequential values (though in the ASCII system, used in most implementations today, they are).1
Third, you don't need so many loops, and you don't need to repeat the c[i]=d; statement in the if, else if and else blocks.
I'm not sure what you want to do with the 'z' and 'Z' characters but, in the code below, I'm assuming these should "wrap around", to 'a' and 'A', respectively.
So, here's a way to do what you want more concisely, and which doesn't depend on the implementation's specific representation values for Latin letters:
#include<iostream>
#include<string>
void pass(std::string& a) // Pass by reference (using "&") - changes will be kept.
{
const std::string Lowers{ "abcdefghijklmnopqrstuvwxyz" };
const std::string Uppers{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" };
size_t p;
for (auto& c : a) { // Note the "&" again: changes to c will be reflected in the corresponding "a" element
if ((p = Lowers.find(c)) != std::string::npos) {
c = Lowers[++p % 26]; // 'z' wraps round to 'a'
}
if ((p = Uppers.find(c)) != std::string::npos) {
c = Lowers[++p % 26]; // 'Z' wraps round to 'A'
}
}
}
int main()
{
std::string x;
std::getline(std::cin, x);
pass(x);
std::cout << x << std::endl;
return 0;
}
I have moved the output of the transformed string to the main function (and simplified it somewhat); generally, a function should do only the task it is set – which, in this case is to make the transformation. The subsequent display of the transformed string should be left to the calling module.
1 The EBCDIC system, for example, doesn't have the Latin letters in a contiguous sequence. You could use the std::islower(), std::isupper() and/or std::isalpha() functions to check for letters, but you would still need some sort of "data table" to determine what the 'next' character should be, unless you assume an ASCII or compatible encoding system.

Encrypting a string but receiving an infinite loop

Problem:
I was trying to encrypt a std::string password with a single rule:
Add "0" before and after a vowel
So that bAnanASplit becomes b0A0n0a0n0A0Spl0i0t.
However, I got stuck in an infinite loop.
Here is the code:
const std::string VOWELS = "AEIOUaeiou";
std::string pass = "bAnanASplit";
//Add zeroes before and after vowels
for (int i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
std::cout << pass << "\n";
if(i != std::string::npos)
{
std::cout << pass[i] << ": " << i << "\n";
pass.insert(pass.begin() + i++, '0');
pass.insert(pass.begin() + ++i, '0');
}
}
...And the result:
bAnanASplit
A: 1
b0A0nanASplit
a: 5
b0A0n0a0nASplit
A: 9
b0A0n0a0n0A0Split
i: 15
b0A0n0a0n0A0Spl0i0t
b0A0n0a0n0A0Spl0i0t
A: 2
b00A00n0a0n0A0Spl0i0t
a: 8
b00A00n00a00n0A0Spl0i0t
A: 14
b00A00n00a00n00A00Spl0i0t
i: 22
b00A00n00a00n00A00Spl00i00t
b00A00n00a00n00A00Spl00i00t
...
Any help? This sure seems strange.
Edit: All the answers were useful, and therefore I have accepted the one which I think best answers the question. However, the best way to solve the problem is shown in this answer.
Never, ever, modify the collection/container you are iterating upon!
Saves you a lot of trouble that way.
Let's start with your code and generate a new string with vowels surrounded by 0.
const std::string VOWELS = "AEIOUaeiou";
std::string pass = "bAnanASplit", replacement;
//Add zeroes before and after vowels
for (auto ch : pass)
{
if(VOWELS.find(ch) != std::string::npos)
replacement += '0' + ch + '0';
else
replacement += ch;
}
And there you have it!
As the OP seems to look for the exact reason for the misbehavior, I thought to add another answer as the existing answers do not show the exact issue.
The reason for the unexpected behavior is visible in following lines.
for (int i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
...
Problem 1:
The loop counter i is an int (i.e. a signed int). But std::string::find_first_of returns std::string::npos if there's no match. This is usually the maximum number representable by an unsigned long. Assigning a huge unsigned value to a shorter signed variable will store a totally unexpected value (assuming you are not aware of that). In this case, i will becomes -1 in most platforms (try int k = std::string::npos; and print k if you need to be sure). i = -1 is valid state for the loop condition i < pass.length(), so the next iteration will be allowed.
Problem 2:
Closely related to the above problem, same variable i is used to define the start position for the find operation. But, as explained, i will not represent the index of the character as you would expect.
Solution:
Storing a malformed value can be solved by using the proper data type. In the current scenario, best options would be using std::string::size_type as this is always guaranteed to work (most probably this will be equal to size_t everywhere). To make the program work with the given logic, you will also have to use a different variable to store the find result.
However, a better solution would be using a std::stringstream for building the string. This will perform better than modifying a string by inserting characters in the middle.
e.g.
#include <iostream>
#include <sstream>
int main() {
using namespace std;
const string VOWELS = "AEIOUaeiou";
const string pass = "bAnanASplit";
stringstream ss;
for (const char pas : pass) {
if (VOWELS.find(pas) == std::string::npos) {
ss << pas;
} else {
ss << '0' << pas << '0';
}
}
cout << pass << "\n";
cout << ss.str() << endl;
}
You are not exiting the loop in case i becomes std::string::npos. So, the i value is changed to some unexpected value (likely something like -1) when it gets to the position of last i or 0 after i(here I am referring to i of split). This is because i is an signed integer but in this case find_first_of() returns std::string::npos which is largest value that can be held by a size_t. In that case the terminating condition i < pass.length() may hold true and the loop continues. So, I am recommending following changes in your code -
for (size_t i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
if(i == std::string::npos)
break;
pass.insert(pass.begin() + i++, '0');
pass.insert(pass.begin() + ++i, '0');
}
On the same note if (i != std::String::npos) does not do what you are expecting it to do.
But then again it better not to modify the container while you are iterating over it which #Tanveer mentioned in his answer

How to apply multiple formatting methods at the same time, e.g."1,234,567,890" and "12-3456-7890" to "1,2-34,56-7,890"

for example, if I want to convert "1234567890" to "1,234,567,890", I can use:
#include <string>
#include <stdio.h>
using namespace std;
int main(){
string st="1234567890";
for(int i=st.length()-3;i>0;st.insert(i,","),i-=3);
printf("%s\n",st.c_str());
return 0;
}
and if I want to convert "1234567890" to "12-3456-7890", just replace the for loop as :
for(int i=st.length()-4;i>0;st.insert(i,"-"),i-=4);
but the problem is, it can apply 1 format method only,and this formatting method is sequential, if I apply 2 for loops together, e.g.:
for(int i=st.length()-3;i>0;st.insert(i,","),i-=3);
for(int i=st.length()-4;i>0;st.insert(i,"-"),i-=4);
the output is 1-,234-,567-,890 but not "1,2-34,56-7,890", what is the generic way to do this?
Or in simpler words, I want a program that can:
1. insert "," for every 3 characters
2. insert "-" for every 4 characters
3. insert ":" for every 7 characters
.
.
.
which can add insert conditions in generic way, what is the simplest way to do this?
Instead of using several loops, you can just use one loop, decremental by 1, with conditional insert. For example:
string st="1234567890";
int originalLength = st.length();
for(int i=originalLength-1;i>0;i--)
{
int positionFromEnd = originalLength - i;
if (positionFromEnd % 3 == 0)
st.insert(i,",");
if (positionFromEnd % 4 == 0)
st.insert(i,"-");
if (positionFromEnd % 5 == 0)
st.insert(i,":");
// add more condition here as need be
}
printf("%s\n", st.c_str());
If it's ok for you, ignore anything that is not a number, in each pass:
#include <string>
#include <stdio.h>
using namespace std;
void insert_delimeter(string& st, string delimeter, int interval) {
for (int i = st.length() - 1, counter = 0; i > 0; i--) {
if (st[i] >= '0' && st[i] <= '9')
++counter;
if (counter == interval) {
st.insert(i,delimeter);
counter = 0;
}
}
}
int main(){
string st = "1234567890";
insert_delimeter(st, ",", 3);
insert_delimeter(st, "-", 4);
printf("%s\n", st.c_str());
return 0;
}
The outcome is
1,2-34,56-7,890
add one more counter to the first for loop
int num=0;
for(int i=st.length()-3;i>0;st.insert(i,","),i-=3)
num++;
for(int i=st.length()-4 + num ;i>0;st.insert(i,"-"),i-=4)
num++;
what happens here is num gets incremented everytime an insert takes place thus it helps to get correct string length in the next string update. which you can use to insert more symbols.

How do I increment letters in c++?

I'm creating a Caesar Cipher in c++ and i can't figure out how to increment a letter.
I need to increment the letter by 1 each time and return the next letter in the alphabet. Something like the following to add 1 to 'a' and return 'b'.
char letter[] = "a";
cout << letter[0] +1;
This snippet should get you started. letter is a char and not an array of chars nor a string.
The static_cast ensures the result of 'a' + 1 is treated as a char.
> cat caesar.cpp
#include <iostream>
int main()
{
char letter = 'a';
std::cout << static_cast<char>(letter + 1) << std::endl;
}
> g++ caesar.cpp -o caesar
> ./caesar
b
Watch out when you get to 'z' (or 'Z'!) and good luck!
It works as-is, but because the addition promotes the expression to int you want to cast it back to char again so that your IOStream renders it as a character rather than a number:
int main() {
char letter[] = "a";
cout << static_cast<char>(letter[0] + 1);
}
Output: b
Also add wrap-around logic (so that when letter[0] is z, you set to a rather than incrementing), and consider case.
You can use 'a'+((letter - 'a'+n)%26);
assuming after 'z' you need 'a' i.e. 'z'+1='a'
#include <iostream>
using namespace std;
int main()
{
char letter='z';
cout<<(char)('a' + ((letter - 'a' + 1) % 26));
return 0;
}
See this https://stackoverflow.com/a/6171969/8511215
Does letter++ work?
All in all char is a numeric type, so it will increment the ascii code.
But I believe it must be defined as char letter not an array. But beware of adding one to 'Z'. You will get '[' =P
#include <iostream>
int main () {
char a = 'a';
a++;
std::cout << a;
}
This seems to work well ;)
char letter = 'a';
cout << ++letter;
waleed#waleed-P17SM-A:~$ nano Good_morning_encryption.cpp
waleed#waleed-P17SM-A:~$ g++ Good_morning_encryption.cpp -o Good_morning_encryption.out
waleed#waleed-P17SM-A:~$ ./Good_morning_encryption.out
Enter your text:waleed
Encrypted text:
jnyrrq
waleed#waleed-P17SM-A:~$ cat Good_morning_encryption.cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
//the string that holds the user input
string text;
//x for the first counter than makes it keeps looping until it encrypts the user input
//len holds the value (int) of the length of the user input ( including spaces)
int x, len;
//simple console output
cout << "Enter your text:";
//gets the user input ( including spaces and saves it to the variable text
getline(cin, text);
//give the variable len the value of the user input length
len = (int)text.length();
//counter that makes it keep looping until it "encrypts" all of the user input (that's why it keeps looping while its less than len
for(x = 0; x < len; x++) {
//checks each letts (and spaces) in the user input (x is the number of the offset keep in mind that it starts from 0 and for example text[x] if the user input was waleed would be w since its text[0]
if (isalpha(text[x])) {
//converts each letter to small letter ( even though it can be done another way by making the check like this if (text[x] =='z' || text[x] == 'Z')
text[x] = tolower(text[x]);
//another counter that loops 13 times
for (int counter = 0; counter < 13; counter++) {
//it checks if the letts text[x] is z and if it is it will make it a
if (text[x] == 'z') {
text[x] = 'a';
}
//if its not z it will keeps increamenting (using the loop 13 times)
else {
text[x]++;
}
}
}
}
//prints out the final value of text
cout << "Encrypted text:\n" << text << endl;
//return 0 (because the the main function is an int so it must return an integer value
return 0;
}
Note: this is called caeser cipher encryption it works like this :
ABCDEFGHIJKLMNOPQRSTUVWXYZ
NOPQRSTUVWXYZABCDEFGHIJKLM
so for example my name is waleed
it will be written as : JNYRRQ
so its simply add 13 letters to each letter
i hope that helped you
It works but don't forget that if you increment 'z' you need to get 'a' so maybe you should pass by a check function that output 'a' when you get 'z'.
cast letter[n] to byte* and increase its referenced value by 1.