본문으로 건너뛰기
김신건의 로그

[Python] str: 문자열과 f-string

· 수정 · 📖 약 2분 · 951자/단어 #python #string #str #f-string #basics
python string, python str, f-string, 문자열 포매팅, string formatting

정의

Python의 str불변(immutable) 유니코드 문자열이다. 내부는 PEP 393 이후 “유연한 문자열 표현(flexible string representation)“으로, 문자열이 사용하는 최대 코드포인트에 따라 1, 2, 4바이트 폭을 자동 선택한다.

리터럴 종류

s1 = 'single quotes'
s2 = "double quotes"
s3 = '''triple
multiline'''
s4 = """triple double"""

# raw string: 이스케이프 미적용
r = r"C:\Users\koa\file.txt"

# bytes (str 아님)
b = b"hello"

# f-string (formatted)
name = "Alice"
f = f"Hello, {name}"

f-string (PEP 498)

Python 3.6+ 의 핵심 포매팅 방식.

python
name = "Alice"
age = 30
pi = 3.14159

print(f"Hello, {name}, age {age}")
print(f"{pi:.2f}")           # 소수점 2자리
print(f"{age:05d}")          # 0 패딩 5자리
print(f"{name:>10}")         # 우측 정렬 10폭
print(f"{name!r}")           # repr() 적용
print(f"{age=}")             # 3.8+: 이름과 값 동시 출력
결과
Hello, Alice, age 30
3.14
00030
   Alice
'Alice'
age=30

f-string 안의 표현식

items = [1, 2, 3, 4]
f"합계: {sum(items)}"          # 임의 표현식 가능
f"{'-' * 20}"                  # 연산도 가능
f"{ {1, 2, 3} }"               # 중괄호 충돌은 공백으로 분리

Python 3.12+ 에서는 같은 인용부호 중첩, 백슬래시, 멀티라인이 허용되었다(PEP 701).

문자열 메서드 핵심

메서드동작예시
.upper() / .lower()대/소문자 변환"abc".upper() == "ABC"
.strip([chars])양끝 공백/문자 제거" x ".strip() == "x"
.lstrip() / .rstrip()왼쪽/오른쪽만
.split(sep)분할 → 리스트"a,b,c".split(",") == ["a", "b", "c"]
.join(iterable)결합",".join(["a", "b"]) == "a,b"
.replace(old, new)치환"aaa".replace("a", "b") == "bbb"
.startswith() / .endswith()접두/접미 검사
.find(sub) / .index(sub)위치, -1 / 예외
.count(sub)출현 횟수
.isdigit() / .isalpha() / .isspace()문자 종류
.title() / .capitalize() / .swapcase()케이스 변환
.zfill(n)0 패딩"7".zfill(3) == "007"
.format(*args, **kwargs)구식 포매팅
python
s = "Hello, World"
print(s.lower())
print(s.replace("World", "Python"))
print(s.split(", "))
print("-".join(["a", "b", "c"]))
print(s.find("World"))
결과
hello, world
Hello, Python
['Hello', 'World']
a-b-c
7

슬라이싱과 인덱싱

문자열은 시퀀스라서 정수 인덱스와 슬라이스가 가능하다.

python
s = "Python"
print(s[0])
print(s[-1])
print(s[1:4])
print(s[::-1])     # 뒤집기
print(s[::2])      # 2칸씩
결과
P
n
yth
nohtyP
Pto

불변성과 연결 성능

# BAD: 매번 새 객체 생성, O(n^2)
result = ""
for s in many_strings:
    result += s

# GOOD: join, O(n)
result = "".join(many_strings)

# 또는 io.StringIO
from io import StringIO
buf = StringIO()
for s in many_strings:
    buf.write(s)
result = buf.getvalue()

CPython 구현 최적화로 += 누적도 빠를 수 있지만 보장되지 않는다. join 사용이 정석.

인코딩

str는 유니코드 추상이고, 실제 바이트는 bytes다.

python
s = "안녕"
b = s.encode("utf-8")
print(b)
print(len(s), "chars,", len(b), "bytes")

# 다시 str로
print(b.decode("utf-8"))
결과
b'\xec\x95\x88\xeb\x85\x95'
2 chars, 6 bytes
안녕

len(str)코드포인트 수, len(bytes)바이트 수다. 한글은 UTF-8에서 코드포인트 1개당 3바이트.

문자열 정규화

같은 글자가 다른 코드포인트 조합으로 표현될 수 있다 (macOS NFD 등).

import unicodedata
nfc = unicodedata.normalize("NFC", text)  # 결합형
nfd = unicodedata.normalize("NFD", text)  # 분해형

파일명/유저 입력 비교 시 정규화하지 않으면 같은 글자도 다르게 보일 수 있다.

PEP 393: 유연한 내부 표현

Python 3.3+ 의 str 객체는 담긴 문자의 최대 코드포인트에 따라 내부 표현을 자동 선택한다.

flowchart TD
    A["str 객체 생성"] --> B{"최대 코드포인트?"}
    B -->|"U+00FF 이하"| C["Latin-1 compact: 1 byte/char"]
    B -->|"U+FFFF 이하"| D["UCS-2: 2 bytes/char"]
    B -->|"U+10FFFF 이하"| E["UCS-4: 4 bytes/char"]
    C --> F["ASCII, Latin 텍스트에 최적"]
    D --> G["BMP 전체: 한글, 한자 포함"]
    E --> H["이모지, 희귀 문자"]
