2017-02-13 4 views
1

Je crée un programme pour faire un César Cipher, qui décale les lettres dans un mot une fois lorsque j'appuie sur Entrée, et invite l'utilisateur à décaler à nouveau ou quitter.Caesar Cipher Java Program ne peut pas décaler plus de 23

Cela fonctionne jusqu'à ce que j'arrive à 23 décalages, puis il commence à utiliser des symboles non-lettre pour une raison quelconque, et je ne sais pas pourquoi cela se produit.

Des suggestions? Voici le code:

import java.io.File; 
import java.io.IOException; 
import java.util.Scanner; 

public class Cipher { 

    public static void main(String[] args) { 

     // encrypted text 
     String ciphertext; 

     // input from keyboard 
     Scanner keyboard = new Scanner(System.in); 

     if (args.length > 0) { 
      ciphertext = ""; 
      try { 
       Scanner inputFile = new Scanner(new File(args[0])); 
       while (inputFile.hasNext()) 
        ciphertext += inputFile.nextLine(); 
      } catch (IOException ioe) { 
       System.out.println("File not found: " + args[0]); 
       System.exit(-1); 
      } 
     } else { 
      System.out.print("Please enter text--> "); 
      ciphertext = keyboard.nextLine(); 
     } 

     // ----------------------------------------------------------------- 

     int distance = 0; // how far the ciphertext should be shifted 
     String next = ""; // user input after viewing 
     while (!next.equals("quit")) { 
      String plaintext = ""; 
      distance += 1; 
      for (int i = 0; i < ciphertext.length(); i++) { 
       char shift = ciphertext.charAt(i); 
       if (Character.isLetter(shift)) { 
        shift = (char) (ciphertext.charAt(i) - distance); 
        if (Character.isUpperCase(ciphertext.charAt(i))) { 
         if (shift > '0' && shift < 'A') { 
          shift = (char) (shift + 26); 
          plaintext += shift; 
         } else { 
          plaintext += shift; 
         } 
        } 
        if (Character.isLowerCase(ciphertext.charAt(i))) { 
         if (shift > '0' && shift < 'a' && ciphertext.charAt(i) < 't') { 
          shift = (char) (shift + 26); 
          plaintext += shift; 
         } else { 
          plaintext += shift; 
         } 
        } 
       } else { 
        plaintext += shift; 
       } 
      } 

      System.out.println(ciphertext); 

      // At this point, plaintext is the shifted ciphertext. 
      System.out.println("distance " + distance); 
      System.out.println(plaintext); 
      System.out.println("Press enter to see the next option," 
        + "type 'quit' to quit."); 
      next = keyboard.nextLine().trim(); 
     } 
     System.out.println("Final shift distance was " + distance + " places"); 
    } 
} 
+1

Avez-vous débogué votre code? –

+0

'... texte en clair + = décalage; } else {texte en clair + = shift; } '- Cela n'a pas de sens. Vous pouvez mettre l'instruction 'plaintext + = shift' en dehors de' else'. –

+1

Juste pour que vous sachiez - passé «Z» et «z» sont une poignée de caractères qui n'ont rien à voir avec les lettres. Vous allez vouloir les ignorer. – Makoto

Répondre

0

Comment fonctionne le décalage dans votre méthode? Eh bien, il exploite le fait qu'un char peut, en Java, également être considéré comme un int, un nombre simple.

En raison de ce que vous pouvez faire des choses comme ceci:

char c = 'A';         // Would print: A 
int cAsValue = (int) c;      // Would print: 65 
int nextValue = cAsValue + 1;     // Would print: 66 
char nextValueAsCharacter = (char) nextValue; // Would print: B 

ou même:

int first = (int) 'A';    // Would print: 65 
int second = (int) 'D';    // Would print: 68 
int third = first + second;   // Would print: 133 
char thirdAsCharacter = (char) third; // Would not print anything meaningful 

Ok, maintenant que nous savons comment nous pouvons interpréter char comme int, laissez nous analysons pourquoi 65 représente le caractère A et pourquoi 133 n'a rien de significatif. Le mot-clé est UTF-16. Les caractères en Java sont codés en UTF-16 et il existe des tables qui répertorient tous les caractères de ce codage avec leur nombre décimal spécifique, comme here.

Voici un extrait pertinent:

UTF-16 table showing characters around 'A'

Cela répond pourquoi 65 représente A et pourquoi 133 est rien de significatif.


La raison pour laquelle vous avez des résultats étranges après certains changements est que l'alphabet a seulement une taille de 26 symboles.

Je pense que vous vous attendriez à ce que tout recommence et a décalé par 26 est de nouveau a. Mais malheureusement, votre code n'est pas assez intelligent, il faut tout simplement le caractère actuel et ajoute le changement à elle, comme ça:

char current = 'a'; 
int shift = 26; 

int currentAsInt = (int) current;  // Would print: 97 
int shifted = currentAsInt + shift;  // Would print: 123 
char currentAfterShift = (char) shifted; // Would print: { 

Comparez cela à la partie correspondante du tableau:

UTF-16 table showing characters around '{'

Donc, après z ne vient pas encore a mais plutôt {.Donc, après que le mystère a été résolu, parlons maintenant de la façon de résoudre le problème et de rendre votre code plus intelligent.

Vous pouvez simplement vérifier les limites, comme "si elle est supérieure à la valeur pour 'z' ou plus petit que 'a', puis le remettre dans la bonne plage". Nous pouvons le faire facilement en utilisant l'opérateur modulo donné par %. Il divise un nombre par un autre et renvoie le reste de la division.

Voici comment nous pouvons l'utiliser:

char current = 'w'; 
int shift = 100; 
int alphabetSize = 26; // Or alternatively ('z' - 'a') 

int currentAsInt = (int) current;   // Would print: 119 
int shiftInRange = shift % alphabetSize; // Would print: 22 
int shifted = currentAsInt + shiftInRange; // Would print: 141 (nothing meaningful) 

// If exceeding the range then begin at 'a' again 
int shiftCorrected = shifted; 
if (shifted > 'z') { 
    shiftCorrected -= alphabetSize; // Would print: 115 
} 

char currentAfterShift = (char) shiftCorrected; // Would print: s 

Ainsi, au lieu de déplacer par 100 nous déplaçons seulement être la partie pertinente, 22. Imaginez le personnage qui passe à trois tours à travers l'alphabet entier parce que 100/26 ~ 3.85. Après ceux trois tours nous allons le reste 0.85 tours qui sont 22 étapes, le reste après avoir divisé 100 par 26. C'est exactement ce que l'opérateur % a fait pour nous. Après 22 étapes, nous pourrions encore dépasser la limite, mais au maximum d'un tour. Nous corrigeons cela en soustrayant la taille de l'alphabet. Donc, au lieu d'aller 22 étapes, nous allons 22 - 26 = -4 étapes qui émule "aller 4 étapes à la fin de l'alphabet, puis à nouveau à 'un' et enfin aller 18 étapes à 's'".