파이썬에서 파일을 다루는 것은 데이터를 저장하고 불러오는 데 필수적인 과정입니다. 파일 작업은 크게 세 단계를 거칩니다. 파일을 열고, 해당 파일을 사용하며, 마지막으로 파일을 닫는 것입니다.
기본 파일 작업 흐름
# 1단계: 파일 열기 (읽기 모드, UTF-8 인코딩)
file_descriptor = open('example.txt', 'r', encoding='utf-8')
# 2단계: 파일 내용 사용 (예: 전체 내용 출력)
print(file_descriptor.read())
# 3단계: 파일 닫기
file_descriptor.close()
파일을 연 후에는 반드시 닫아주어 시스템 자원을 해제해야 합니다. 파이썬의 with 문을 사용하면 파일을 자동으로 닫아주므로 더욱 안전하고 편리합니다.
# with 문을 사용한 자동 파일 닫기
with open('example.txt', 'r', encoding='utf-8') as file_obj:
content = file_obj.readlines()
print(content)
파일 경로 지정 방식
파일 경로를 지정하는 방법은 여러 가지가 있습니다.
# 1. 원시(raw) 문자열 사용: '\'가 특수 문자로 해석되지 않음
file_path_raw = r'C:\Users\User\Documents\data.txt'
with open(file_path_raw, 'r') as f:
pass
# 2. 이스케이프 문자 '\\' 사용
file_path_escaped = 'C:\\Users\\User\\Documents\\data.txt'
with open(file_path_escaped, 'r') as f:
pass
# 3. Unix 스타일 '/' 슬래시 사용 (Windows에서도 호환 가능)
file_path_unix = 'C:/Users/User/Documents/data.txt'
with open(file_path_unix, 'r') as f:
pass
파일 읽기 모드 (텍스트 vs. 이진)
파일을 읽을 때는 텍스트 모드('t')와 이진 모드('b')를 구분하여 사용합니다.
'rt'(기본값): 텍스트 파일 읽기 모드. 문자열 단위로 데이터를 처리합니다.'rb': 이진 파일 읽기 모드. 바이트 단위로 데이터를 처리합니다. 이미지, 오디오 파일 등에서 사용됩니다.
read() 메서드를 이용한 데이터 읽기
read() 메서드는 파일 전체 내용을 한 번에 읽거나, 인자로 전달된 숫자만큼의 문자 또는 바이트를 읽습니다.
# 'rt' 모드: 인자는 문자(character) 수
with open('text_data.txt', 'r', encoding='utf-8') as f:
print(f.read(5)) # 첫 5개 문자 읽기
# 'rb' 모드: 인자는 바이트(byte) 수
# (예시를 위해 'rb'로 열고 바이트로 읽는 척 하지만, 실제로는 text_data.txt는 텍스트 파일이므로 주의)
with open('text_data.txt', 'rb') as f:
print(f.read(5)) # 첫 5개 바이트 읽기
예시 파일 (text_data.txt 내용):
안녕하세요
파이썬 파일
입출력입니다
read() 전체 읽기 예시:
with open('text_data.txt', 'r', encoding='utf-8') as f:
all_content = f.read()
print(all_content)
실행 결과:
안녕하세요
파이썬 파일
입출력입니다
readline() 메서드를 이용한 한 줄 읽기
readline()은 파일을 한 줄씩 읽어옵니다. 인자를 전달하면 해당 줄에서 지정된 문자(바이트) 수만큼 읽습니다.
with open('text_data.txt', 'r', encoding='utf-8') as f:
first_line_partial = f.readline(4) # 첫 줄에서 4개 문자 읽기
print(first_line_partial)
실행 결과:
안녕하
readlines() 메서드를 이용한 전체 줄 읽기
readlines()는 파일의 모든 줄을 읽어와 각각의 줄을 리스트의 요소로 반환합니다. 각 요소는 줄바꿈 문자(\n)를 포함합니다.
with open('text_data.txt', 'r', encoding='utf-8') as f:
all_lines = f.readlines()
print(all_lines)
실행 결과:
['안녕하세요\n', '파이썬 파일\n', '입출력입니다\n']
대용량 파일 효율적으로 읽기
대용량 파일은 read()로 한 번에 읽으면 메모리 문제를 일으킬 수 있습니다. 대신 파일을 작은 청크(chunk) 단위로 나누어 순차적으로 읽는 것이 효율적입니다.
def read_large_file_in_chunks(filepath, chunk_size_chars=1024):
"""
대용량 텍스트 파일을 청크 단위로 읽어 내용을 반환합니다.
"""
total_content = []
try:
with open(filepath, 'r', encoding='utf-8') as file_handle:
while True:
segment = file_handle.read(chunk_size_chars)
if not segment: # 더 이상 읽을 내용이 없으면 반복 종료
break
total_content.append(segment)
except FileNotFoundError:
print(f"오류: '{filepath}' 파일을 찾을 수 없습니다.")
return ""
except Exception as e:
print(f"파일 읽기 중 오류 발생: {e}")
return ""
return "".join(total_content)
# 함수 사용 예시
large_file_path = 'large_document.txt'
# 예시 파일을 생성합니다 (실제 사용 시에는 기존 파일을 사용)
with open(large_file_path, 'w', encoding='utf-8') as f:
for i in range(1000):
f.write(f"This is line {i+1} in the large document.\n")
document_data = read_large_file_in_chunks(large_file_path, 256)
# print(document_data[:500]) # 처음 500자만 출력하여 확인
print("대용량 파일 읽기 완료.")
파일 쓰기: 덮어쓰기 ('wt')
'w' 모드는 파일이 존재하면 기존 내용을 모두 지우고 새 내용을 작성합니다. 파일이 없으면 새로 생성합니다.
target_file = 'output.txt'
# 파일 덮어쓰기
with open(target_file, 'w', encoding='utf-8') as f:
f.write('새로운 내용입니다.\n')
f.write('이전 내용은 사라졌습니다.\n')
# 내용 확인
with open(target_file, 'r', encoding='utf-8') as f:
print(f.read())
실행 결과:
새로운 내용입니다.
이전 내용은 사라졌습니다.
파일 쓰기: 내용 추가 ('at')
'a' 모드는 파일 끝에 내용을 추가합니다. 파일이 없으면 새로 생성합니다.
target_file = 'output.txt'
# 기존 내용이 있다고 가정하고 추가
with open(target_file, 'a', encoding='utf-8') as f:
f.write('이 줄은 추가되었습니다.\n')
f.write('또 다른 줄이 추가됩니다.\n')
# 내용 확인
with open(target_file, 'r', encoding='utf-8') as f:
print(f.read())
실행 결과 (이전 'output.txt' 내용에 이어서):
새로운 내용입니다.
이전 내용은 사라졌습니다.
이 줄은 추가되었습니다.
또 다른 줄이 추가됩니다.
이진 파일 입출력 ('rb', 'wb', 'ab')
텍스트 파일이 아닌 이미지, 동영상, 오디오 파일 등은 이진 모드로 다루어야 합니다.
'rb': 이진 파일 읽기'wb': 이진 파일 덮어쓰기 (쓰기)'ab': 이진 파일 내용 추가 (쓰기)
이진 파일 복사 예시:
import os
source_binary_file = 'input.bin'
destination_binary_file = 'copied.bin'
# 예시를 위한 더미 이진 파일 생성
with open(source_binary_file, 'wb') as f:
f.write(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09' * 100) # 1000 바이트의 더미 데이터
print(f"'{source_binary_file}' 복사 중...")
try:
with open(source_binary_file, 'rb') as src_file:
with open(destination_binary_file, 'wb') as dest_file:
chunk_size = 4096 # 4KB씩 읽고 쓰기
while True:
binary_data = src_file.read(chunk_size)
if not binary_data: # 더 이상 읽을 데이터가 없으면 종료
break
dest_file.write(binary_data)
print(f"'{source_binary_file}'이(가) '{destination_binary_file}'(으)로 성공적으로 복사되었습니다.")
except FileNotFoundError:
print(f"오류: 원본 파일 '{source_binary_file}'을(를) 찾을 수 없습니다.")
except Exception as e:
print(f"파일 복사 중 오류 발생: {e}")
# 복사된 파일 크기 확인 (선택 사항)
if os.path.exists(source_binary_file) and os.path.exists(destination_binary_file):
src_size = os.path.getsize(source_binary_file)
dest_size = os.path.getsize(destination_binary_file)
print(f"원본 파일 크기: {src_size} 바이트")
print(f"복사본 파일 크기: {dest_size} 바이트")
파일 커서(포인터) 제어: seek() 및 tell()
파일 내에서 읽기/쓰기 위치(커서)를 변경하거나 현재 위치를 확인할 수 있습니다.
seek(offset, whence): 커서 위치를 변경합니다.offset: 이동할 바이트 수입니다.whence: 기준 위치를 지정합니다.0(os.SEEK_SET): 파일의 시작 (기본값)1(os.SEEK_CUR): 현재 커서 위치2(os.SEEK_END): 파일의 끝
참고: 텍스트 모드(
't')에서는whence가1또는2일 때offset값은 0이어야 합니다. 즉, 텍스트 모드에서는 파일 시작부터의 절대 위치로만 이동할 수 있으며, 인코딩 문제로 인해 상대적 이동은 지원되지 않습니다. 이진 모드('b')에서는 모든whence값과offset값을 자유롭게 사용할 수 있습니다.tell(): 현재 커서 위치를 바이트 단위로 반환합니다.
예시: 로그 파일 실시간 모니터링 (tail -f와 유사)
import time
import os
log_file = 'application.log'
# 예시 로그 파일 생성
with open(log_file, 'w', encoding='utf-8') as f:
f.write("INFO: Application started.\n")
f.write("DEBUG: Processing user input.\n")
print(f"'{log_file}' 파일 실시간 모니터링 시작... (Ctrl+C로 종료)")
try:
with open(log_file, 'rb') as f_bin: # 이진 모드로 열어야 seek(0, 2)가 제대로 작동
f_bin.seek(0, os.SEEK_END) # 파일의 끝으로 이동
while True:
new_data = f_bin.read()
if new_data:
decoded_data = new_data.decode('utf-8', errors='ignore')
if 'ERROR' in decoded_data or 'CRITICAL' in decoded_data:
print(f"[알림] 오류 감지: {decoded_data.strip()}")
else:
print(f"새 로그: {decoded_data.strip()}")
else:
# 새로운 로그를 시뮬레이션
with open(log_file, 'a', encoding='utf-8') as f_append:
if time.time() % 10 < 2: # 대략 10초에 한 번 ERROR 추가
f_append.write(f"ERROR: Something went wrong at {time.ctime()}.\n")
elif time.time() % 5 < 1: # 대략 5초에 한 번 INFO 추가
f_append.write(f"INFO: Task completed successfully at {time.ctime()}.\n")
time.sleep(1) # 1초마다 새로운 데이터 확인
except KeyboardInterrupt:
print("\n모니터링이 중단되었습니다.")
except Exception as e:
print(f"오류 발생: {e}")
# 생성된 예시 파일 정리
# if os.path.exists(log_file):
# os.remove(log_file)
OS 모듈을 이용한 파일 시스템 관리
파이썬의 os 모듈은 운영체제와 상호작용하여 파일 및 디렉터리 작업을 수행하는 다양한 함수를 제공합니다. (표준 라이브러리 문서)
import os
from pprint import pprint
print("현재 작업 디렉터리:", os.getcwd())
# 디렉터리 목록 가져오기
print("\n현재 디렉터리 내용:")
current_dir_contents = os.listdir('.')
pprint(current_dir_contents)
# 디렉터리 변경 (예시로 상위 디렉터리로 이동 후 다시 돌아옴)
# os.chdir('..')
# print("\n상위 디렉터리로 이동 후:", os.getcwd())
# os.chdir(os.path.dirname(os.path.abspath(__file__))) # 스크립트 위치로 다시 이동
# print("스크립트 디렉터리로 돌아옴:", os.getcwd())
# 새 디렉터리 생성 (이미 존재하면 오류 발생)
new_directory = 'my_new_folder'
if not os.path.exists(new_directory):
os.mkdir(new_directory)
print(f"\n'{new_directory}' 디렉터리 생성.")
else:
print(f"\n'{new_directory}' 디렉터리가 이미 존재합니다.")
# 파일 생성 및 삭제
temp_file = os.path.join(new_directory, 'temp_data.txt')
with open(temp_file, 'w') as f:
f.write("임시 데이터.")
print(f"'{temp_file}' 파일 생성.")
if os.path.exists(temp_file):
os.remove(temp_file)
print(f"'{temp_file}' 파일 삭제.")
# 디렉터리 삭제 (디렉터리가 비어있어야 함)
if os.path.exists(new_directory) and not os.listdir(new_directory):
os.rmdir(new_directory)
print(f"비어있는 '{new_directory}' 디렉터리 삭제.")
elif os.path.exists(new_directory):
print(f"'{new_directory}' 디렉터리가 비어있지 않아 삭제할 수 없습니다.")
# 파일 또는 디렉터리 이름 변경/이동
old_name = 'old_file.txt'
new_name = 'renamed_file.txt'
another_folder = 'another_place'
with open(old_name, 'w') as f:
f.write("이름을 변경할 파일.")
print(f"\n'{old_name}' 파일 생성.")
os.rename(old_name, new_name)
print(f"'{old_name}'을(를) '{new_name}'으로 이름 변경.")
if not os.path.exists(another_folder):
os.mkdir(another_folder)
print(f"'{another_folder}' 디렉터리 생성.")
moved_path = os.path.join(another_folder, 'moved_file.txt')
with open('file_to_move.txt', 'w') as f:
f.write("이동할 파일.")
print("'file_to_move.txt' 파일 생성.")
os.rename('file_to_move.txt', moved_path)
print(f"'file_to_move.txt'을(를) '{moved_path}'(으)로 이동.")
# 정리 (선택 사항)
if os.path.exists(new_name): os.remove(new_name)
if os.path.exists(moved_path): os.remove(moved_path)
if os.path.exists(another_folder) and not os.listdir(another_folder): os.rmdir(another_folder)