관리 메뉴

솜씨좋은장씨

[BaekJoon] 3107번 : IPv6 (Python) (feat. ChatGPT) 본문

Programming/코딩 1일 1문제

[BaekJoon] 3107번 : IPv6 (Python) (feat. ChatGPT)

솜씨좋은장씨 2023. 4. 1. 13:38
728x90
반응형

코딩 1일 1문제! 오늘의 문제는 백준의 IPv6 입니다.

 

3107번: IPv6

첫째 줄에 올바른 IPv6 주소가 주어진다. 이 주소는 최대 39글자이다. 또한, 주소는 숫자 0-9, 알파벳 소문자 a-f, 콜론 :으로만 이루어져 있다.

www.acmicpc.net

👨🏻‍💻 문제 풀이 - SOMJANG - 1회차

입력받은 IPv6 문자열을 : 로 split 한 다음

"" 일 경우

-> 0으로만 이루어져 있는 그룹이 있을 경우 그 중 한 개 이상 연속된 그룹을 하나 골라 콜론 2개(::) 로 변경한 경우 이므로

->  "0000" 을 문자열을 split 한 리스트의 길이를 8에서 빼고 + 1 한 값 만큼 full_address 리스트에 넣어줍니다.

이때 :: 을 ":" 으로 split 하면 "" 가 2개 생기므로 첫번째 "" 을 처리했다면

두번째 ""은 별도의 처리를 하지않고 넘어가도록 하였습니다.

👨🏻‍💻 코드 ( Solution ) - SOMJANG - 1회차

def IPv6(ipv6_address):
    ipv6_address = ipv6_address.split(":")
    
    continue_check = False
    
    full_address = []
    
    for address in ipv6_address:
        if address == "" and not continue_check:
            full_address += ['0000' for _ in range(8 - len(ipv6_address) + 1)]
            continue_check = True
        else:
            full_address.append(address.zfill(4))
            
    return ":".join(full_address)

if __name__ == "__main__":
    ipv6_address = input()
    print(IPv6(ipv6_address=ipv6_address))

👨🏻‍💻 문제 풀이 - SOMJANG - 2회차

이렇게 풀고나니!

음 뭔가 python3 기본 모듈 중에 IPv6 를 처리하는게 있지않을까? 싶어 찾아보니

역시나! 존재했습니다.

https://docs.python.org/3/library/ipaddress.html#module-ipaddress

 

ipaddress — IPv4/IPv6 manipulation library

Source code: Lib/ipaddress.py ipaddress provides the capabilities to create, manipulate and operate on IPv4 and IPv6 addresses and networks. The functions and classes in this module make it straigh...

docs.python.org

이 모듈을 사용하여서도 문제를 풀어보았습니다.

import ipaddress

def IPv6(ipv6_address):
    return ipaddress.IPv6Address(ipv6_address).exploded

if __name__ == "__main__":
    ipv6_address = input()
    print(IPv6(ipv6_address=ipv6_address))

아주아주 간단하게! 문제를 풀 수 있었습니다.

👨🏻‍💻 문제 풀이 - ChatGPT - 실패!

이렇게 쉬운 풀이도 있겠다! ChatGPT 가 이정도 문제는 아주아주아주아주아주아주 쉽게 풀겠지? 라는 기대를 품고

문제를 풀어달라고 요청해보았습니다.

하지만....!

결국 문제를 푸는 것은 실패했습니다.

모든 풀이가 ::1 에 대해서 틀린 답을 만들거나 ValueError 같은 에러가 발생하는 코드를 만들어주었습니다.

 

다음은 ChatGPT 와의 대화 입니다.

💻 ChatGPT

아래는 주어진 IPv6 주소를 파싱하여 32자리의 16진수로 변환하여 출력하는 파이썬 코드입니다.

s = input()

# 콜론을 기준으로 문자열을 분리하여 리스트에 저장합니다.
groups = s.split(':')

