pandas로 CSV를 읽었더니 우편번호·사번·전화번호 앞자리 0이 사라질 때, dtype=str로 막는 법
pd.read_csv로 읽었더니 우편번호 06236이 6236으로, 사번 00123이 123으로, 전화번호 01000000123이 1000000123으로 바뀌었다면, 열을 숫자로 판단해서 읽었기 때문입니다. 오류도 경고도 없이 값이 바뀌고, 그 상태로 to_csv로 저장하면 바뀐 값이 파일에 남습니다. 읽을 때 dtype=str을 주면 글자 그대로 읽습니다. 아래에 재현 파일과 코드, 실행 결과를 실었습니다.
증상
고객 네 명의 우편번호, 사번, 전화번호, 금액이 든 CSV입니다. 전화번호는 실제로 쓰이지 않는 010-0000-xxxx 꼴로 만들었습니다. 원본 파일은 이렇습니다.
이름,우편번호,사번,전화번호,금액
김민준,06236,00123,01000000123,45000
이서연,04524,00457,01000000457,27500
박지후,48058,10025,01000001025,36000
최수아,01234,00009,01000000009,16500
아무 옵션 없이 읽고, 같은 내용을 다시 저장해 보았습니다.
import pandas as pd
df = pd.read_csv("고객.csv")
print(df.to_string())
print()
print(df.dtypes.to_string())
df.to_csv("저장본_기본.csv", index=False, encoding="utf-8")
print(open("저장본_기본.csv", encoding="utf-8").read())
이름 우편번호 사번 전화번호 금액
0 김민준 6236 123 1000000123 45000
1 이서연 4524 457 1000000457 27500
2 박지후 48058 10025 1000001025 36000
3 최수아 1234 9 1000000009 16500
이름 str
우편번호 int64
사번 int64
전화번호 int64
금액 int64
--- 저장본_기본.csv ---
이름,우편번호,사번,전화번호,금액
김민준,6236,123,1000000123,45000
이서연,4524,457,1000000457,27500
박지후,48058,10025,1000001025,36000
최수아,1234,9,1000000009,16500
Windows 11, Python 3.14.7, pandas 3.1.0에서 나온 출력입니다.
세 번째 줄 박지후의 값은 그대로입니다. 앞에 0이 없는 값은 바뀔 것이 없으니 일부 줄만 멀쩡해 보입니다. 이 때문에 위쪽 몇 줄만 확인하면 놓치기 쉽습니다. 아래쪽 dtypes에서 우편번호, 사번, 전화번호가 int64로 나와 있는 것이 단서입니다. 이 열들은 계산에 쓰는 숫자가 아니라 이름표인데 숫자로 읽혔습니다.
원인
read_csv는 열의 값이 모두 숫자로 보이면 정수 열로 읽습니다. 정수 06236은 6236과 같은 값이라서 앞의 0을 따로 기억하지 않습니다. 파일에 적힌 글자와 숫자로 바뀐 값이 다를 때, 그 차이가 읽는 단계에서 사라집니다. 공식 문서의 dtype 항목은 str 또는 object를 쓰면 "preserve and not interpret dtype", 곧 값을 해석하지 않고 보존한다고 설명합니다.
해결: dtype=str
모든 열을 글자로 읽는 방법입니다.
import pandas as pd
df = pd.read_csv("고객.csv", dtype=str)
print(df.to_string())
print()
print(df.dtypes.to_string())
df["금액"] = df["금액"].astype(int)
print()
print("금액 합계:", df["금액"].sum())
df.to_csv("저장본_str.csv", index=False, encoding="utf-8-sig")
print(open("저장본_str.csv", encoding="utf-8-sig").read())
이름 우편번호 사번 전화번호 금액
0 김민준 06236 00123 01000000123 45000
1 이서연 04524 00457 01000000457 27500
2 박지후 48058 10025 01000001025 36000
3 최수아 01234 00009 01000000009 16500
이름 str
우편번호 str
사번 str
전화번호 str
금액 str
금액 합계: 125000
--- 저장본_str.csv ---
이름,우편번호,사번,전화번호,금액
김민준,06236,00123,01000000123,45000
이서연,04524,00457,01000000457,27500
박지후,48058,10025,01000001025,36000
최수아,01234,00009,01000000009,16500
앞자리 0이 그대로이고, 저장한 파일도 원본과 같습니다. dtype=str은 금액까지 글자로 읽으므로, 계산할 열은 위처럼 astype(int)로 다시 숫자로 바꿔야 합니다. 바꾸지 않고 sum()을 하면 숫자가 아니라 글자가 이어 붙습니다.
열이 많다면 앞자리 0이 필요한 열만 지정하는 편이 간단합니다.
df = pd.read_csv("고객.csv", dtype={"우편번호": str, "사번": str, "전화번호": str})
이름 str
우편번호 str
사번 str
전화번호 str
금액 int64
금액 dtype: int64 / 합계: 125000
금액은 숫자로 남아 합계가 바로 나옵니다. 열 이름이 파일과 한 글자라도 다르면 지정이 적용되지 않으니, 위처럼 dtypes를 출력해 눈으로 확인하시기 바랍니다.
이미 숫자로 읽은 뒤에는
벌써 숫자로 읽어서 앞자리 0이 없어졌다면, 자릿수가 늘 같은 열에 한해 zfill로 채울 수 있습니다. 우편번호는 5자리, 사번은 5자리, 휴대전화 번호는 11자리인 경우입니다.
lost = pd.read_csv("고객.csv")
fixed = lost.copy()
fixed["우편번호"] = fixed["우편번호"].astype(str).str.zfill(5)
fixed["사번"] = fixed["사번"].astype(str).str.zfill(5)
fixed["전화번호"] = fixed["전화번호"].astype(str).str.zfill(11)
print(fixed.to_string())
이름 우편번호 사번 전화번호 금액
0 김민준 06236 00123 01000000123 45000
1 이서연 04524 00457 01000000457 27500
2 박지후 48058 10025 01000001025 36000
3 최수아 01234 00009 01000000009 16500
원본을 dtype=str로 읽은 결과와 같았습니다. 다만 이 방법은 원래 자릿수를 알 때만 맞습니다. 사번 자릿수가 사람마다 다르거나 지역번호가 있는 전화번호가 섞여 있으면 채워 넣은 값이 원래와 달라질 수 있습니다. 가능하면 처음부터 dtype=str로 읽는 쪽이 안전합니다.
빈 칸이 있을 때
사번 칸이 빈 줄이 하나 끼면 일이 하나 더 생깁니다. 이 경우는 따로 시험했습니다.
from io import StringIO
import pandas as pd
text = "이름,사번\n김민준,00123\n이서연,\n박지후,10025\n"
a = pd.read_csv(StringIO(text))
print(a.to_string())
print(a.dtypes.to_string())
print("문자로 바꾸고 5자리로 채우면:", a["사번"].astype(str).str.zfill(5).tolist())
이름 사번
0 김민준 123.0
1 이서연 NaN
2 박지후 10025.0
이름 str
사번 float64
문자로 바꾸고 5자리로 채우면: ['123.0', nan, '10025.0']
빈 칸은 NaN이고, NaN은 정수가 될 수 없어 열 전체가 소수(float64)가 됩니다. 앞자리 0을 잃는 데다 123.0이라는 값까지 생기고, zfill(5)를 해도 123.0은 5자리를 넘어서 그대로입니다. 복구 코드를 쓰기 전에 dtypes부터 보시기 바랍니다. dtype=str로 읽으면 이렇게 됩니다.
이름 사번
0 김민준 00123
1 이서연 NaN
2 박지후 10025
이름 str
사번 str
빈 칸 확인: [False, True, False]
값은 글자로 보존되고, 빈 칸은 NaN으로 남습니다. 공식 문서도 dtype을 지정해도 결측값 처리는 바뀌지 않는다고 적고 있습니다. 빈 칸을 다른 값으로 바꾸고 싶으면 읽은 뒤에 fillna로 따로 처리하면 됩니다.
엑셀로 CSV를 열 때
같은 일이 엑셀에서도 생긴다는 글이 많습니다. 엑셀 쪽은 Microsoft 공식 문서의 설명을 옮깁니다. 텍스트 또는 CSV 파일 가져오기·내보내기 문서에 따르면 엑셀은 .csv 파일을 열 때 현재의 기본 데이터 서식 설정으로 각 열을 해석합니다. 열 서식을 직접 정하고 싶으면 텍스트 가져오기 마법사를 쓰면 되고, 이 문서는 마법사의 용도로 앞자리 0이 있는 숫자 열을 텍스트로 바꿔 0을 보존하는 일을 듭니다. 마법사를 강제로 띄우려면 파일 확장자를 .csv에서 .txt로 바꾼 뒤 열라고 안내합니다.
pandas로 저장한 CSV를 엑셀에서 열었을 때 0이 보이는지는 위 문서만으로 단정할 수 없으니, 엑셀에서 열어 확인하시기 바랍니다. 파이썬 안에서 읽을 때는 dtype=str로 읽으면 0이 사라지지 않습니다.
AI에게 맡길 때
AI에게 CSV를 읽는 코드를 부탁하면 pd.read_csv("파일.csv")만 쓰는 경우가 흔합니다. 받은 코드에 dtype이 없다면 우편번호, 사번, 전화번호, 사업자번호처럼 계산하지 않는 번호 열이 있는지 먼저 보시기 바랍니다. 있다면 "번호처럼 쓰는 열은 dtype을 글자로 지정해서 읽고, 읽은 뒤 dtypes를 출력해 달라"고 요청하면 됩니다.