-1

I am trying to create a code that will use the entire printable ASCII Character set. My problem is that when it comes to characters that will be a number higher than 126 they print as '?', except for 'r', which prints correctly. Why does my code allow 'r' to roll back up to a printable character but not any characters after that? (stuvwxyz{|}~)

" Please enter your password.

abcdefghijklmnopqrstuvwxyz{|}~

nopqrstuvwxyz{|}~!???????????? "

#include <iostream>
#include <string>
using namespace std;

void encrypt(string password)
{

int count = 0;
char i;

    for (count = 0; count < password.length(); count++)
    {
        i = password.at(count) + 13;
            if (i >= 127)
                {
                    i = 32 + (i - 126);
                    cout << i;
                }
            else 
                cout << i;          
    }
    return;
}

int main()
{
    string password;

    cout << "Please enter your password." << endl;
    cin >> password;

    encrypt(password);
    cout << "\n";
    return 0;
}
Lacey
  • 11
  • 1
  • 2
    " My problem is that when it comes to characters that will be a number higher than 126 they print as '?', " - because such numbers are not part of the ASCII character set. –  Oct 01 '17 at 16:49
  • BTW, if you would **use debugger**, you would have seen the value in `i` is `-128` for `"s"`, not `+128`. ... so next time, before posting to SO, try debugging first. – Ped7g Oct 02 '17 at 04:35

1 Answers1

0

Your target platform has signed char, so valid values in char are of range -128 .. +127.

Thus your if (i >= 127) will cover only 'r' (value 114), for 's' the i is equal to -128, not +128. And your if will be false and you will output -128 char value.

One quick-fix is to declare i as unsigned char to operate rather on range +0 .. +255. If you are going to support only valid ASCII 7b input, then that is enough to have all that +13 arithmetic correct.

Also I'm not sure why you do 32 + (i - 126);, that's asking r to convert to ! (33), skipping space (32), which is printable ASCII character.

So after those fixes applied, and simplifying the code a bit (toward C++ style), I ended with:

void encrypt(const string & password)
{
    for (unsigned char charToRot13 : password) {
        charToRot13 += 13;
        if (127 <= charToRot13) charToRot13 -= (127 - ' ');
        cout << charToRot13;
    }
}

Which does input+output ('r' now maps to ' '):

Please enter your password.
abcopqrstuvw
nop|}~ !"#$%

BUT. The main ROT13 feature is, that you can encrypt and decrypt the text by the same operation. I.e. "abc" -> "nop" -> "abc" -> "nop" -> ...

Your conversion to whole ASCII range breaks that feature. So what you probably want is to cover whole +32 .. +126 range of ASCII values, that's 95 of them. 95/2 = 47.5, not good. Let's remove space, because you already did that by that -126, so you probably don't want it. The value range to encrypt is then +33 .. +126 = 94 of them. 94/2 = 47, good. To get ROT13-like encryption for whole ASCII range (except space) you can use ROT47.

void encrypt(const string & password)
{
    for (unsigned char charToRot47 : password) {
        if (charToRot47 < 33 || 126 < charToRot47) {
            cout << "\nERROR: detected invalid character.\n";
            break;
        }
        charToRot47 += 47;
        if (126 < charToRot47) charToRot47 -= 94;
        cout << charToRot47;
    }
}

This will output:

Please enter your password.
xyz{|}~!"#$%000000IJKLMNOPQRST
IJKLMNOPQRST______xyz{|}~!"#$%

(notice how for example 'y' -> 'J' -> 'y' -> 'J' -> ...)

Ped7g
  • 16,236
  • 3
  • 26
  • 63
  • And I cleaned up that final code to operate only with the "magic constants" from the text description, i.e. valid range is 33..126, number of characters covered is 94, and 94/2 = 47. There's no good reason to suddenly use some magic numbers like 127 and similar, those are not directly related to ROT47, and indeed it's possible to write it cleanly with only those values from ROT47 definition. – Ped7g Oct 02 '17 at 04:54