학습 자료

문자열을 자른다 — 슬라이싱과 반복문


Article

AI 서비스를 만든다고 하면 보통 모델부터 떠올린다. 그런데 문서를 다루는 AI 서비스가 가장 먼저 하는 일은 모델과 아무 상관이 없다. 긴 문서를 짧은 조각으로 자르는 것이다.

이번 강의에서 그 자르는 일을 직접 해본다. 라이브러리를 하나도 안 쓰고, 설치할 것도 없다. 파이썬만 있으면 된다.

왜 자르나

모델에게 문서를 통째로 넘길 수는 없다. 넣을 수 있는 글자 수에 한계가 있어서다. 한계가 없다 해도 통째로 넣으면 안 된다. 질문이 「연차 이월」인데 회사 규정 50장을 전부 넘기면, 모델은 그 안에서 답을 찾느라 헤매고 비용도 50장어치가 나간다.

문서 하나가 답이 되기까지
  1. 긴 문서규정집 · 매뉴얼 · 가이드
  2. 자른다이번 강의
  3. 숫자로 바꾼다임베딩
  4. 질문과 가까운 조각을 찾는다유사도
  5. 그 조각만 모델에게 넘긴다답변 생성

첫 칸이 무너지면 나머지가 다 무너진다. 조각이 엉뚱하게 잘리면 그 뒤로 아무리 좋은 모델을 써도 답이 안 나온다.

실습 방식

1강에서 정한 대로다. 코드 블록 하나가 파일 하나이고, 블록 위에 적힌 이름으로 만들어 붙여넣은 뒤 터미널에서 돌린다.

python 20_문자열.py

파일은 전부 ai-course 폴더에 넣는다. 번호 앞자리가 강의 번호라고 보면 된다 — 1강이 10번대, 이번 강의가 20번대다.

문자열은 글자를 한 줄로 세운 것이다

ai-course/20_문자열.py
# 이 시리즈에서 계속 쓸 예제 문서다 - IT 직무 가이드를 짧게 줄였다.
# = 는 "같다"가 아니라 "왼쪽 이름에 오른쪽 값을 넣는다"는 뜻이다.
text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

print(len(text))     # len = length(길이). 글자가 몇 개인지 세어 준다
print(text[0])       # 대괄호에 번호를 넣으면 그 자리의 글자 하나. 0부터 센다
print(text[-1])      # 음수는 뒤에서부터. -1 이 마지막
터미널
  • 110
  • 백
  • .

이 문서는 110글자다. 파이썬에서 한글 한 글자는 그냥 한 글자로 센다. 영어든 한글이든 공백이든 똑같이 1이다.

문자열은 글자를 한 줄로 세워놓은 것이고, 각 자리에 번호가 붙어 있다. 번호는 0부터 시작한다. 첫 글자가 1번이 아니라 0번이다. 프로그래밍을 처음 하면 여기서 한 번씩 걸린다.

번호는 0부터

text = "백엔드 개발자는 ..."

text[0]
백
text[1]
엔
text[2]
드
text[109]
. — 마지막 글자
길이가 110이니 마지막 번호는 109다

마지막 글자를 꺼내려고 109를 세고 있을 필요는 없다. 음수를 쓰면 뒤에서부터 센다. -1 이 마지막, -2 가 그 앞이다. 길이를 몰라도 끝 글자를 꺼낼 수 있다.

슬라이싱 — 여러 글자를 한 번에 꺼낸다

글자 하나가 아니라 구간을 꺼내는 것을 슬라이싱이라고 한다. 대괄호 안에 콜론을 넣고 시작과 끝을 적는다.

ai-course/21_슬라이싱.py
text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

# 대괄호 안에 콜론(:)을 넣으면 하나가 아니라 구간을 꺼낸다.
# 왼쪽이 시작, 오른쪽이 끝이다. 끝 번호는 포함되지 않는다 - 0~19번까지다.
print(text[0:20])

print(text[:20])     # 시작을 비우면 "처음부터". 위와 같다
print(text[90:])     # 끝을 비우면 "끝까지". 몇 글자인지 몰라도 된다