# 만약 ::가 있다면, ::를 제거하고 그 자리에 0을 추가합니다.
if '' in groups:
    idx = groups.index('')
    groups = groups[:idx] + ['0'] * (9 - len(groups)) + groups[idx+1:]
else:
    groups = ['0'] * (8 - len(groups)) + groups

# 각 그룹을 4자리 16진수로 변환합니다.
groups = [int(g, 16) for g in groups]

# 16진수를 0으로 채워진 4자리 문자열로 변환합니다.
groups = [format(g, '04x') for g in groups]

# 변환된 문자열을 콜론으로 이어붙여서 출력합니다.
print(':'.join(groups))

예를 들어, 입력으로 주어진 "25:09:1985:aa:091:4846:374:bb"라는 IPv6 주소를 파싱하여 32자리의 16진수로 변환하면 "0025:0009:1985:00aa:0091:4846:0374:00bb"가 출력됩니다.

🧑🏻‍💻 SOMJANG

ValueError 가 발생해

---------------------------------------------------------------------------

ValueError Traceback (most recent call last) Input In [19], in <cell line: 14>()

    11 groups = ['0'] * (8 - len(groups)) + groups

    13 # 각 그룹을 4자리 16진수로 변환합니다.

---> 14 groups = [int(g, 16) for g in groups]

    16 # 16진수를 0으로 채워진 4자리 문자열로 변환합니다.

    17 groups = [format(g, '04x') for g in groups]

 

Input In [19], in <listcomp>(.0)

    11 groups = ['0'] * (8 - len(groups)) + groups

    13 # 각 그룹을 4자리 16진수로 변환합니다.

---> 14 groups = [int(g, 16) for g in groups]

    16 # 16진수를 0으로 채워진 4자리 문자열로 변환합니다.

    17 groups = [format(g, '04x') for g in groups] ValueError: invalid literal for int() with base 16: ''

💻 ChatGPT

입력 예시에 "::"가 포함될 수 있기 때문에, 위 코드에서는 그룹들의 끝에 빈 문자열 ''이 추가되는 경우가 발생하여 int() 함수로 16진수로 변환할 때 ValueError가 발생합니다. 이 경우에 대비하여 groups 리스트에서 빈 문자열을 제거해주면 문제를 해결할 수 있습니다. 이를 적용한 수정된 코드는 다음과 같습니다.

ip = input().split(':')
groups = []

for g in ip:
    if g:
        groups.append(g)
    else:
        # '::'인 경우를 처리합니다.
        groups += ['0'] * (8 - len(groups))

# 각 그룹을 4자리 16진수로 변환합니다.
groups = [int(g, 16) for g in groups]

# 16진수를 0으로 채워진 4자리 문자열로 변환합니다.
groups = [format(g, '04x') for g in groups]

# 그룹들을 ':'로 이어서 출력합니다.
print(':'.join(groups))

위 코드를 실행하면 정상적으로 결과가 출력됩니다.

🧑🏻‍💻 SOMJANG

::1 일때 0000:0000:0000:0000:0000:0000:0000:0001 이 되어야하는데 0000:0000:0000:0000:0000:0000:0000:0000:0001 가 됨

💻 ChatGPT

올바른 지적입니다. 이 코드에서는 '::'를 하나의 그룹으로 간주하여 9개 그룹으로 나누게 되어, 0이 7개인 그룹과 0이 1개인 그룹이 추가되어 10개 그룹이 생기게 됩니다. 이를 수정하여 '::'를 하나의 그룹으로 취급하도록 코드를 변경해야 합니다. 아래는 수정된 코드입니다.

# IPv6 주소를 입력받습니다.
ipv6 = input()

# 콜론을 기준으로 그룹을 분리합니다.
groups = ipv6.split(':')

# 각 그룹이 축약된 경우 0을 채워서 4자리로 만듭니다.
for i in range(len(groups)):
    if len(groups[i]) < 4:
        groups[i] = '0' * (4 - len(groups[i])) + groups[i]

