본문 바로가기
IT 톺아보기/이런저런 공부

인코딩이란 - 2. base64 Encoding / Decoding

by 파초우 2023. 1. 18.
반응형

이번에 진행한 base64 인코딩/디코딩은 UTF-8을 기준으로 진행했다.
Encoding은 일상 언어를 bits로 변환하는 작업이고, Decoding을 해당 bits를 적절한 언어로 변환하는 작업이다.
기본적으로 문자는 8bits로 이루어져 있고 이를 Encoding을 진행하는 경우, 입력받은 문자를 8bits로 만들고 6bits로 나누어 변환을 진행.
Man을 예시로 Encoding을 진행하면,

Base64 Encoding Algorithm. In the early days of the Internet… | by Anagha  Kumbhojkar | The Startup | Medium

위 그림처럼 작성이 되고 남는 구간은 0으로 채워 넣는다. 그리고 000000의 문자는 "="으로 padding.
Decoding은 위 작업을 반대로 진행하면 된다.
Encoding / Decoding 시에는 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="를 기준으로 작업을 수행하면 된다.
유니코드 Encoding 방식을 이용하여 한글을 Encoding 하는 코드는..

def base64_encode(string):
    bits, result = "", ""
    chunks = [ord(ch) for ch in string]
    for ch in chunks:
        first, second, third, fourth = '','','',''
        if ch < 0x80:
            # 그대로 인코딩 진행
            first = bin(ch & 0x7f)[2:].zfill(8)
        elif 0x80 <= ch < 0x8000:
            # 입력받은 문자 bits xxx xxxx xxxx -> 110xxxxx(first) 10xxxxxx(second)
            first = bin(((ch >> 6) & 0x3) | 0xc0)[2:]
            second = bin((ch & 0x3f) | 0x80)[2:]
        elif 0x8000 <= ch < 0x10000:
            # 입력받은 문자 bits xxxx xxxxx xxxx xxxx -> 1110xxxx(first) 10xxxxxx(second) 10xxxxxx(third)
            first = bin(((ch >> 12) & 0xf) | 0xe0)[2:]
            second = bin(((ch >> 6) & 0x3f) | 0x80)[2:]
            third = bin((ch & 0x3f) | 0x80)[2:]
        elif 0x10000 <= ch <= 0x1FFFFF: 
            # 입력받은 문자 bits x xxxx xxxx xxxxx xxxx xxxx -> 11110xxx(first) 10xxxxxx(second) 10xxxxxx(third) 10xxxxxx(fourth)
            first = bin(((ch >> 18) & 7) | 0xf0)[2:]
            second = bin(((ch >> 12) & 3) | 0x80)[2:]
            third = bin(((ch >> 6) & 0x3f) | 0x80)[2:]
            fourth = bin((ch & 0x3f) | 0x80)[2:]
        bits += first + second + third + fourth

    if len(bits) % 6:
        # 남은 자리 0으로 채우는 작업
        bits += "0" * (6 - len(bits) % 6)
    # 변환 작업
    for idx in range(0, len(bits), 6):
        alpha_val = bits[idx:idx + 6]
        result += alphabet[int(alpha_val, 2)]

    if len(result) % 4:
        result += "=" * (4 - len(result) % 4)

    return result

한글 Encoding을 위한 python code를 구성해봤다. 한글의 유니코드가 0xD7FF까지라 21 bits의 범위는 없어도 상관없다고 판단이 되지만, 만약을 대비하여 코드에 적어두었다.
Decoding의 경우에는 해당 작업을 반대로 하되 한글 문자인지 확인하는 조건문을 추가 해주는 것이 좋다. 한글 문자의 유니코드는 10, 110, 1110이 앞에 붙어 있는 채로 Encoding 되기 때문에 해당 문자로 시작하는지 감지한 후 변환해주면 된다.
아래는 Decoding python code이다.

def base64_decode(strings):

    decoding_val = ""
    words = ""

    # encoding된 문자의 alphabet index를 6bits binary로 저장
    for ch in strings:
        alpha_idx = alphabet.index(ch)
        if alpha_idx < 64:
            decoding_val += bin(alpha_idx)[2:].zfill(6)
        else:
            decoding_val += '000000'

    # 변환된 bits를 8마디씩 잘라 작업을 진행하여 단어를 이어 붙여준다.
    idx = 0
    while idx < len(decoding_val):

        first = decoding_val[idx:idx + 8]
        # 앞이 110으로 시작하는 경우
        if first.startswith('110'):
            second = decoding_val[idx + 8:idx + 16]
            # 110xxxxx 10xxxxxx 경우
            if second.startswith('10'):
                words += chr(int(first[3:] + second[2:], 2))
                idx += 16
            else:
                words += chr(int(first[3:], 2))
                idx += 8
        # 앞이 1110으로 시작하는 경우
        elif first.startswith('1110'):
            second = decoding_val[idx + 8:idx + 16]
            third = decoding_val[idx + 16:idx + 24]
            # 1110xxxx 10xxxxxx 10xxxxxx 경우
            if second.startswith('10') and third.startswith('10'):
                words += chr(int(first[4:] + second[2:] + third[2:], 2))
                idx += 24
            else:
                words += chr(int(first[3:], 2))
                idx += 8
        # 앞이 11110으로 시작하는 경우
        elif first.startswith('11110'):
            second = decoding_val[idx + 8:idx + 16]
            third = decoding_val[idx + 16:idx + 24]
            fourth = decoding_val[idx + 24:idx + 32]
            # 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 경우
            if second.startswith('10') and third.startswith('10') and fourth.startswith('10'):
                words += chr(int(first[4:] + second[2:] + third[2:] + fourth[2:], 2))
                idx += 32
            else:
                words += chr(int(first, 2))
                idx += 8
        # 위 경우에 해당하지 않는 경우
        else:
            words += chr(int(first, 2))
            idx += 8

    return words

해당 코드로 decoding을 진행하면 자음/모음은 안되고 단어는 잘된다.
(그 부분은 한번 검토해봐야할 것 같음)


Encoding /Decoding 작업을 진행하면 이렇게 출력이 된다.

반응형

'IT 톺아보기 > 이런저런 공부' 카테고리의 다른 글

한글 오토마타 만들기 - 2  (0) 2023.01.18
한글 오토마타 만들기 - 1  (1) 2023.01.18
인코딩이란? - 1  (0) 2023.01.17
플로이드-와샬 알고리즘  (0) 2022.08.09
SQLD 끄적임  (0) 2021.09.12