# 끝 번호가 실제 길이(110)를 한참 넘어도 에러가 안 난다. 있는 만큼만 준다
print(text[90:9999])
터미널
  • 백엔드 개발자는 서버와 데이터베이스를
  • 백엔드 개발자는 서버와 데이터베이스를
  • 즘은 클라우드 지식도 함께 요구된다.
  • 즘은 클라우드 지식도 함께 요구된다.

세어보면 첫 줄이 정확히 20글자다. 그리고 끝 번호가 길이를 넘어가도 에러가 안 난다. 셋째 줄과 넷째 줄이 같은 이유다. 이 성질 덕분에 나중에 문서를 자를 때 마지막 조각을 따로 처리하지 않아도 된다.

문서를 30글자씩 자른다

이제 본론이다. 110글자짜리 문서를 30글자씩 잘라본다. 손으로 하면 text[0:30], text[30:60], text[60:90], text[90:120] 네 줄이다. 그런데 문서가 5,000글자면 이 짓을 167번 해야 한다. 그래서 반복문을 쓴다.

ai-course/22_30글자씩.py
text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

# range(시작, 끝, 건너뛰기) 는 숫자를 차례로 만들어 주는 도구다.
#   range(0, 110, 30)  ->  0, 30, 60, 90
#   110 은 "여기까지"가 아니라 "여기 전까지"다. 슬라이싱과 같은 규칙이다.
#
# for A in B: 는 "B 에서 하나씩 꺼내 A 에 넣고, 아래 줄을 반복하라"는 뜻이다.
for start in range(0, len(text), 30):
    # 이 줄 앞의 공백 4칸이 "이 줄은 for 안쪽"이라는 표시다. 문법이다.
    #   start 가 0 이면   text[0:30]
    #   start 가 30 이면  text[30:60]
    # 앞 조각의 끝과 다음 조각의 시작이 같은 숫자라 겹치지도 빠지지도 않는다.
    print(text[start:start + 30])
터미널
  • 백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자
  • 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을
  • 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요
  • 즘은 클라우드 지식도 함께 요구된다.

네 조각이 나왔다. range(0, 110, 30) 이 0, 30, 60, 90 네 개를 만들었고, 마지막 text[90:120] 은 110에서 끊겨 20글자만 나왔다.

반복문이 하는 일
  1. 01

    range(0, len(text), 30) 이 시작 번호를 만든다

    0에서 시작해 30씩 더하면서 110을 넘기 전까지. 0, 30, 60, 90 네 개.

  2. 02

    start 라는 이름에 그 값이 하나씩 들어간다

    첫 바퀴에 0, 둘째 바퀴에 30. 이름은 아무거나 써도 되지만 뜻이 보이는 쪽이 낫다.

  3. 03

    text[start:start + 30] 으로 30글자를 꺼낸다

    start 가 30이면 text[30:60] 이 된다. 앞 조각의 끝과 다음 조각의 시작이 같은 숫자라 겹치지 않는다.

  4. 04

    이게 없으면 아무것도 안 나온다. 자르기는 다 됐는데 보여달라고 한 적이 없는 상태가 된다.

그런데 문제가 보인다

출력을 다시 본다. 첫 조각이 이렇게 끝난다.

조각 경계에서 무슨 일이 일어났나

1번 조각 끝

text[0:30]

  • ... 다룬다. 사용자
  • 「사용자」 뒤에서 뚝 끊겼다
  • 문장이 안 끝났다

2번 조각 시작

text[30:60]

  • 눈에 보이지 않는 곳에서 ...
  • 앞 조각과 이어야 뜻이 산다
  • 「사용자 눈에」가 두 조각에 걸쳤다

「사용자 눈에 보이지 않는 곳」이라는 한 덩어리가 두 조각으로 찢어졌다. 이 상태로 검색을 하면 어떻게 될까. 누가 「사용자 눈에 안 보이는 일이 뭔가요」라고 물으면 답이 두 조각에 나뉘어 있어서 어느 쪽도 온전한 답이 못 된다.

숫자로만 자르면 반드시 이 일이 생긴다. 글자 수는 문장의 사정을 모르기 때문이다.

문단으로 자르기 — split()

문장 끝에 마침표가 있으니, 마침표를 기준으로 자르면 문장이 안 찢어진다. split() 이 그 일을 한다.