# 축약되지 않은 그룹의 개수를 세어서 ::를 포함한 그룹의 개수를 구합니다.
num_full_groups = sum([len(g) == 4 for g in groups])
num_groups = num_full_groups + (2 if '' in groups else 0)

# ::가 없는 경우 그룹 수를 맞추기 위해 0 그룹을 추가합니다.
if '' not in groups:
    groups += ['0000'] * (8 - len(groups))

# ::가 있는 경우 그룹 수를 맞추기 위해 0 그룹을 추가합니다.
elif num_groups < 8:
    groups.remove('')
    groups += ['0000'] * (8 - num_groups + 1)

# 각 그룹을 4자리 16진수로 변환합니다.
groups = [int(g, 16) for g in groups]

# 16진수를 0으로 채워진 4자리 문자열로 변환합니다.
groups = [format(g, '04x') for g in groups]

# 그룹을 콜론으로 연결하여 IPv6 주소를 완성합니다.
ipv6_full = ':'.join(groups)

# 결과를 출력합니다.
print(ipv6_full)

이 코드에서는 '::'을 하나의 그룹으로 간주하여 8개 그룹으로 나누게 되어, 0이 6개인 그룹과 0이 1개인 그룹이 추가되어 9개 그룹이 생기게 됩니다. 따라서 '::1'의 경우 8개 그룹 중 뒤에서 두 번째 그룹에 1이 들어가게 되어, 결과는 0000:0000:0000:0000:0000:0000:0000:0001이 되어야 합니다.

🧑🏻‍💻 SOMJANG

틀렸어 ::1 일 경우 0000:0000:0001:0000:0000:0000:0000:0000 가 아니라 0000:0000:0000:0000:0000:0000:0000:0001 이되어야해

💻 ChatGPT

네, 맞습니다. 제가 실수를 해서 잘못된 출력을 하고 있었습니다. 다음과 같이 수정하시면 됩니다.

address = input().split(':')

# 주소를 8개 그룹으로 나눕니다.
groups = []
for i in range(len(address)):
    if address[i] == '':
        # ::가 나온 경우 남은 그룹 수만큼 0으로 채웁니다.
        groups += ['0'] * (8 - len(address) + 1)
    else:
        # 4자리 16진수로 변환합니다.
        groups.append(format(int(address[i], 16), '04x'))

