https://www.acmicpc.net/problem/3107
IPv6은 길이가 128비트인 차세대 인터넷 프로토콜이다.
IPv6의 주소는 32자리의 16진수를 4자리씩 끊어 나타낸다. 이때, 각 그룹은 콜론 (:)으로 구분해서 나타낸다.
예를 들면, 다음과 같다.
2001:0db8:85a3:0000:0000:8a2e:0370:7334
32자리의 16진수는 사람이 읽고 쓰기에 불편하고, 대부분의 자리가 0이기 때문에 아래와 같이 축약할 수 있다.
2001:db8:85a3:0:00:8a2e:370:7334
2001:db8:85a3::8a2e:370:7334
올바른 축약형 IPv6주소가 주어졌을 때, 이를 원래 IPv6 (32자리의 16진수)로 복원하는 프로그램을 작성하시오.
입력
첫째 줄에 올바른 IPv6 주소가 주어진다. 이 주소는 최대 39글자이다. 또한, 주소는 숫자 0-9, 알파벳 소문자 a-f, 콜론 :으로만 이루어져 있다.
출력
첫째 줄에, 입력으로 주어진 IPv6의 축약되지 않은 형태를 출력한다.
예제 입력 1
25:09:1985:aa:091:4846:374:bb
예제 출력 1
0025:0009:1985:00aa:0091:4846:0374:00bb
예제 입력 2
::1
예제 출력 2
0000:0000:0000:0000:0000:0000:0000:0001
축약하는 규칙 2가지 중 첫 번째(앞자리 0 생략)에 따른 복원은 그냥 생략된 개수만큼 0을 추가해주면 되니 어렵지 않다. 문제는 두 번째 규칙인 ::
을 어떻게 처리하느냐인 것 같다.
::
을 제외한 나머지 부분은 콜론 1개(:
)로만 구분되어 있으므로 전체 IPv6 주소를 일단 ::
를 기준으로 한 번 쪼갠 다음 쪼개진 각 부분을 다시 :
로 분할해야겠다고 생각했다.
예를 들어 입력이 25:09:1985:aa::374:bb
라고 주어졌다면,
우선 ::
를 기준으로 나누면 25:09:1985:aa
와 374:bb
로 분할된다.
25:09:1985:aa
은 4개의 그룹으로, 374:bb
은 2개의 그룹으로 이루어져 있다. 그런데 그룹은 총 8개여야 하므로 25:09:1985:aa
와 374:bb
사이에 0만으로 이루어진 그룹이 2개 있음을 알 수 있다.
따라서, 크기가 8인 String 배열 result
의 처음 부분(result[0], result[1], result[3])에 25:09:1985:aa
를 차례대로 넣고, 끝 부분에 374:bb
(result[6], result[7])를 넣은 후 그 사이에 존재하는 빈 공간(result[4], result[5])에는 "0000"
을 채워넣으면 된다. 그리고 각 그룹을 순회하며 문자열의 길이가 4 미만인 경우에는 앞에 생략된 0을 필요한 개수만큼 채워주면 복원이 완료된다.
구현할 때 가장 신경 쓰였던 부분은 구분자가 문자열의 처음이나 끝에 존재할 때 java의 String.split() 메소드가 어떻게 동작하느냐였다.
예를 들어 문제에 주어진 두 번째 예제 입력 ::1
에 대해 .split("::")를 호출할 때, ["", "1"]과 같이 빈 문자열을 포함한 길이가 2인 String 배열을 반환할지 아니면 그냥 ["1"]을 반환할지에 대해 확신이 없었다.
결론만 말하면,
1. 구분자가 문자열의 맨 앞에 존재할 때는 반환된 배열의 첫 번째 요소가 빈 문자열이 된다.
2. 구분자가 문자열에 마지막에 존재할 때는 반환된 배열의 마지막 요소가 빈 문자열이 된다.
그래서 split한 결과가 빈 문자열인 경우는 따로 처리가 필요했다.
.split("::")
를 호출했을 때 반환받은 배열의 요소를 result 배열에 추가할 때, 빈 문자열의 경우는 추가하지 않고 스킵해주었다.
import java.io.*;
class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String original = br.readLine();
String[] temp = original.split("::");
String[] result = new String[8];
if (temp.length == 1) {
String[] left = temp[0].split(":");
for (int i = 0; i < left.length; i++) {
result[i] = left[i];
}
} else {
String[] left = temp[0].split(":");
String[] right = temp[1].split(":");
for (int i = 0, j = 0; i < left.length; i++) {
if (left[i].isEmpty()) {
continue;
}
result[j++] = left[i];
}
for (int i = right.length - 1, j = 7; i >= 0; i--) {
if (right[i].isEmpty()) {
continue;
}
result[j--] = right[i];
}
}
for (int i = 0; i < 8; i++) {
if (result[i] == null) {
result[i] = "0000";
continue;
}
if (result[i].length() != 4) {
StringBuilder sb = new StringBuilder(result[i]);
while (sb.length() < 4) {
sb.insert(0, '0');
}
result[i] = sb.toString();
}
}
System.out.println(String.join(":", result));
}
}
처음에는 original.split("::");
의 반환 값인 temp의 크기가 무조건 2 이상일 거라고 생각해서 temp의 크기가 1일 때를 따로 고려하지 않았었는데, 입력이 ::로 주어진 경우 "::"를 구분자로 split했을 때 빈 문자열 하나만 요소로 갖는 배열이 리턴되기에 temp의 크기가 1인 경우도 존재했다. 그래서 이 경우를 따로 처리해주는 코드를 추가했다.