ai-course/23_split.py
text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

# text.split(x) 는 "text 를 x 가 나올 때마다 잘라 리스트로 만들라"는 뜻이다.
# 점(.)을 붙여 부르는 것은 "이 문자열에게 시키는 일"이라는 표시다.
#   기준을 ". " (마침표 + 공백)로 준 이유는 문장이 그렇게 끝나기 때문이다.
parts = text.split(". ")

print(parts)
print(len(parts))    # 리스트에 쓰면 "항목이 몇 개인가". 문자열일 때는 글자 수였다
print(parts[0])      # 리스트도 대괄호로 꺼낸다. 0번이 첫 항목인 것도 같다
print(parts[:2])     # 리스트도 콜론으로 구간을 꺼낸다. 0번과 1번, 두 개다
터미널
  • ['백엔드 개발자는 서버와 데이터베이스를 다룬다', '사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다', '주로 쓰는 언어는 자바, 파이썬, 노드다', '요즘은 클라우드 지식도 함께 요구된다.']
  • 4
  • 백엔드 개발자는 서버와 데이터베이스를 다룬다
  • ['백엔드 개발자는 서버와 데이터베이스를 다룬다', '사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다']

대괄호로 감싸인 이것이 리스트다. 문자열 여러 개를 순서대로 담아둔 상자다. 이번엔 문장이 하나도 안 찢어졌다.

셋째 줄과 넷째 줄을 비교해보면 차이가 보인다. 항목 하나를 찍으면 따옴표가 없고, 리스트째 찍으면 따옴표가 붙는다. 파이썬이 「이 안의 것들은 문자열이다」를 표시해주는 것이다.

리스트도 문자열처럼 0번부터 번호가 붙고, 슬라이싱도 똑같이 된다. 문자열은 글자를 담은 줄이고 리스트는 아무거나 담은 줄인데, 다루는 방식이 같다.

조각에 번호를 붙여 보기 좋게 찍는다

조각이 여러 개면 몇 번째인지 알아야 한다. enumerate() 를 쓰면 번호와 값을 같이 준다.

ai-course/24_번호붙이기.py
text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

parts = text.split(". ")

# enumerate(리스트) 는 "번호와 값을 짝지어" 내놓는다.
# 그래서 받는 이름도 두 개다 - i 에 번호, part 에 값이 들어간다.
for i, part in enumerate(parts):
    print(i, part)   # print 에 값을 여럿 주면 공백으로 이어 찍는다

print("---")

for i, part in enumerate(parts):
    # 따옴표 앞의 f 는 "이 문자열 안의 중괄호를 값으로 바꿔라"는 표시다.
    #   {i + 1}      번호가 0부터라 1을 더해 사람이 세는 대로 만든다
    #   {len(part)}  중괄호 안에서는 계산도 함수 호출도 된다
    print(f"{i + 1}번 조각 ({len(part)}글자) : {part}")
터미널
  • 0 백엔드 개발자는 서버와 데이터베이스를 다룬다
  • 1 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다
  • 2 주로 쓰는 언어는 자바, 파이썬, 노드다
  • 3 요즘은 클라우드 지식도 함께 요구된다.
  • ---
  • 1번 조각 (24글자) : 백엔드 개발자는 서버와 데이터베이스를 다룬다
  • 2번 조각 (37글자) : 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다
  • 3번 조각 (22글자) : 주로 쓰는 언어는 자바, 파이썬, 노드다
  • 4번 조각 (21글자) : 요즘은 클라우드 지식도 함께 요구된다.

f-string 은 문자열 안에서 계산까지 된다. {i + 1} 처럼 식을 그냥 넣으면 결과가 들어간다. 길이를 같이 찍어두면 조각이 고른지 한눈에 보인다 — 24, 37, 22, 21 로 하나가 유독 길다.

이번 강의에 나온 것
쓴 것하는 일예
len(x)길이를 잰다len(text) → 110
text[3]3번 글자 하나개
text[-1]뒤에서 첫 글자.
text[0:20]0번부터 19번까지20글자
text[:20]처음부터위와 같다
text[90:]끝까지나머지 전부
range(0, n, 30)0, 30, 60 ... 을 만든다반복문의 재료
text.split(". ")기준 문자로 쪼개 리스트로문장 4개
enumerate(리스트)번호와 값을 같이 준다for i, x in ...
f"{값}"문자열 안에 값을 넣는다f"{n}번"