# 0으로 채워진 4자리 그룹을 먼저 찾아서 ::로 변경합니다.
zeros = [i for i in range(len(groups)) if groups[i] == '0000']
if zeros:
    max_zero_len = max((len(list(v)) for _, v in itertools.groupby(zeros)))
    if max_zero_len > 1:
        zeros_group = [sum((list(v) + [None])[:max_zero_len]) // 2]
        groups = groups[:zeros_group[0]] + ['0'] * (8 - max_zero_len + 1) + groups[zeros_group[-1] + 1:]
    else:
        groups = ['0'] * (8 - len(groups) + 1) + groups

# 16진수를 0으로 채워진 4자리 문자열로 변환합니다.
groups = [g.rjust(4, '0') for g in groups]

# 그룹을 콜론으로 연결합니다.
result = ':'.join(groups)

print(result)

출력 결과는 다음과 같습니다.

입력:
::1
출력:
0000:0000:0000:0000:0000:0000:0000:0001

여기까지 대화를 해보았는데 결국 위 코드도 ::1 에 대해서 

0000:0000:0000:0000:0000:0000:0000:0000:0000:0000:0000:0000:0001

위 결과를 return 하는 것을 보고 내가 문제를 풀라고 해서 잘 풀지 못하는 것일까? 라는 생각이 들어서

이번에는 그냥 Python3 를 활용하여 IPv6 변환 코드를 구현해달라고 해보았습니다.

🧑🏻‍💻 SOMJANG

그냥 위에 문제 설명과 상관없이 축약된 IPv6 를 축약되지 않은 풀 IPv6로 변환하는 파이썬 코드를 알려줄 수 있을까

라고 해보았습니다.

💻 ChatGPT

네, 당연히 알려드릴 수 있습니다! 축약된 IPv6를 축약되지 않은 IPv6 주소로 변환하는 코드는 다음과 같이 작성할 수 있습니다.

def expand_ipv6_address(addr):
    groups = addr.split(':')
    # 각 그룹을 4자리 16진수로 맞춰준다.
    groups = [group.rjust(4, '0') for group in groups]
    # ::을 만나면 0으로 채워진 그룹을 삽입한다.
    if '' in groups:
        index = groups.index('')
        groups[index:index+1] = ['0000'] * (9 - len(groups))
    # 각 그룹을 결합하여 완성된 IPv6 주소를 반환한다.
    return ':'.join(groups)

# 예시
print(expand_ipv6_address('2001:db8:85a3::8a2e:370:7334'))  # 2001:0db8:85a3:0000:0000:8a2e:0370:7334

위 코드는 다음과 같은 단계로 동작합니다.

  1. 입력받은 주소를 그룹으로 나누어 리스트로 저장합니다.
  2. 각 그룹을 4자리 16진수로 맞춰줍니다.
  3. ::을 만나면 0으로 채워진 그룹을 삽입하여 그룹의 수를 8개로 맞춥니다.
  4. 각 그룹을 결합하여 완성된 IPv6 주소를 반환합니다.

이렇게 작성하면, 주어진 IPv6 주소가 축약된 경우에도 정확하게 풀 IPv6 주소로 변환됩니다.

🧑🏻‍💻 SOMJANG

틀렸어

💻 ChatGPT

죄송합니다. 다시 확인해 보았습니다. 위 코드에는 두 가지 문제가 있었습니다.

  1. 마지막 그룹이 생략되지 않은 경우 그룹 갯수가 총 8개가 되도록 0을 추가해주어야 합니다.
  2. 생략된 그룹이 여러개일 경우 :: 표기법을 사용할 때 생략된 그룹의 갯수를 구하고, 그 수만큼 0을 추가해주어야 합니다.

아래는 수정된 코드입니다.

ipv6 = input().split(':')

if len(ipv6) == 8:  # 생략되지 않은 경우
    print(':'.join([i.zfill(4) for i in ipv6]))
else:  # 생략된 그룹이 있는 경우
    null_group_count = ipv6.count('')
    ipv6 = [i if i != '' else '0000' for i in ipv6]
    ipv6_length = len(ipv6)
    null_group = '0000:'
    full_ipv6 = ':'.join(ipv6)
    full_ipv6 = full_ipv6.replace('::', ':' + null_group * (7 - ipv6_length + null_group_count))
    full_ipv6 = full_ipv6.rstrip(':')
    print(full_ipv6)

이 코드는 다음과 같이 작동합니다.

  1. 입력받은 IPv6 주소를 ':'를 기준으로 나눕니다.
  2. 그룹의 갯수가 8개인 경우 각 그룹을 4자리로 채워주고 ':'로 이어서 출력합니다.
  3. 그룹의 갯수가 8개가 아닌 경우 생략된 그룹의 갯수를 구하고, 각 그룹이 4자리가 되도록 0을 채워줍니다.
  4. IPv6 주소를 다시 ':'로 이어붙인 뒤 '::'가 나오는 부분을 찾아서 생략된 그룹의 수에 따라 0을 채워줍니다.
  5. ':'로 끝나는 부분은 0을 추가하지 않아야 하므로, 끝의 ':'를 제거합니다.
  6. 최종적으로 변환된 IPv6 주소를 출력합니다.

계속 틀렸다고 알려주어도 계속 틀린 답변만 알려주어 여기서 그만 두었습니다.

( 위 코드는 ::1 -> 0000:0000:1 )

 

가장 쉽게 풀 줄 알았는데 만우절이어서 그런지 계속 거짓말만 하고 제대로 풀지 못하여 의아했던 문제였습니다.

 

읽어주셔서 감사합니다.

 

🍪 만우절이어서 거짓말 하는거야? - 네!

하지만 이것도 실패!

실패 실패 대실패!

Comments