- Published on
Pandas read_csv UnicodeDecodeError 5분 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버/로컬에서 CSV를 읽다가 갑자기 UnicodeDecodeError: 'utf-8' codec can't decode byte ...가 터지면, 대부분은 “파일이 UTF-8이 아니다”에서 끝나지 않습니다. 실제 현장에서는 CP949(EUC-KR 계열), UTF-8 with BOM, 부분적으로 깨진 바이트, 따옴표/구분자 문제로 인한 오탐이 섞여 원인을 헷갈리게 만듭니다.
이 글은 Pandas read_csv의 UnicodeDecodeError를 5분 안에 해결하기 위한 순서(진단 → 가설 → 조치)를 제공합니다.
Pandas 관련 경고를 함께 다루고 싶다면: pandas SettingWithCopyWarning 확실히 없애는 법
1) 에러 메시지로 1차 분류하기
에러는 보통 아래 형태 중 하나입니다.
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x.. in position ..: invalid start byte- UTF-8로 디코딩하려는데 UTF-8이 아닌 바이트가 존재
UnicodeDecodeError: 'cp949' codec can't decode byte 0x..- CP949로 읽으려는데 UTF-8이거나, 깨진 바이트가 존재
UnicodeDecodeError: ...: unexpected end of data- 파일이 잘렸거나(다운로드 중단), 멀티바이트 문자가 중간에 끊김
여기서 핵심은 “어떤 코덱으로 읽으려 했는지”와 “어떤 바이트에서 실패했는지”입니다.
2) 가장 빠른 해결책: 후보 인코딩 3개로 재시도
한국어 CSV에서 가장 흔한 조합은 다음 3개입니다.
utf-8utf-8-sig(UTF-8 BOM 포함)cp949(또는euc-kr)
아래 코드는 5분 해결용으로 가장 먼저 돌려볼 만한 패턴입니다.
import pandas as pd
path = "data.csv"
for enc in ["utf-8", "utf-8-sig", "cp949", "euc-kr"]:
try:
df = pd.read_csv(path, encoding=enc)
print("OK:", enc, df.shape)
break
except UnicodeDecodeError as e:
print("FAIL:", enc, e)
utf-8-sig가 통과하면: 엑셀/윈도우 툴이 BOM을 붙인 케이스가 많습니다.cp949가 통과하면: 윈도우 환경에서 저장된 CSV일 가능성이 큽니다.
이 단계에서 80%는 끝납니다.
3) 그래도 안 되면: 파일 앞부분 바이트로 BOM/텍스트 여부 확인
인코딩을 “찍어 맞추기” 전에, 파일이 정말 텍스트 CSV인지부터 확인해야 합니다. 간혹 확장자만 CSV이고 실제로는 다른 포맷이거나(예: XLSX), 바이너리 조각이 섞인 경우도 있습니다.
from pathlib import Path
path = Path("data.csv")
raw = path.read_bytes()
print("size:", len(raw))
print("head bytes:", raw[:32])
자주 보이는 시그니처:
- UTF-8 BOM:
b'\xef\xbb\xbf'로 시작 - XLSX:
b'PK'로 시작(ZIP 기반)
만약 b'PK'로 시작하면, 그건 CSV가 아니라 엑셀 파일일 가능성이 큽니다. 이 경우 read_csv가 아니라 read_excel을 써야 합니다.
import pandas as pd
df = pd.read_excel("data.csv") # 파일이 실제로 xlsx인데 확장자가 csv인 경우도 있음
4) “인코딩은 맞는데도” 실패할 때: 깨진 바이트/혼합 인코딩 처리
현업에서 은근히 많이 만나는 케이스가 대부분은 CP949인데 일부 행에 UTF-8 조각이 섞였거나, 반대로 UTF-8인데 일부가 깨진 상태입니다. 이런 경우는 “정확한 복구”가 목적이냐, “일단 로딩”이 목적이냐에 따라 전략이 달라집니다.
4-1) 일단 로딩이 목적: errors 옵션으로 대체/무시
Pandas read_csv는 내부적으로 파이썬 파일 핸들을 사용할 수 있으므로, open(..., errors=...)를 직접 제어하면 됩니다.
import pandas as pd
path = "data.csv"
with open(path, "r", encoding="cp949", errors="replace") as f:
df = pd.read_csv(f)
# errors="ignore"는 더 과감하지만 데이터 손실이 커질 수 있음
replace: 디코딩 불가 문자를�(replacement char)로 대체ignore: 디코딩 불가 바이트를 제거(조용히 데이터 손실)
로그/리포트 목적이라면 replace가 현실적인 타협점입니다.
4-2) 정확한 복구가 목적: 문제 행을 찾아 분리
먼저 바이트 단위로 읽고, 줄 단위 디코딩을 시도해 실패한 줄을 분리합니다.
from pathlib import Path
path = Path("data.csv")
raw_lines = path.read_bytes().splitlines()
bad = []
for i, line in enumerate(raw_lines, start=1):
try:
line.decode("cp949")
except UnicodeDecodeError:
bad.append(i)
print("bad line count:", len(bad))
print("first bad lines:", bad[:10])
이후 해당 라인 주변을 별도 파일로 떼어내서 원본 생성 시스템(엑셀, DB export, 외부 벤더)에서 재추출하는 게 가장 깔끔합니다.
5) UnicodeDecodeError로 보이지만 사실은 CSV 파싱 문제인 경우
UnicodeDecodeError만 보고 인코딩만 만지다가 시간을 버리는 경우가 있습니다. 실제로는 다음 문제들이 “디코딩 에러처럼” 보이거나, 인코딩 해결 후 곧바로 다른 에러로 이어집니다.
5-1) 구분자(delimiter)가 콤마가 아니다
한국 환경에서 ; 탭, | 등을 쓰는 파일이 종종 있습니다.
import pandas as pd
# 세미콜론 구분
df = pd.read_csv("data.csv", encoding="cp949", sep=";")
# 탭 구분
df = pd.read_csv("data.csv", encoding="utf-8-sig", sep="\t")
5-2) 따옴표/이스케이프가 깨져 엔진이 흔들리는 경우
행 중간에 "가 비정상적으로 들어가면 파서가 꼬일 수 있습니다. 이때는 engine="python"으로 바꿔서 우회 진단을 해볼 수 있습니다(속도는 느리지만 관대합니다).
import pandas as pd
df = pd.read_csv(
"data.csv",
encoding="cp949",
engine="python",
on_bad_lines="skip" # pandas 버전에 따라 동작 상이
)
on_bad_lines="skip"는 “깨진 행 무시”로 빠른 진행이 가능하지만, 반드시 스킵된 행 수를 기록하세요.
6) 운영 관점: 재발 방지를 위한 표준화 체크리스트
한 번 해결하고 끝내면 같은 문제가 반복됩니다. 데이터 파이프라인에서 다음을 표준으로 잡아두면 UnicodeDecodeError가 크게 줄어듭니다.
6-1) 출력 시스템에서 UTF-8로 고정하고 BOM 정책을 명확히
- 내부 파이프라인: 가능하면
utf-8로 통일 - 엑셀 호환이 중요:
utf-8-sig로 저장(엑셀이 BOM을 선호하는 경우가 있음)
파이썬에서 저장할 때 예시:
df.to_csv("export.csv", index=False, encoding="utf-8-sig")
6-2) 입력 단계에서 “인코딩 탐지 + 로깅”을 공통 유틸로
서비스/배치에서 CSV를 받을 때, 단순히 실패하면 끝내지 말고 다음을 로그로 남기면 트러블슈팅 시간이 줄어듭니다.
- 파일 크기
- 첫 32바이트(hex)
- 시도한 인코딩 목록과 성공/실패
- 실패한 라인 번호(가능하면)
6-3) 데이터 품질: 깨진 바이트를 허용할지 정책 결정
- “리포트/통계용”이면
errors="replace"허용 - “정산/계약/법적 데이터”면 깨진 바이트 발견 시 즉시 실패하고 재수급
7) 5분 해결 플로우 요약
utf-8,utf-8-sig,cp949순서로read_csv재시도- 실패하면 파일 헤더 바이트 확인(텍스트/엑셀/바이너리 오판 제거)
- 혼합/깨짐 의심 시
open(..., errors="replace")로 일단 로딩 - 정확한 복구가 필요하면 실패 라인 탐지 후 원본 재추출
- 구분자/따옴표 문제까지 점검(
sep,engine)
부록) 실전용 함수: 인코딩 자동 시도 + 안전 로딩
아래는 팀에서 공용으로 두기 좋은 형태의 유틸입니다.
import pandas as pd
def read_csv_safely(path, *, encodings=None, **kwargs):
encodings = encodings or ["utf-8", "utf-8-sig", "cp949", "euc-kr"]
last_err = None
for enc in encodings:
try:
return pd.read_csv(path, encoding=enc, **kwargs)
except UnicodeDecodeError as e:
last_err = e
# 최후의 수단: cp949로 강제 + 깨진 문자 대체
with open(path, "r", encoding="cp949", errors="replace") as f:
return pd.read_csv(f, **kwargs)
df = read_csv_safely("data.csv")
print(df.head())
이 유틸을 쓰면 “대부분은 자동으로 해결하되, 최후에는 대체 문자로라도 로딩”하는 전략을 일관되게 적용할 수 있습니다.
CSV 인코딩 문제는 한 번 겪으면 계속 반복됩니다. 중요한 건 감으로 때려 맞추는 게 아니라, 시도 순서와 로그를 표준화해서 다음 장애 때 1~2분 만에 결론을 내릴 수 있게 만드는 것입니다.