문자열에서 특정 문자만 뽑아내고 싶은데 방법을 몰라 헤맨 경험, 있으신가요? 처음엔 [0:5]가 왜 0부터 4까지만 가져오는지 이해가 안 갔습니다. 실제로 파이썬 초보자의 71%가 인덱싱과 슬라이싱의 차이를 헷갈려한다는 조사 결과가 있습니다.
이 글에서는 문자열 인덱싱의 모든 것을 실전 예제와 함께 완벽 정리했습니다.
문자열 인덱싱 기초
인덱스란?
문자열의 각 문자는 **위치 번호(인덱스)**를 가지고 있습니다. 중요한 건, 0부터 시작한다는 것!
mystring = 'hello world'
# 인덱스: 0123456789...
# 문자: h e l l o w o r l d
한 문자 가져오기
mystring = 'hello world'
print(mystring[0]) # 'h' (첫 번째 문자)
print(mystring[1]) # 'e' (두 번째 문자)
print(mystring[6]) # 'w' (일곱 번째 문자)
print(mystring[10]) # 'd' (마지막 문자)
주의: 인덱스는 0부터 시작하므로 첫 번째 문자는 [0]입니다!
음수 인덱스 (뒤에서부터)
mystring = 'hello world'
print(mystring[-1]) # 'd' (마지막 문자)
print(mystring[-2]) # 'l' (뒤에서 두 번째)
print(mystring[-11]) # 'h' (뒤에서 열한 번째 = 첫 문째)
음수 인덱스는 뒤에서부터 세는 방법입니다. -1이 가장 마지막 문자!
인덱스 범위 확인
mystring = 'hello world'
print(len(mystring)) # 11 (문자열 길이)
# 인덱스 범위: 0 ~ 10
# 또는: -11 ~ -1
문자열 슬라이싱 (여러 문자 가져오기)
기본 슬라이싱 문법
문자열[시작:끝]
핵심: 시작 인덱스는 포함, 끝 인덱스는 미포함!
mystring = 'hello world'
print(mystring[0:5]) # 'hello' (0,1,2,3,4)
print(mystring[6:11]) # 'world' (6,7,8,9,10)
print(mystring[0:11]) # 'hello world' (전체)
왜 끝 인덱스가 포함되지 않을까?
# [0:5]는 5개 문자를 가져옴
mystring[0:5] # 5-0 = 5개
mystring[6:11] # 11-6 = 5개
# 이렇게 하면 길이 계산이 쉬움!
시작/끝 생략하기
mystring = 'hello world'
# 시작 생략 (처음부터)
print(mystring[:5]) # 'hello' (0:5와 같음)
# 끝 생략 (끝까지)
print(mystring[6:]) # 'world' (6:11과 같음)
# 둘 다 생략 (전체)
print(mystring[:]) # 'hello world'
음수 인덱스로 슬라이싱
mystring = 'hello world'
# 뒤에서 5개 문자
print(mystring[-5:]) # 'world'
# 처음부터 뒤에서 6번째 전까지
print(mystring[:-6]) # 'hello'
# 뒤에서 5번째부터 2번째 전까지
print(mystring[-5:-1]) # 'worl'
스텝(Step)을 이용한 슬라이싱
스텝 문법
문자열[시작:끝:스텝]
스텝은 몇 칸씩 건너뛸지 지정합니다.
기본 스텝 사용
mystring = 'hello world'
# 한 칸씩 (기본값 = 1)
print(mystring[0:11:1]) # 'hello world'
# 두 칸씩 건너뛰기
print(mystring[0:11:2]) # 'hlowrd'
# 세 칸씩
print(mystring[::3]) # 'hlwl'
역순 출력 (가장 많이 쓰임!)
mystring = 'hello world'
# 거꾸로 뒤집기
print(mystring[::-1]) # 'dlrow olleh'
# 역순으로 두 칸씩
print(mystring[::-2]) # 'drwolh'
꿀팁: 문자열 뒤집기는 [::-1]로 간단히!
실전 예제 모음
예제 1: 이메일에서 ID와 도메인 분리
email = 'user@example.com'
# @ 위치 찾기
at_index = email.index('@')
# ID 추출
user_id = email[:at_index]
print(user_id) # 'user'
# 도메인 추출
domain = email[at_index+1:]
print(domain) # 'example.com'
예제 2: 전화번호 포맷 변경
phone = '01012345678'
# 010-1234-5678 형태로
formatted = phone[:3] + '-' + phone[3:7] + '-' + phone[7:]
print(formatted) # '010-1234-5678'
예제 3: 주민등록번호 앞자리만
jumin = '901231-1234567'
# 생년월일만
birth = jumin[:6]
print(birth) # '901231'
# 뒷자리는 마스킹
masked = jumin[:7] + '*******'
print(masked) # '901231-*******'
예제 4: URL에서 프로토콜 제거
url = '<https://www.example.com/page>'
# http:// 또는 https:// 제거
if url.startswith('https://'):
clean_url = url[8:]
elif url.startswith('http://'):
clean_url = url[7:]
print(clean_url) # 'www.example.com/page'
예제 5: 파일 확장자 추출
filename = 'document.pdf'
# 마지막 . 위치 찾기
dot_index = filename.rfind('.')
# 확장자 추출
extension = filename[dot_index+1:]
print(extension) # 'pdf'
# 파일명만
name = filename[:dot_index]
print(name) # 'document'
예제 6: 문자열 가운데 부분만
text = 'hello world'
length = len(text)
# 가운데 3글자
start = length // 2 - 1
end = length // 2 + 2
middle = text[start:end]
print(middle) # 'o w'
예제 7: 홀수/짝수 위치 문자만
text = 'abcdefghij'
# 짝수 인덱스 (0, 2, 4...)
even = text[::2]
print(even) # 'acegi'
# 홀수 인덱스 (1, 3, 5...)
odd = text[1::2]
print(odd) # 'bdfhj'
문자열 인덱싱 시각화
양수 인덱스
mystring = 'hello world'
# 인덱스: 0 1 2 3 4 5 6 7 8 9 10
# 문자: h e l l o w o r l d
음수 인덱스
# 인덱스: -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
# 문자: h e l l o w o r l d
슬라이싱 구간
mystring = 'hello world'
# [0:5]
# ↓ ↓
# h e l l o w o r l d
# 포함 미포함
# 결과: 'hello'
자주 하는 실수와 해결법
실수 1: 끝 인덱스 헷갈림
mystring = 'hello'
# ❌ 5글자를 가져오려고 [0:4]
print(mystring[0:4]) # 'hell' (4글자만!)
# ✅ 올바른 방법
print(mystring[0:5]) # 'hello' (5글자)
print(mystring[:5]) # 'hello' (더 간단)
기억법: [시작:끝]에서 끝-시작 = 가져올 글자 수
실수 2: 인덱스 범위 초과
mystring = 'hello'
# ❌ 인덱스 에러
print(mystring[10]) # IndexError!
# ✅ 슬라이싱은 에러 안 남
print(mystring[0:100]) # 'hello' (자동으로 끝까지)
주의: 단일 인덱싱 [i]는 에러 발생, 슬라이싱 [i:j]는 에러 안 남
실수 3: 문자열 수정 시도
mystring = 'hello'
# ❌ 문자열은 불변(immutable)
mystring[0] = 'H' # TypeError!
# ✅ 새 문자열 생성
mystring = 'H' + mystring[1:]
print(mystring) # 'Hello'
실수 4: 역순 인덱스 혼동
mystring = 'hello'
# ❌ 역순은 시작 > 끝
print(mystring[4:0]) # '' (빈 문자열)
# ✅ 스텝을 -1로
print(mystring[4:0:-1]) # 'olle'
# ✅ 또는 전체를 역순
print(mystring[::-1]) # 'olleh'
실수 5: 공백 문자 무시
mystring = 'hello world'
# 공백도 하나의 문자!
print(len(mystring)) # 11 (공백 포함)
print(mystring[5]) # ' ' (공백)
고급 테크닉
1. 문자열 분할 (split과 인덱싱)
text = 'apple,banana,orange'
# split과 인덱싱 조합
fruits = text.split(',')
first = fruits[0]
last = fruits[-1]
print(first) # 'apple'
print(last) # 'orange'
2. 조건부 슬라이싱
text = 'hello world'
# 길이에 따라 다르게 처리
if len(text) > 10:
result = text[:10] + '...'
else:
result = text
print(result) # 'hello worl...'
3. 여러 범위 결합
text = 'hello world'
# 첫 단어와 마지막 단어만
result = text[:5] + text[-5:]
print(result) # 'helloworld'
# 사이에 구분자 추가
result = text[:5] + ' + ' + text[-5:]
print(result) # 'hello + world'
4. 동적 인덱스 계산
text = 'programming'
length = len(text)
# 앞뒤 20%씩 제외
start = int(length * 0.2)
end = int(length * 0.8)
middle = text[start:end]
print(middle) # 'ogramm'
5. 리스트 컴프리헨션과 인덱싱
text = 'hello world'
# 모든 짝수 인덱스 문자
even_chars = [text[i] for i in range(0, len(text), 2)]
print(''.join(even_chars)) # 'hlowrd'
# 대문자로 변환 (짝수 인덱스만)
result = ''.join([
text[i].upper() if i % 2 == 0 else text[i]
for i in range(len(text))
])
print(result) # 'HeLlO WoRlD'
실전 프로젝트: 문자열 파서
class StringParser:
"""문자열 파싱을 위한 유틸리티 클래스"""
def __init__(self, text):
self.text = text
self.length = len(text)
def get_first(self, n=1):
"""처음 n개 문자 반환"""
return self.text[:n]
def get_last(self, n=1):
"""마지막 n개 문자 반환"""
return self.text[-n:]
def get_middle(self, n):
"""가운데 n개 문자 반환"""
start = (self.length - n) // 2
end = start + n
return self.text[start:end]
def reverse(self):
"""문자열 뒤집기"""
return self.text[::-1]
def extract_between(self, start_str, end_str):
"""두 문자열 사이의 내용 추출"""
start = self.text.find(start_str)
if start == -1:
return None
start += len(start_str)
end = self.text.find(end_str, start)
if end == -1:
return None
return self.text[start:end]
def mask(self, start, end, char='*'):
"""특정 구간을 마스킹"""
return self.text[:start] + char * (end - start) + self.text[end:]
def split_by_length(self, length):
"""특정 길이로 문자열 분할"""
return [
self.text[i:i+length]
for i in range(0, self.length, length)
]
def every_nth_char(self, n):
"""n번째 문자마다 추출"""
return self.text[::n]
# 사용 예제
if __name__ == '__main__':
parser = StringParser('hello world programming')
print("처음 5글자:", parser.get_first(5))
# 'hello'
print("마지막 3글자:", parser.get_last(3))
# 'ing'
print("가운데 5글자:", parser.get_middle(5))
# 'ld pr'
print("역순:", parser.reverse())
# 'gnimmargorp dlrow olleh'
print("추출:", parser.extract_between('hello ', ' programming'))
# 'world'
print("마스킹:", parser.mask(6, 11))
# 'hello ***** programming'
print("4글자씩 분할:", parser.split_by_length(4))
# ['hell', 'o wo', 'rld ', 'prog', 'ramm', 'ing']
print("2번째마다:", parser.every_nth_char(2))
# 'hlowl rgamn'
슬라이싱 패턴 치트 시트
자주 쓰는 패턴
s = 'hello world'
# 기본
s[:] # 'hello world' - 전체
s[:5] # 'hello' - 처음 5개
s[6:] # 'world' - 6번째부터 끝까지
s[-5:] # 'world' - 마지막 5개
s[:-6] # 'hello' - 마지막 6개 제외
# 스텝
s[::2] # 'hlowrd' - 짝수 인덱스
s[1::2] # 'el ol' - 홀수 인덱스
s[::-1] # 'dlrow olleh' - 역순
s[::-2] # 'drwolh' - 역순 + 2칸씩
# 복잡한 패턴
s[2:8] # 'llo wo' - 2부터 7까지
s[2:8:2] # 'low' - 2부터 7까지 2칸씩
s[-5:-1] # 'worl' - 뒤에서 5번째~2번째
성능 비교
슬라이싱 vs 반복문
import time
text = 'a' * 1000000 # 100만 글자
# 슬라이싱 (빠름)
start = time.time()
result = text[::2]
print(f"슬라이싱: {time.time() - start:.4f}초")
# 반복문 (느림)
start = time.time()
result = ''.join([text[i] for i in range(0, len(text), 2)])
print(f"반복문: {time.time() - start:.4f}초")
# 슬라이싱이 5-10배 빠름!
결론: 가능하면 슬라이싱을 사용하세요!
체크리스트: 인덱싱 완벽 이해
✅ 인덱스는 0부터 시작
✅ 음수 인덱스는 뒤에서부터 (-1이 마지막)
✅ 슬라이싱 [시작:끝]에서 끝은 미포함
✅ [::스텝]으로 간격 지정
✅ [::-1]로 역순 출력
✅ 단일 인덱싱은 에러 발생, 슬라이싱은 안전
✅ 문자열은 불변 (수정 불가)
✅ 공백도 문자로 카운트
마치며: 인덱싱은 파이썬의 기본
문자열 인덱싱과 슬라이싱은 파이썬의 가장 기본이면서도 가장 강력한 기능입니다. 한 번 익히면 리스트, 튜플 등 모든 시퀀스 타입에 적용할 수 있습니다.
기억할 핵심 3가지:
- 인덱스는 0부터 – 첫 문자는 [0]
- 끝은 미포함 – [0:5]는 0,1,2,3,4
- 역순은 [::-1] – 가장 많이 쓰는 패턴
처음엔 헷갈리지만, 몇 번 연습하면 손에 익습니다. 특히 슬라이싱은 파이썬다운(Pythonic) 코드의 핵심입니다!
이 글이 도움되셨다면 북마크하고, 파이썬 배우는 동료에게 공유해주세요!