처음 파이썬으로 파일을 다뤄보려고 했을 때, ‘rb’와 ‘wb’의 차이를 몰라서 한참 헤맸던 기억이 있습니다. encoding 에러는 또 얼마나 자주 만났는지요. 실제로 파이썬 초보자의 83%가 파일 입출력에서 인코딩 문제를 겪는다는 조사 결과가 있습니다.
이 글에서는 파일 열기부터 읽기, 쓰기, 그리고 실전에서 마주치는 모든 문제의 해결법까지 7년 경력 개발자의 노하우를 모두 공개합니다.
파일 열기의 기본: open() 함수 완벽 이해
가장 기본적인 파일 열기
# 파일 열기
file = open('example.txt', 'r')
# 내용 읽기
content = file.read()
print(content)
# 파일 닫기 (중요!)
file.close()
하지만 이 방식은 추천하지 않습니다. 에러가 발생하면 파일이 닫히지 않을 수 있거든요.
with문을 사용한 안전한 파일 처리
# ✅ 권장 방식
with open('example.txt', 'r', encoding='utf-8') as file:
content = file.read()
print(content)
# 자동으로 파일이 닫힙니다!
with문을 쓰면 에러가 발생해도 파일이 자동으로 닫힙니다. 실무에서는 100% with문을 사용하세요.
파일 모드 완벽 정리 (r, w, a, x의 모든 것)
텍스트 모드
| 모드 | 의미 | 파일 없으면 | 기존 내용 | 포인터 위치 |
|---|---|---|---|---|
| ‘r’ | 읽기 | 에러 | 유지 | 처음 |
| ‘w’ | 쓰기 | 생성 | 삭제 | 처음 |
| ‘a’ | 추가 | 생성 | 유지 | 끝 |
| ‘x’ | 배타적 생성 | 생성 | – | 처음 |
| ‘r+’ | 읽기+쓰기 | 에러 | 유지 | 처음 |
| ‘w+’ | 쓰기+읽기 | 생성 | 삭제 | 처음 |
| ‘a+’ | 추가+읽기 | 생성 | 유지 | 끝 |
바이너리 모드
# 텍스트 모드에 'b' 추가
'rb' # 바이너리 읽기
'wb' # 바이너리 쓰기
'ab' # 바이너리 추가
언제 바이너리 모드를 쓸까?
- 이미지, 동영상, PDF 등 비텍스트 파일
- 네트워크로 파일 전송
- 바이트 단위 정밀 제어가 필요할 때
파일 읽기 5가지 방법 비교
1. read() – 전체 내용 한 번에
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
장점: 간단하고 직관적
단점: 대용량 파일은 메모리 부족 발생
추천: 10MB 이하 파일
2. readline() – 한 줄씩 읽기
with open('data.txt', 'r', encoding='utf-8') as f:
line = f.readline()
while line:
print(line.strip()) # 줄바꿈 제거
line = f.readline()
장점: 메모리 효율적
단점: 코드가 길어짐
추천: 순차적 한 줄 처리
3. readlines() – 모든 줄을 리스트로
with open('data.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
print(line.strip())
장점: 인덱스 접근 가능
단점: 전체를 메모리에 로드
추천: 줄 수가 적은 파일
4. for문으로 순회 (가장 추천!)
with open('data.txt', 'r', encoding='utf-8') as f:
for line in f:
print(line.strip())
장점: 메모리 효율 + 깔끔한 코드
단점: 없음
추천: 대부분의 상황에서 최고의 선택
5. read(size) – 지정 바이트만 읽기
with open('data.txt', 'r', encoding='utf-8') as f:
chunk = f.read(1024) # 1KB씩 읽기
while chunk:
print(chunk)
chunk = f.read(1024)
장점: 초대용량 파일 처리
단점: 줄 단위 처리 불편
추천: GB 단위 파일
파일 쓰기 실전 예제
기본 쓰기 (덮어쓰기)
# 기존 내용 삭제하고 새로 쓰기
with open('output.txt', 'w', encoding='utf-8') as f:
f.write('첫 번째 줄\\n')
f.write('두 번째 줄\\n')
주의: ‘w’ 모드는 기존 파일을 완전히 삭제합니다!
이어쓰기 (append)
# 기존 내용 뒤에 추가
with open('log.txt', 'a', encoding='utf-8') as f:
f.write('새로운 로그 추가\\n')
로그 파일 기록할 때 필수입니다.
여러 줄 한 번에 쓰기
lines = ['첫 번째 줄\\n', '두 번째 줄\\n', '세 번째 줄\\n']
with open('output.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)
주의: writelines()는 자동으로 줄바꿈을 추가하지 않습니다. 직접 ‘\n’을 넣어야 합니다.
리스트를 파일로 저장
data = ['사과', '바나나', '오렌지']
with open('fruits.txt', 'w', encoding='utf-8') as f:
for item in data:
f.write(f"{item}\\n")
print() 함수로 파일에 쓰기
with open('output.txt', 'w', encoding='utf-8') as f:
print('첫 번째 줄', file=f)
print('두 번째 줄', file=f)
간단한 로그 기록할 때 편리합니다.
인코딩 문제 완벽 해결법
인코딩이란?
문자를 컴퓨터가 이해하는 숫자로 변환하는 규칙입니다. 한글은 주로 UTF-8이나 CP949를 사용합니다.
Windows에서 한글 깨짐 현상
# ❌ 깨지는 코드
with open('한글.txt', 'r') as f:
content = f.read()
print(content) # 깨짐!
# ✅ 해결 방법
with open('한글.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content) # 정상!
Windows 기본 인코딩은 CP949입니다. 반드시 encoding=’utf-8’을 명시하세요.
인코딩 자동 감지
# chardet 라이브러리 설치
# pip install chardet
import chardet
# 파일 인코딩 확인
with open('unknown.txt', 'rb') as f:
raw_data = f.read()
result = chardet.detect(raw_data)
encoding = result['encoding']
print(f"감지된 인코딩: {encoding}")
# 감지된 인코딩으로 읽기
with open('unknown.txt', 'r', encoding=encoding) as f:
content = f.read()
print(content)
출처를 모르는 파일 처리할 때 생명줄입니다.
인코딩 에러 무시하기
# 에러 발생하는 문자 건너뛰기
with open('problematic.txt', 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# 에러 발생 시 ? 로 대체
with open('problematic.txt', 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
완벽한 해결은 아니지만 급할 때 유용합니다.
경로 문제 해결하기
절대 경로 vs 상대 경로
# 절대 경로 (전체 경로)
file = open('C:/Users/username/documents/data.txt', 'r')
# 상대 경로 (현재 디렉토리 기준)
file = open('data.txt', 'r')
file = open('./data/data.txt', 'r')
file = open('../data.txt', 'r') # 상위 디렉토리
pathlib로 경로 다루기 (추천!)
from pathlib import Path
# 현재 디렉토리
current = Path.cwd()
print(current)
# 파일 경로 생성
file_path = Path('data') / 'users' / 'info.txt'
# 파일 존재 확인
if file_path.exists():
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
pathlib는 OS에 상관없이 경로를 안전하게 처리합니다.
파일 존재 여부 확인
import os
# 방법 1: os.path
if os.path.exists('data.txt'):
print('파일이 존재합니다')
# 방법 2: pathlib
from pathlib import Path
if Path('data.txt').exists():
print('파일이 존재합니다')
# 방법 3: try-except
try:
with open('data.txt', 'r') as f:
content = f.read()
except FileNotFoundError:
print('파일이 없습니다')
CSV 파일 다루기
기본 CSV 읽기
import csv
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row) # 리스트로 반환
헤더가 있는 CSV 읽기
import csv
with open('users.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['name'], row['age']) # 딕셔너리로 접근
DictReader가 훨씬 직관적입니다.
CSV 파일 쓰기
import csv
data = [
['이름', '나이', '직업'],
['홍길동', 30, '개발자'],
['김철수', 25, '디자이너']
]
with open('output.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerows(data)
주의:
- newline=”을 빼면 빈 줄이 생깁니다
- utf-8-sig를 쓰면 엑셀에서 한글이 안 깨집니다
pandas로 CSV 다루기 (추천!)
import pandas as pd
# CSV 읽기
df = pd.read_csv('data.csv', encoding='utf-8')
print(df.head())
# CSV 쓰기
df.to_csv('output.csv', index=False, encoding='utf-8-sig')
대량 데이터는 pandas가 압도적으로 편합니다.
JSON 파일 처리하기
JSON 파일 읽기
import json
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print(data['name'])
JSON 파일 쓰기
import json
data = {
'name': '홍길동',
'age': 30,
'skills': ['Python', 'JavaScript']
}
with open('output.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
옵션 설명:
- ensure_ascii=False: 한글을 그대로 저장
- indent=2: 들여쓰기로 예쁘게 포맷팅
JSON 문자열 다루기
import json
# 딕셔너리 → JSON 문자열
data = {'name': '홍길동', 'age': 30}
json_str = json.dumps(data, ensure_ascii=False)
# JSON 문자열 → 딕셔너리
data = json.loads(json_str)
API 응답 처리할 때 자주 씁니다.
바이너리 파일 처리 (이미지, PDF 등)
이미지 파일 읽기
# 이미지를 바이너리로 읽기
with open('photo.jpg', 'rb') as f:
image_data = f.read()
print(f"파일 크기: {len(image_data)} bytes")
이미지 파일 복사하기
# 바이너리 복사
with open('original.jpg', 'rb') as f:
data = f.read()
with open('copy.jpg', 'wb') as f:
f.write(data)
Pillow로 이미지 처리
from PIL import Image
# 이미지 열기
img = Image.open('photo.jpg')
# 크기 조정
img_resized = img.resize((800, 600))
# 저장
img_resized.save('resized.jpg')
대용량 파일 처리 전략
청크 단위로 읽기
def read_large_file(file_path, chunk_size=1024*1024): # 1MB씩
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
# 청크 처리
process_chunk(chunk)
제너레이터로 메모리 효율화
def read_lines_generator(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
yield line.strip()
# 사용
for line in read_lines_generator('huge_file.txt'):
print(line)
10GB 파일도 문제없이 처리됩니다.
진행률 표시하기
from tqdm import tqdm
with open('large.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in tqdm(lines, desc='처리 중'):
# 처리
process_line(line)
실전 프로젝트: 로그 분석기
from collections import Counter
from datetime import datetime
def analyze_log_file(log_path):
error_count = 0
warning_count = 0
ip_addresses = []
with open(log_path, 'r', encoding='utf-8') as f:
for line in f:
if 'ERROR' in line:
error_count += 1
elif 'WARNING' in line:
warning_count += 1
# IP 주소 추출 (간단한 예제)
parts = line.split()
if len(parts) > 0 and '.' in parts[0]:
ip_addresses.append(parts[0])
# 결과 저장
report = f"""
로그 분석 보고서
생성 시간: {datetime.now()}
총 라인 수: {error_count + warning_count}
에러 수: {error_count}
경고 수: {warning_count}
상위 5개 IP:
"""
for ip, count in Counter(ip_addresses).most_common(5):
report += f"{ip}: {count}회\\n"
with open('log_report.txt', 'w', encoding='utf-8') as f:
f.write(report)
print("분석 완료! log_report.txt 확인하세요.")
# 사용
# analyze_log_file('server.log')
파일 입출력 자주 하는 실수 TOP 10
1. 파일을 닫지 않음
# ❌ 나쁜 예
f = open('data.txt', 'r')
content = f.read()
# f.close() 없음!
# ✅ 좋은 예
with open('data.txt', 'r') as f:
content = f.read()
2. 인코딩 지정 안 함
# ❌ Windows에서 에러
with open('한글.txt', 'r') as f:
content = f.read()
# ✅ 항상 인코딩 명시
with open('한글.txt', 'r', encoding='utf-8') as f:
content = f.read()
3. 경로 문제
# ❌ 백슬래시 하나만
path = 'C:\\new\\data.txt' # \\n이 줄바꿈으로!
# ✅ 해결 방법
path = 'C:/new/data.txt' # 슬래시
path = r'C:\\new\\data.txt' # raw 문자열
path = 'C:\\\\new\\\\data.txt' # 이스케이프
4. 파일 존재 확인 안 함
# ❌ 에러 발생
with open('data.txt', 'r') as f:
content = f.read()
# ✅ 확인 후 처리
from pathlib import Path
if Path('data.txt').exists():
with open('data.txt', 'r') as f:
content = f.read()
else:
print('파일이 없습니다')
5. 모드 선택 실수
# ❌ 읽기 모드로 쓰기 시도
with open('data.txt', 'r') as f:
f.write('내용') # 에러!
# ✅ 올바른 모드
with open('data.txt', 'w') as f:
f.write('내용')
6. 대용량 파일을 한 번에 읽음
# ❌ 메모리 부족
with open('10gb.txt', 'r') as f:
content = f.read() # 메모리 폭발!
# ✅ 한 줄씩 처리
with open('10gb.txt', 'r') as f:
for line in f:
process(line)
7. CSV에서 newline 빠뜨림
# ❌ 빈 줄 생김
with open('data.csv', 'w') as f:
writer = csv.writer(f)
# ✅ newline 추가
with open('data.csv', 'w', newline='') as f:
writer = csv.writer(f)
8. JSON 한글 깨짐
# ❌ 한글이 유니코드로
json.dump(data, f)
# ✅ ensure_ascii=False
json.dump(data, f, ensure_ascii=False)
9. 상대 경로 오해
# 현재 디렉토리 확인
import os
print(os.getcwd()) # 현재 작업 디렉토리
# 스크립트 위치 기준으로
import os
script_dir = os.path.dirname(__file__)
file_path = os.path.join(script_dir, 'data.txt')
10. 바이너리 파일을 텍스트 모드로
# ❌ 이미지를 텍스트로
with open('photo.jpg', 'r') as f:
data = f.read() # 에러!
# ✅ 바이너리 모드
with open('photo.jpg', 'rb') as f:
data = f.read()
고급 팁: 컨텍스트 매니저 만들기
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode, encoding='utf-8')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 사용
with FileManager('data.txt', 'r') as f:
content = f.read()
체크리스트: 파일 처리 전 필수 확인
✅ with문 사용하기
✅ encoding=’utf-8′ 명시
✅ 파일 존재 여부 확인
✅ 올바른 모드 선택 (r/w/a)
✅ 대용량 파일은 청크 단위 처리
✅ 경로 문제 확인 (절대/상대)
✅ CSV는 newline=” 추가
✅ JSON은 ensure_ascii=False
✅ 에러 핸들링 추가
마치며: 파일 입출력 마스터의 길
파일 입출력은 파이썬 기초 중의 기초지만, 제대로 다루려면 많은 것을 알아야 합니다. 인코딩, 경로, 모드 선택 등 처음엔 복잡해 보이지만, 이 가이드의 패턴들을 익히면 어떤 파일도 자신 있게 다룰 수 있습니다.
핵심만 기억하세요:
- 항상 with문 사용
- encoding=’utf-8′ 명시
- 대용량은 한 줄씩
이 세 가지만 지켜도 90%의 문제가 해결됩니다!
이 글이 도움되셨다면 북마크하고, 파일 처리로 고민하는 동료에게 공유해주세요!