2017-02-13 6 views
1

Я создаю программу для создания кода Caesar Cipher, который меняет буквы одним словом, когда я нажимаю Enter, и предлагает пользователю снова переключиться или уволиться.Программа Caesar Cipher Java не может сдвинуться более чем на 23

Он работает до тех пор, пока я не доберусь до 23 смены, после чего он начинает использовать символы, отличные от буквы, по какой-то причине, и я не уверен, почему это происходит.

Любые предложения? Вот код:

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

Вы отлаживали свой код? –

+0

'... plaintext + = shift; } else {plaintext + = shift; } '- Это не имеет смысла. Вы можете поместить выражение 'plaintext + = shift' за пределы' else'. –

+1

Просто чтобы вы знали - прошлое '' Z'' и '' z'' - это несколько символов, которые не имеют никакого отношения к буквам. Вы захотите пропустить их. – Makoto

ответ

0

Как работает смена вашего метода? Хорошо, он использует тот факт, что char может на Java также рассматриваться как простое число int.

Из-за того, что вы можете делать такие вещи, как это:

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 

или даже что:

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 

Хорошо, теперь, когда мы знаем, как мы можем интерпретировать char, как int, пусть мы анализируем, почему 65 представляет персонажа A и почему 133 ничего толкового.

Ключевое слово: UTF-16. Символы в Java кодируются в UTF-16, и есть таблицы, которые перечисляют все символы этой кодировки с их определенным десятичным числом, например here.

Вот соответствующий отрывок:

UTF-16 table showing characters around 'A'

Это отвечает, почему 65 представляет A и почему 133 ничего смысла.


Причина, почему вы испытываете странные результаты после некоторых сдвигов является то, что алфавит имеет только размер 26 символов.

Я думаю, вы могли бы ожидать, что он начнется снова и снова, и a смещен 26 снова 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: { 

Сравните это с соответствующей части в таблице:

UTF-16 table showing characters around '{'

Так после z не приходит a снова, а скорее {.


Итак, после того, как тайна была решена, давайте теперь поговорим о том, как ее исправить и сделать ваш код умнее.

Вы можете просто проверить границы, как «, если это больше, чем значение для« z »или меньше, чем« a », а затем вернуть его обратно в правильный диапазон». Мы можем сделать это легко с помощью оператора modulo, предоставленного %. Он делит число на другое и возвращает остаток от деления.

Вот как мы можем использовать его:

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 

Таким образом, вместо перехода на 100 мы только смещаться быть соответствующая часть, 22. Представьте себе персонаж, идущий три раунда через весь алфавит, потому что 100/26 ~ 3.85. После этих три раунда мы идем оставшиеся 0.85 раундов, которые 22 шагов, тем Остаточных после деления 100 на 26. Именно это сделал нам оператор %.

После того, как мы выполнили шаги 22, мы все равно можем превысить границу, но максимально на один раунд. Мы исправим это, вычтем размер алфавита. Итак, вместо того, чтобы идти 22 шагов, мы идем 22 - 26 = -4 шагов, которые эмулируют «, идущий 4 шага в конец алфавита, затем снова начинающийся с« a »и, наконец, идущий на 18 шагов до« s »».