凯撒密码 Java 程序不能移位超过 23
Caesar Cipher Java Program can't shift more than 23
我正在创建一个程序来执行 Caesar Cipher,它会在我按下回车键时将单词中的字母移位一次,并提示用户再次移位或退出.
在我到达 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");
}
}
你的方法中的转变是如何运作的?好吧,它利用了一个事实,即 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
中编码,并且 table 列出了该编码的所有字符及其特定的十进制数,例如 here.
以下是相关摘录:
这回答了为什么 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: {
将其与 table 中的相关部分进行比较:
所以在 z
之后不再是 a
而是 {
.
那么在谜团解开之后,我们现在来谈谈如何修复它并使您的代码更智能。
您可以简单地检查边界,例如“如果它大于 'z' 的值或小于 'a',然后将其重新放回到正确的范围内”。我们可以使用 %
给出的 模运算符 轻松做到这一点。它将一个数除以另一个数,然后 returns 除法的余数。
下面是我们如何使用它:
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'".
我正在创建一个程序来执行 Caesar Cipher,它会在我按下回车键时将单词中的字母移位一次,并提示用户再次移位或退出.
在我到达 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");
}
}
你的方法中的转变是如何运作的?好吧,它利用了一个事实,即 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
中编码,并且 table 列出了该编码的所有字符及其特定的十进制数,例如 here.
以下是相关摘录:
这回答了为什么 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: {
将其与 table 中的相关部分进行比较:
所以在 z
之后不再是 a
而是 {
.
那么在谜团解开之后,我们现在来谈谈如何修复它并使您的代码更智能。
您可以简单地检查边界,例如“如果它大于 'z' 的值或小于 'a',然后将其重新放回到正确的范围内”。我们可以使用 %
给出的 模运算符 轻松做到这一点。它将一个数除以另一个数,然后 returns 除法的余数。
下面是我们如何使用它:
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'".