스스로 해보기

확인 문제 셋
  1. 01

    text 의 가운데 20글자를 꺼내본다

    ai-course/25_연습1.py 로 만든다. 110글자니까 가운데는 45번쯤이다. 숫자를 바꿔가며 원하는 대목이 나올 때까지 해본다.

  2. 02

    30이 아니라 50글자씩 잘라본다

    ai-course/26_연습2.py. 조각 수가 어떻게 달라지나. 찢어지는 자리도 달라졌을 것이다.

  3. 03

    조각 중에 가장 긴 것이 몇 번인지 찾아본다

    ai-course/27_연습3.py. 길이를 찍어놓고 눈으로 찾아도 되고, 코드로 찾아도 된다. 3강에서 이걸 제대로 다룬다.

1번 — 가운데 20글자충분히 고민해본 뒤 꼭 필요한 경우에만 열어보세요

110글자니까 가운데는 55번 근처다. 20글자를 꺼내려면 45번쯤에서 시작하면 가운데가 걸친다.

숫자를 정확히 맞출 필요는 없다. 바꿔가며 원하는 대목이 나오는지 보는 게 목적이다.

text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

print(text[45:65])        # 가운데 20글자
print(len(text[45:65]))

이터를 저장하고 꺼내는 일을 한다. 가 나오고 길이는 20이다.

여기서도 앞뒤가 문장 한가운데에서 잘린 것이 보인다. 어디를 꺼내도 마찬가지다.

2번 — 50글자씩 자르기충분히 고민해본 뒤 꼭 필요한 경우에만 열어보세요

range 의 세 번째 값과 슬라이싱의 폭을 둘 다 50으로 바꿔야 한다. 하나만 바꾸면 겹치거나 빠진다.

text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

for start in range(0, len(text), 50):
    piece = text[start:start + 50]
    print(f"({len(piece)}자) {piece}")

30글자일 때 4조각이던 것이 3조각이 된다. 마지막은 10글자다.

조각이 커지면 개수가 줄고, 찢어지는 자리도 옮겨간다. 없어지지는 않는다.

3번 — 가장 긴 조각 찾기충분히 고민해본 뒤 꼭 필요한 경우에만 열어보세요

길이를 나란히 찍으면 24 · 37 · 22 · 21 이라 1번이 제일 길다.

코드로 찾는 방법도 미리 봐둔다. max 에 key= 를 주면 「무엇을 기준으로 큰지」를 정할 수 있다.

text = "백엔드 개발자는 서버와 데이터베이스를 다룬다. 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다. 주로 쓰는 언어는 자바, 파이썬, 노드다. 요즘은 클라우드 지식도 함께 요구된다."

parts = text.split(". ")

# 길이를 나란히 찍어 눈으로 찾기
for i, p in enumerate(parts):
    print(i, len(p))

# max 에 key= 를 주면 "무엇을 기준으로 큰지" 정할 수 있다
print(max(parts, key=len))

1번 조각(37자) — 사용자 눈에 보이지 않는 곳에서 데이터를 저장하고 꺼내는 일을 한다

key= 는 3강의 sorted(key=...) 와 같은 문법이다. 여기서 한 번 봐두면 그때 낯설지 않다.

정리하면

문자열은 번호가 붙은 글자의 줄이고, 슬라이싱으로 구간을 꺼낸다. 반복문과 합치면 긴 문서를 일정한 크기로 자를 수 있다. 다만 글자 수로만 자르면 문장이 찢어지고, 그래서 문단 단위 자르기와 오버랩이 필요해진다.

이 문제를 눈으로 확인한 것이 이번 강의의 핵심이다. 다음 강의에서는 조각을 담아둔 리스트를 함수로 다루고, 점수를 매겨 상위 3개만 뽑는 법을 배운다. 검색의 마지막 단계다.

Share
  • 파이썬
  • AI
  • RAG
  • 청킹
  • 문자열
  • 슬라이싱
문자열을 자른다 — 슬라이싱과 반복문 — 디코드랩(DCODELAB)