import sys
print(sys.getsizeof("A"))          # ~50 bytes (Latin-1)
print(sys.getsizeof("가"))          # ~76 bytes (UCS-2)
print(sys.getsizeof("\U0001F600")) # ~80 bytes (UCS-4, 이모지)

len(str) 은 항상 코드포인트 수 를 반환한다. 내부 바이트 크기와 다를 수 있다.

textwrap: 긴 텍스트 정리

textwrap 모듈은 긴 문자열을 지정 너비로 줄바꿈하거나 들여쓰기를 정규화할 때 사용한다.

import textwrap

text = "Python 의 str 은 불변 유니코드 시퀀스다. PEP 393 이후 코드포인트에 따라 내부 표현이 자동 선택된다."

# 30자 너비로 자동 줄바꿈
print(textwrap.fill(text, width=30))

# docstring 공통 들여쓰기 제거
raw = """
    첫 번째 줄
    두 번째 줄
"""
print(textwrap.dedent(raw).strip())

# 각 줄 앞에 접두어 추가
print(textwrap.indent("foo\nbar", prefix="  >> "))

자세히는 py-textwrap-locale 참고.

문자열 인터닝

CPython 은 identifier 처럼 생긴 짧은 문자열 리터럴을 자동으로 인터닝(캐시)한다.

a = "hello"
b = "hello"
print(a is b)       # True (같은 객체, CPython 보장)

a = "hello world"   # 공백 포함: 인터닝 보장 안 됨
b = "hello world"
print(a is b)       # CPython 에서도 False 일 수 있음

문자열 동등 비교에는 반드시 ==. is 를 쓰면 SyntaxWarning (Python 3.8+).

명시적 인터닝이 필요하면 sys.intern():

import sys
a = sys.intern("hello world")
b = sys.intern("hello world")
print(a is b)   # True

같은 문자열 키가 사전에 수백만 번 등장할 때 메모리 절약 목적으로 사용한다.

포매팅 방식 비교

방식예시비고
% 연산자"Hello %s" % nameC 스타일, 구식
.format()"Hello {}".format(name)3.0+, 유연한 위치/키워드 인자
f-stringf"Hello {name}"3.6+, 가장 빠름, 권장
string.TemplateTemplate("Hello $name").substitute(...)외부 입력 처리에 안전

f-string 은 CPython 3.12 에서 컴파일 타임 추가 최적화되었다.

인코딩 오류 처리 전략

bad = b'\xff\xfe'

bad.decode('utf-8', errors='strict')           # UnicodeDecodeError (기본)
bad.decode('utf-8', errors='replace')          # U+FFFD 대체 문자로 치환
bad.decode('utf-8', errors='ignore')           # 무효 바이트 삭제
bad.decode('utf-8', errors='backslashreplace') # \xNN 이스케이프 형식

파일 읽기 시 인코딩과 오류 처리 전략을 명시하는 것이 좋은 습관이다:

with open("data.txt", encoding="utf-8", errors="replace") as f:
    content = f.read()

str vs bytes 구분

Python 3 는 str (유니코드 텍스트) 와 bytes (바이트열) 를 엄격히 분리한다.

"text" + b"bytes"    # TypeError: 섞어 쓸 수 없음
len("가")             # 1 (코드포인트)
len("가".encode())    # 3 (UTF-8 바이트)

네트워크 I/O, 바이너리 파일, 이미지 처리에는 bytes / bytearray 사용. 자세히는 py-bytes-bytearray 참고.

정규식 연계

복잡한 패턴 매칭, 치환, 분할이 필요하면 re 모듈을 사용한다. 자세히는 py-re-regex 참고.

import re

# 숫자 추출
nums = re.findall(r'\d+', "order-123, item-456")
# ['123', '456']

# 이메일 마스킹
masked = re.sub(r'[\w.]+@[\w.]+', '***', "user@example.com 에 전송")
# '*** 에 전송'

관련 위키

이 글의 용어 (4개)
[Python] 문자열 포매팅: %, .format, f-string, Templatepython
정의 Python에는 4가지 문자열 포매팅 방법이 있다. 새 코드는 f-string 우선. | 방법 | 도입 | 상태 | |------|------|------| | 포매팅 | …
[Python] bytes, bytearray, memoryviewpython
정의 Python에서 바이너리 데이터는 세 가지 타입으로 다룬다. - : 불변 바이트 시퀀스. 0255 정수의 immutable 배열. - : 가변 바이트 시퀀스. 의 mutab…
[Python] re: 정규 표현식python
정의 모듈은 PCRE 호환 정규 표현식(완전 호환은 아니나 거의 유사) 엔진을 제공한다. 백트래킹 NFA 기반으로 표현력은 강하나 catastrophic backtracking …
[Python] textwrap, locale, unicodedata: 텍스트 처리python
정의 텍스트 처리 표준 모듈 3개. - : 줄바꿈, 들여쓰기 정렬 - : 지역화 (숫자/통화/날짜 포맷) - : 유니코드 정규화·분류·검색 textwrap 긴 텍스트를 일정 폭으…

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기