카이사르 암호는 가장 간단한 치환 암호방법으로 암호문을 만드는 것이다.
알파벳을 일정거리를 두어 기존 문자열을 다른 문자로 치환하는 방법이다.
예를 들면 문자에 대해서 오른쪽으로 2칸 이동시키는 치환 암호방법으로 암호문을 만들면
"ABC"는 "CDF"가 된다. 알파벳 대문자를 기준으로 카이사를 암호문을 작성하시오.
public class Quiz3 {
public static void main(String[] args) {
rot13("ILOVEYOU");
}
public static void rot13(String text) {
// 암호문으로 변경한 문자를 담을 StringBuffer 객체를 생성한다.
StringBuffer password = new StringBuffer();
for (int i=0; i<text.length(); i++) {
int target = text.charAt(i);
// 정수 연산으로 문자의 위치를 이동시킬 것이므로 char 객체를 int 타입 변수에 담는다. (상호 형변환 가능)
// 알파벳 대문자가 아닌 경우 종료시킨다.
if (target < 65 || target > 90) {
return;
}
int replacement = target > 77 ? target - 13 : target + 13;
// 정수로 연산했던 replacement를 char타입으로 저장해야함을 유의한다.
password.append((char)replacement);
}
System.out.println(password);
}
}
처음에는 StringBuffer 객체를 따로 생성하지 않고,
아래와 같이 String 객체가 제공하는 replace를 반복해서 이용하려고 했다.
text = text.replace((char)target, (char)replacement);
그러나 replace는 '문자의 내용'을 기준으로 하는 것이기 때문에
같은 알파벳인 문자가 여러 개 있는 상태일 때,
이미 치환을 시킨 문자를 다시 한 번 더 치환시키는 상황이 생겨서 원상복구 되어버린다는 사실을 깨달았다.
예를 들어 보자면
ILOVEYOU에서 0번째에 위치한 I는 첫번째 반복수행에서 V로 치환된다.
3번째에 위치한 V를 I로 치환하려고 할 때,
이미 I에서 V로 치환을 끝낸 0번째의 V도 다시 I로 변해버린다.
인덱스값이 아닌 문자의 내용(알파벳)을 기준으로 치환할 경우 이런 문제가 생긴다.
정상적인 출력값: VYBIRLBH
text.replace()를 사용했을 때의 출력값: ILOIRLOH
따라서 문자의 인덱스값을 기준으로 치환시키기 위하여,
replace 대신
인덱스 순서대로 문자를 저장시킬 수 있는 StringBuffer 객체의 append를 이용했다.
StringBuffer는 바로 문자열처럼 출력이 가능하다.
String객체에 변환한 문자 하나 하나를 + 연산자로 더할 수도 있겠지만,
메모리에 너무 많은 String객체를 생성하게 되는 비효율적인 방법이기 때문에 그렇게 하지는 않았다.
위 풀이와 달리, 문자열이 공백일 경우도 고려한다.
변하지 않는 값에는 final을 붙여줄 수 있다.
StringBuffer와 비슷하게 StringBuilder를 사용할 수 있다.
public class Quizz3 {
public static void main(String[] args) {
rotate("I love you", 12);
}
/**
* 알파벳 대소문자와 공백으로 구성된 문자열을 전달받아서 지정된 칸만큼으로 오른쪽으로 이동된 문자열을 반환한다.
* @param text 원본문자열
* @param shift 이동시킬 칸 수
*/
public static void rotate(final String text, int shift) {
// 지정된 칸 만큼 이동한 위치에 있는 문자을 저장하는 StringBuilder 객체를 생성한다.
StringBuilder stringBuilder = new StringBuilder();
// 대문자 A-Z, 소문자 a-z의 총 갯수를 계산한다.
final int aToZLength = 'z' - 'A' + 1;
final int len = text.length();
for (int i =0; i < len; i++) {
// 문자열에서 지정된 위치의 문자를 조회한다.
char ch = text.charAt(i);
// 조회된 문자열이 공백이면 해당 문자를 변환없이 StringBuilder객체에 저장하고, for문의 증감식으로 이동한다.
if (ch == ' ') {
stringBuilder.append(ch);
continue;
}
// 조회된 문자열을 지정된 칸만큼 이동시킨 문자가 'z'보다 뒤에 있는 문자면 (aToZLength - shift)값만큼 빼고,
// 조회된 문자열을 지정된 칸마큼 이동시킨 문자가 'z'보다 앞에 있거나 같은 위치에 있는 문자면 shift값만큼 더한다.
int value = (ch + shift) > 'z' ? ch - (aToZLength - shift) : ch + shift;
stringBuilder.append((char) value);
}
System.out.println(stringBuilder.toString());
}
}