코드체계는 문자에 대응되는 숫자를 정한 것이다.
바이트(Byte): 주소가 부여되는 최소 단위지역마다 서로 다른 코드체계를 사용하면 같은 숫자를 서로 다른 문자로 해석할 수 있다. 이런 혼동을 줄이기 위 해 표준 문자 인코딩 체계가 만들어졌다.
# 문자와 코드 값 확인 예시
print(ord('A')) # 65
print(chr(65)) # A
자료에서는 유니코드 Character Set 으로 UCS-2 와 UCS-4를 소개하고, 저장 변수의 크기와 바이트 순서 문제 를 함께 설명한다.
핵심 같은 여러 바이트 데이터라도 저장 순서를 서로 다르게 해석하면 전혀 다른 값으로 읽힐 수 있다.
| 방식 | 자료의 설명 | 크기 |
|---|---|---|
| UTF-8 | Web 에서 주로 사용 | MIN 8-bit, MAX 32-bit (1Byte × 4) |
| UTF-16 | Windows, Java | MIN 16-bit, MAX 32-bit (2Byte × 2) |
| UTF-32 | Unix | 32-bit (4Byte × 1) |
강의자료는 PyCharm 등의 상태 표시줄에서 파일 인코딩과 줄바꿈 형식을 확인할 수 있음을 예로 든다.
문자열(String)은 문자들이 순서대로 나열된 데이터이다. 문장, 단어, 기호 등의 텍스트를 표현하거나 처리할 때사용한다.
| 언어 | 자료의 핵심 설명 |
|---|---|
| C | 문자 배열로 문자열을 저장하며 문자열 끝에 널 문자가 필 요하다. strlen(), strcpy(), strcmp() 등의 함수 사용 |
| Java | String 클래스로 문자열을 다루고 +, length(), replace(), split(), substring() 등을 제공 |
| Python3 | 유니코드 기반 문자열을 사용하며 인덱싱•슬라이싱•문자열 메 서드를 사용할 수 있다. 문자열은 immutable |
replace(), split(), isalpha(), find() 등의 메서드를 제공한다.immutable 자료형이다.s = "abc"
print(s[1]) # b
print(s[0:2]) # ab
# s[0] = "A" # TypeError: 문자열 요소는 직접 변경할 수 없음
작은따옴표(’‘), 큰따옴표(““), 삼중따옴표(”’ ““,”“” “““)로 문자열을 만들 수 있다.print('ab' + 'c') # abc
print('ab' * 3) # ababab
text = input()
input()은 입력된 한 줄을 문자열로 읽어 들인다. 공백이 포함되어 있어도 한 줄 전체가 문자열에 저장된다.
text = list(input())
# 예: Hello -> ['H', 'e', 'l', 'l', 'o']
각 문자를 리스트의 원소로 따로 다루고 싶을 때 사용할 수 있다.
N = int(input())
arr = [input() for _ in range(N)]
각 줄을 문자열 한 개로 저장한다.
N = int(input())
arr = [list(input()) for _ in range(N)]
문자 지도처럼 각 칸의 문자를 직접 확인하거나 변경해야 할 때 사용하기 편하다.
text = input()
for char in text:
if char == "Z":
print("Z 가 존재합니다.")
문자열 뒤집기
문자열을 역순으로 재정의하는 것이다.
s = "Reverse this strings"
s = s[::-1]
print(s) # sgnirts siht esreveR
리스트로 변환한 뒤 reverse()를 사용하고 다시 문자열로 합칠 수도 있다.
s = "abcd"
s = list(s)
s.reverse()
s = "".join(s)
print(s) # dcba
기러기, 토마토처럼 앞에서 읽어도 뒤에서 읽어도 같은 문자열을 회문이라고 한다. 문자열 길이의 절반만 비교하면 된다.
def is_palindrome(txt):
# 앞쪽 절반만 확인
for i in range(len(txt) // 2):
# 앞쪽 문자와 뒤쪽 문자가 다르면 회문이 아님
if txt[i] != txt[len(txt) - 1 - i]:
return False
return True
인덱스 핵심 앞쪽 와 뒤쪽 - i]를 짝지어 비교한다.
문자열 비교: == 와 is
str1 = "abc"
str2 = "abc"
str4 = str1
str5 = str1[:2] + "c"
print(str1 = str2) # True
print(str1 is str2) # 실행 환경에 따라 같은 객체로 재사용 될 수 있음
print(str4 == str5) # True
print(str4 is str5) # False 가 될 수 있음
주의 문자열 내용 비교에는 ==를 사용한다. is 는 값 비교용이 아니라 객체 동일성 비교용이다.
사전 순서 비교
문자열은 <, > 연산자로 사전 순서를 비교할 수 있으며 자료에서는 유니코드 값을 기준으로 비교한다고 설명한다.
def my_strcmp(str1, str2):
if str1 < str2:
return -1
elif str1 > str2:
return 1
else:
return 0
# 문자열 -> 숫자
a = int("123")
b = float("3.14")
c = int("A0", 16) # 문자열 A0를 16진법으로 해석
# 숫자 -> 문자열
d = str(123)
e = str(3.14)
패턴 매칭은 본문 문자열(Text) 안에서 찾고 싶은 문자열(Pattern)이 존재하는지, 존재한다면 어느 위치에 있는지 찾는 과정이다.
| 기호 | 의미 |
|---|---|
| T 또는 t | 본문 문자열(Text) |
| P 또는 p | 찾을 패턴(Pattern) |
| N | 본문 길이 |
| M | 패턴 길이 |
강의자료에서는 고지식한 패턴 검색, KMP, 보이어-무어 알고리즘을 비교한다.
본문 문자열의 처음부터 끝까지 순회하면서 패턴의 문자들을 일일이 비교하는 가장 단순한 방법이다.
def brute_force(p, t):
# p: 찾을 패턴, t: 본문 문자열
i = 0
j = 0
M = len(p)
N = len(t)
while j < M and i < N:
if t[i] != p[j]:
# 시작 위치의 다음 칸으로 이동하도록 복원
i = i - j
j = -1
i += 1
j += 1
if j = m:
return i - M # 검색 성공: 시작 인덱스
else:
return -1 # 검색 실패
자료의 예에서는 길이 10,000 인 문자열에서 길이 80 인 패턴을 찾는 경우 최악 약 800,000 번 비교가 필요할수 있다고 설명한다.
KMP 는 Knuth, Morris, Pratt 세 사람의 이름에서 유래한 문자열 검색 알고리즘이다.
LPS 는 Longest Prefix which is also Suffix 의 약자로, 패턴의 각 위치까지 보았을 때 접두사(prefix)와 접미사 (suffix)가 같은 최대 길이를 저장한다. 자료에서는 next 또는 pi 배열이라고도 설명한다.
패턴: a b c d a b c e f
LPS : 0 0 0 0 1 2 3 0 0
역할 비교 중 실패했을 때 패턴 인덱스를 무조건 0 으로 되돌리지 않고, LPS 값을 이용해 다음 비교 위치를 정한 다.
보이어-무어(Boyer-Moore)는 실제 문자열 검색 소프트웨어에서도 채택되는 빠른 검색 알고리즘으로 소개된다.
패턴의 오른쪽 끝 문자와 본문의 문자가 불일치할 때, 그 본문 문자가 패턴 안에 존재하는지 확인해서 이동 거리 를 정한다.
자료의 ‘rithm’ 패턴 예에서는 다음과 같은 이동 값을 사용한다.
| 문자 | r | i | t | h | m | 그외 |
|---|---|---|---|---|---|---|
| Skip | 4 | 3 | 2 | 1 | 0 | 5 |
| 알고리즘 | 비교 방향 / 핵심 | 자료의 복잡도 설명 |
|---|---|---|
| Brute Force | 왼쪽부터 단순 비교, 실패 시 한 칸 이동 | O(MN) |
| KMP | LPS 배열로 이미 비교한 정보를 재사용 | |
| Boyer-Moore | 오른쪽부터 비교, Skip 으로 크게 이동 | 최악 가능, 일반적으로 비교 횟 수가 적음 |
기억법 Brute Force = 처음부터 / KMP = 앞에서 얻은 정보 재사용 / Boyer-Moore = 오른쪽부터 보고 크게 점프
시저 암호 (Caesar Cipher)
율리우스 시저가 사용했다고 알려진 암호 방식으로, 평문에 사용된 알파벳을 일정한 문자 수만큼 평행 이동시켜암호화한다.
키 = 1
A -> B
B -> C
C -> D
...
자료의 예에서는 “SAVE PRIVATE RYAN”을 일정한 키 값으로 이동한 암호문을 보여준다. 암호화할 때 사용한이동 값을 키(Key)라고 한다.
전사 공격 (Brute Force Attack)
시저 암호는 가능한 키의 수가 적기 때문에 키 값을 하나씩 모두 적용해 원문이 나오는지 확인할 수 있다.
키 1 2 적용
키 25 적용
단일 치환 암호
각 알파벳 하나를 다른 고정된 알파벳 하나로 대응시키는 방식이다. 시저 암호처럼 일정한 거리로 이동하는 것이아니라 문자 변환표 자체를 사용한다.
A -> Q
B -> H
C -> C
D -> B
...
가능한 키의 수
알파벳 26 개를 서로 다르게 배치하는 경우의 수는 다음과 같다.
26 x 25 × 24 × ... × 1 = 26!
자료에서는 초당 매우 많은 키를 적용하더라도 전부 확인하는 데 매우 긴 시간이 걸린다고 설명한다.
빈도 분석
전사 공격이 어려워도 언어의 통계적 특성을 활용할 수 있다. 영어 평문에서 자주 나오는 문자를 추정해 암호문에서 빈도가 높은 문자와 대응시키는 방식이다.
Run-Length Encoding (RLE)
같은 값이 몇 번 반복되는지를 나타내는 방식이다. 반복이 많은 데이터에서 효과적이다.
원본: ABBBBBBBBA
압축: A1B8A1
대표적인 압축 방법으로, 등장 빈도에 따라 서로 다른 길이의 코드를 부여한다.
결과적으로 전체 데이터의 평균 코드 길이를 줄이는 방식이다.
문자열 뒤집기
s[::-1]
회문 간단 확인
s == s[::-1]
문자열 찾기
s.find("abc")
문자열 치환
s.replace("a", "b")
문자열 분리
s.split()
리스트 → 문자열
"".join(arr)
문자 코드
ord('A')
chr(65)
문자열 → 숫자
int("123")
float("3.14")
int("A0", 16)
숫자 → 문자열
str(123)