Caesar cipher - problem with letters and spaces

Asked

Viewed 4,690 times

7

I’m doing an exercise on the figure of the table below:

Cifra de César

#include <stdio.h>
#include <string.h>

main(){
  char frase[200];

  printf("String: ");
  gets(frase);

  for(int i=0; i < strlen(frase); i++){

   if(frase[i] == 'x')
     frase[i] = 'A';
   if(frase[i] == 'y')
     frase[i] = 'B';
   if(frase[i] == 'z')
     frase[i] = 'C';

     frase[i] = frase[i] + 3;
  }

  printf("String: %s", frase);
}

Problems:

  1. If I try to convert the letters 'XYZ' is not possible appears '[/] Note: I tried to include if() for case phrase[i] == 'y' it would store the letter 'B' in the case, but it did not work. inserir a descrição da imagem aqui

  2. When I use spaces it appears # in their place.

  • Try to say what the goal is, the information is too much to understand. I don’t even know what the problem is. Would it be just to make all letters worth 3 misplaced units? The problem is only the last ones? Show what you tried and didn’t work. Do you want to prevent the use of spaces or other characters? If not, what is the criteria to deal with them?

  • The person writes something and the letters will be worth those below according to the table, yes the problem is only in the last ones, I do not want to prevent the use of space I just do not want the space to appear # (be space)

  • 1

    From what I understand you’re wanting to make XYZ become ABC, as I saw in the cipher description of César, what’s happening is when you perform (+ 3) you’re adding up the number according to the ANSI table, so if you take a look at it, the code for Y is different for y, so your if logic didn’t work. Take a look at the ASCII table and you will notice that the next values after Z is [ ]. You can include a logic that when arriving in the last letters (X,Y,Z,x,y,z) it overwrites by (A,B,C,a,b,c). I hope I have helped.

  • We still need to know what to do if something is typed out of this range and what you did on if that didn’t work out.

  • I will add in the code the if I used, it changes the values but the change is made again this way: X > A > D

4 answers

9


If I understand the problem, you need to make an exception for the last characters. I took the opportunity to discard something outside of what is expected.

I made the most idiomatic code C walking a pointer instead of picking up its size. Don’t use the strlen() where you don’t need.

If you use another data entry mechanism like fgets() will read the data more appropriately, including space. Scanf() is only for testing and trivial use.

I did as stated, if other characters need to be treated, you need to stir the code a little to add them. There is no clear criterion for what to do in the case of characters leaving the stated range, I chose to leave as is.

#include <stdio.h>

int main() {
    char frase[200];
    printf("String: ");
    scanf("%200s", frase);
    for (char *i = frase; *i != '\0'; i++) *i = *i < 'A' || *i > 'Z' ? *i : (((*i - 65) + 3) % 26)  + 65;
    printf("String: %s", frase);
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

If you want to do not idiomatic, has how, works, technically it is not wrong, but this is not how you do it in C. If you want to do it this way it is better to use another language.

#include <stdio.h>
#include <string.h>

int main() {
    char frase[200];
    printf("String: ");
    scanf("%200s", frase);
    for (int i = 0; i < strlen(frase); i++) {
        if (frase[i] >= 'A' && frase[i] <= 'Z') { //só altera se estiver na faixa correta
            if (frase[i] > 'W') { //trata os casos que precisam girar a tabela
                frase[i] += -23;
            } else {
                frase[i] += 3;
            }
        }
    }
    printf("String: %s", frase);
}

I put in the ideone, but I wish I hadn’t written like this.

  • It became difficult for me to understand what is happening these * and < ? :

  • @WSS made a version worse, but simpler for you to understand. If you’re having trouble understanding these things you probably need to start doing simpler things before you get to that point.

  • @bigown What’s wrong with this other version? Alias I tested it here and did not effect the lyrics changes

  • 1

    @WSS I will test, as I did on hand here, without testing may have some problem. The problem is that this is not how you do it in C, for the C program as if it were another language use another language. C is only advantageous when using it right, otherwise it works, but you get the disadvantages of C, which are many, and do not gain the advantages of it, which are important.

  • @bigown why not use the operator module % to limit the sum?

  • 1

    @merchant improved.

  • @WSS tidied up there. I wasn’t picking up the element of array.

  • @bigown would be interesting to mention the ASCII table to explain the sum and subtraction of the number 65. Otherwise it gives the impression of being a magic number. As well as choosing the number 26 to use the module operator, although this is clearer why.

Show 3 more comments

3

I think an easier way to explain it would be:

int main(){

  char frase[200];

  strncpy(frase, "SUA FRASE QUALQUER", 200);

  for(int i=0; i < strlen(frase); i++){

      if(frase[i] >= 65 && frase[i] <= 90){

        frase[i] = (((frase[i] - 65) + 3) % 26)  + 65;

      }else{
          frase[i] = 32;
      }

  }

  printf("String: %s", frase);
}

Test this.

All capital letters supported in ASCII then between 65 and 90, in decimal, so is used the if(frase[i] >= 65 && frase[i] <= 90){, if not it will switch to a space, in the else{}.


frase[i] = (((frase[i] - 65) + 3) % 26)  + 65;

The cipher of César itself is basically moving the alphabet by 3 characters.

The problem is that the ASCII A is 65. That’s why I used the (frase[i] - 65), thus the A will stay 0, because 65 - 65. Then it is added to "key", thus 0 + 3, then the rest of the division is done by the number of characters in the alphabet 3 % 26, that will give 3 and then it’s added with 65, thus will give 68 which is the D in the ASCII.

If it were Z would look exactly 90 - 65, afterward 25 + 3 and then do the 28 % 26 that will result in 2. Soon the Z will become a C, after all is the result of 65 + 2.

  • It is much less error-prone if you use character literals 'A' and 'Z' instead of its ASCII values 65 and 90, and for the compiler whatever.

3

On the basis of Article wikipedia, implemented the functions of cifragem and decifragem from a chave, represented by an integer value.

Note that encryption and decryption only consider letters, ignoring all other types of characters.

Follow the Code Tested:

#include <stdio.h>
#include <string.h>

void cifrar( char * saida, const char * entrada, int chave )
{
    int i = 0;
    int tam = strlen(entrada);

    for( i = 0; i <= tam; i++ )
    {
        if( (entrada[i] >= 'a') && (entrada[i] <= 'z') )
        {
            saida[i] = (((entrada[i] - 'a') + chave) % 26) + 'a';
        }
        else if( (entrada[i] >= 'A') && (entrada[i] <= 'Z') )
        {
            saida[i] = (((entrada[i] - 'A') + chave) % 26) + 'A';
        }
        else
        {
            saida[i] = entrada[i];
        }

    }
}


void decifrar( char * saida, const char * entrada, int chave )
{
    int ch = 0;
    int i = 0;
    int tam = strlen(entrada);

    for( i = 0; i <= tam; i++ )
    {
        if( (entrada[i] >= 'a') && (entrada[i] <= 'z') )
        {
            ch = (entrada[i] - 'a') - chave;
            if( ch < 0 ) ch += 26;
            saida[i] = ch + 'a';
        }
        else if( (entrada[i] >= 'A') && (entrada[i] <= 'Z') )
        {
            ch = (entrada[i] - 'A') - chave;
            if( ch < 0 ) ch += 26;
            saida[i] = ch + 'A';
        }
        else
        {
            saida[i] = entrada[i];
        }

    }
}

int main( int argc, char * argv[] )
{
    char original[] = "Um pequeno jabuti xereta viu dez cegonhas felizes!";

    char cifrado[100] = {0};
    char decifrado[100] = {0};

    cifrar( cifrado, original, 3 );
    decifrar( decifrado, cifrado, 3 );

    printf( "Original: %s\n", original );
    printf( "Cifrado: %s\n", cifrado );
    printf( "Decifrado: %s\n", decifrado );

    return 0;
}

/* fim-de-arquivo */

Testing:

$ ./cesar
Original: Um pequeno jabuti xereta viu dez cegonhas felizes!
Cifrado: Xp shtxhqr mdexwl ahuhwd ylx ghc fhjrqkdv iholchv!
Decifrado: Um pequeno jabuti xereta viu dez cegonhas felizes!

2

You should write your parole differently

if(frase[i] == 'x')
     frase[i] = 'A';
else if(frase[i] == 'y')
    frase[i] = 'B';
else if(frase[i] == 'z')
    frase[i] = 'C';
else
    frase[i] = frase[i] + 3;

This way the increment will only be done if the letters are different from x, y and z.

According to your image of the Caesar Cipher conversion table, blank spaces can be disregarded. Using scanf("%200s", frase); like the Maniero suggested solves the problem

  • thus became closer to what was already doing

Browser other questions tagged

You are not signed in. Login or sign up in order to post.