개발노트

파이썬으로 CSV 파일 가공하기

Happy._. 2025. 6. 20. 10:57
반응형

 

CSV(Comma Separated Value)

  • 쉼표로 나눠진 값을 저장한 데이터
  • *.csv 형식으로 저장
  • 텍스트 기반으로 서식 정보 등이 저장되지 않음
국어,영어,수학
90,80,100
 

 

CSV 파일을 읽고 CSV형 리스트로 만들기(read)

import csv, os

# sample.csv 파일이 있는 경로로 이동
os.chdir('/Users/**/downloads') 

# csv 파일을 읽기 모드로 열기
f = open('sample.csv', 'r') 

# scv 파일이 열리지 않는 경우 인코딩 형식 변경
# 원본 데이터가 utf-8이 아닌 경우 제대로 출력되지 않을 수 있음
# 그럴 땐 인코딩 형식을 cp949로 지정
f = open('sample.csv', 'r', encoding='utf-8') 

# csv.reader() 함수를 사용하여 csv 파일을 읽기
reader = csv.reader(f)

# reader 객체를 사용하여 csv 파일의 내용을 읽기
for line in reader:
    print(line) # 각 행을 출력

# 출력 결과
# ['국어', '영어', '수학']
# ['90', '80', '100']

# 파이썬에서 사용할 수 있도록 CSV 파일의 내용을 리스트로 변환
data = []
for line in reader:
    data.append(line) # 각 행을 리스트에 추가

# 리스트의 내용을 출력
print(data) # []

# csv.reader(f)로 읽은 후에는 파일 포인터가 끝에 위치하므로, 다시 처음으로 이동해야 함
# 파일 포인터를 처음으로 이동, 반환 값 0
f.seek(0) 

# 다시 csv.reader() 함수를 사용하여 csv 파일을 읽기
reader = csv.reader(f)

# csv 파일의 내용을 리스트로 변환
data = []
for line in reader:
    data.append(line) # 각 행을 리스트에 추가

# 리스트의 내용을 출력
print(data) # [['국어', '영어', '수학'], ['90', '80', '100']]
 

 

CSV형 리스트를 CSV 파일로 만들기(write)

import csv

sample_data = [[
    '국어', '영어', '수학'
], [
    '90', '80', '100'
], [
    '80', '70', '90'
], [
    '70', '60', '80'
], [
    '60', '50', '70'
], [
    '50', '40', '60'
]]

# csv 파일을 쓰기 모드로 열기
# w: 쓰기 모드, a: 추가 모드, r: 읽기 모드, r+: 읽기/쓰기 모드
# newline='' : 줄바꿈 문자 처리, 이것을 추가하지 않으면 각 행 사이에 빈 줄이 추가되어 CSV 파일을 열었을 때 별도 편집을 해야 함
f = open('sample.csv', 'w', newline='')

# csv.writer() 함수를 사용하여 csv 파일을 쓰기
# delimiter : 구분자, 기본값은 쉼표(,), 사용할 CSV형 리스트의 원소가 쉼표(,)로 구분되어 있을 때 생략 가능
csvobject = csv.writer(f, delimiter=',')

# writerow() 함수를 사용하여 csv 파일에 한 행씩 쓰기
# writerow() : 한 행을 쓰기, writerows() : 여러 행을 쓰기
# 대부분의 경우 writerows()를 사용하여 여러 행을 한 번에 쓰는 것이 더 효율적
csvobject.writerows(sample_data)

# csv 파일을 닫기
# close() : 파일을 닫기
# close()를 사용하지 않으면 파일이 닫히지 않아서 다른 프로그램에서 파일을 열 수 없음
# close()를 사용하지 않으면 메모리 누수 발생 가능
f.close()
 

 

CSV 파일 ↔ CSV형 리스트 모듈 만들기

import csv

# csv파일을 csv형 리스트로 변환
def opencsv(filename):
    f = open(filename, 'r')
    reader = csv.reader(f)
    output = []
    for line in reader:
        output.append(line)
    return output

# writecsv: 기존 파일에 쓰는 함수, 해당 함수를 사용하는 경우 기존 파일에 새 내용으로 덮어쓰기 됨
# usecsv.writecsv('/users/**/downloads/sample.csv', sample_data)
# mac os의 다운로드 폴더 경로: '/users/**/downloads/*.csv'
def writecsv(filename, data):
    with open(filename, 'w', newline='') as f:
        csvobject = csv.writer(f, delimiter=',')
        csvobject.writerows(data)

# writecsv_newfile: 새로운 파일을 생성하는 함수
def writecsv_newfile(filename, data):
    try:
        with open(filename, 'x', newline='') as f:  # 'x' 모드 사용
            csvobject = csv.writer(f, delimiter=',')
            csvobject.writerows(data)
        print(f"새 파일 '{filename}'이 생성되었습니다.")
    except FileExistsError:
        print(f"오류: '{filename}' 파일이 이미 존재합니다!")
 

 

open() 함수의 모드

  • r: 읽기 모드, 파일이 존재해야 함(기본값)
    • 파일을 열면 커서가 맨 앞에서 시작(seek(0)를 사용하지 않아도 처음부터 읽기/쓰기 가능)
    • seek(0)을 사용하면 원하는 위치에 쓰기 가능
    • 파일이 없으면 오류가 발생(FileNotFoundError)하므로 파일 존재 여부 검사 필요
import os

filename = "/users/**/downloads/sample.csv"
if not os.path.exists(filename):
    print(f"오류: 파일 '{filename}'이 존재하지 않습니다.")
 
  • w: 쓰기 모드, 파일이 존재하면 덮어씀, 없으면 새로 생성
  • x: 새 파일 생성 모드, 파일이 이미 존재하면 오류 발생(FileExistsError)
  • a: 추가 모드, 파일이 존재하면 끝에 내용 추가, 없으면 생성
  • 커서를 이동할 수 없으며, 항상 파일 끝에서만 추가 가능
  • b: 바이너리 모드, 이미지, 동영상, 오디오 등 바이너리 파일 읽기/쓰기
  • 단독으로 사용되지 않고, rb, wb, ab등과 조합해서 사용
  • b 자체는 파일을 이진(Binary) 형식으로 처리하겠다는 의미
  • t: 텍스트 모드, 문자열 파일에 읽고 쓰기(기본값)
  • +: 읽기 및 쓰기 모드, 파일을 읽고 쓸 수 있음(단독 사용 X, r+, w+, a+ 형태로 사용)

💡정리

  • 기존 파일의 내용을 수정하고 싶다면 ⇒ r+
  • 파일 끝에 새로운 데이터를 추가하고 싶다면 ⇒ a+
  • 새로운 파일을 만들고 싶다면 ⇒ a+

 

seek(offset, whence)의 동작방식

seek() 함수는 파일 내에서 특정 위치로 커서를 이동할 수 있도록 두 개의 인자를 받음

  • offset: 이동할 바이트(byte) 수
  • whence(옵션, 기본값 0): 기준 위치
    • 0 → 파일의 시작에서부터 offset만큼 이동
    • 1 → 현재 커서 위치에서 offset만큼 이동
    • 2 → 파일의 끝에서 offset만큼 이동

💡 정리

  • 바이트 단위로 이동하는 경우 한글 깨짐
  • 영어(ASCII)는 1바이트이지만, 한글(UTF-8)은 최소 2바이트 이상을 차지하므로 한글의 일부만 불러오면 깨지므로 문자 단위로 이동해야 함
  • seek()대신 read()로 전체 문자열을 읽거나 바이트모드(rb)에서 decode()를 사용해야 함

 

한글이 깨지지 않게 처리하는 방법

'''
sample.txt의 내용은 다음과 같음
sample data
sample data
sample data
'''

with open("/users/**/downloads/sample.txt", "r", encoding="utf-8") as f:
    content = f.read()  # 전체 문자열을 읽음
    print(content[5:])  # 문자열 기준으로 처리 (바이트 단위 X)

'''
출력결과
e data
sample data
sample data
'''

with open("/users/**/downloads/sample.txt", "rb") as f:
    f.seek(6)  # 특정 바이트 위치 이동
    data = f.read(10)  # 10바이트 읽기
    print(data.decode("utf-8", errors="ignore"))  # UTF-8로 변환 (깨진 문자 무시)

'''
출력결과
 data
samp
'''
 

 

CSV 파일 안의 문자를 숫자로 전환하기

  • 숫자 데이터를 받아 연산을 하고 싶은 경우에는 자료형이 정수(int) 혹은 실수(float)이어야 함
  • 사용자 정의 모듈은 모든 자료형을 문자형(str)로 불러옴
  • 문자형 → 정수형: int()
  • 문자형 → 실수형: float()

💡정리

CSV 파일을 바로 숫자형으로 변경하는 경우 쉼표(,) 때문에 직접 바꿀 수 없음

re.sub() 함수를 사용해 쉼표(,)를 제거 후 int() 또는 float() 함수를 사용해 변경할 수 있음

import re # 정규식 모듈

str = '123,456'

# int(str) # ValueError: invalid literal for int() with base 10: '123,456'

# re.sub(r'\s+', ' ', str)  # replace multiple spaces with a single space
# re.sub(바꿀문자열, 바뀔문자열, 대상문자열)
number = re.sub(',', '', str) # 123456

# change the type of number to int
int(number) 

# check the type of number
type(number) # <class 'str'>
 

 

예외처리

import re

data = ['151,767', ' ', '11,093', '27,394', '123 456']

result = []

# 데이터에 빈 값이 있는 경우 예외 처리 하기
'''
try: 
    실행할 명령문 
except: 
    예외 발생 시 처리할 명령문
'''
for i in data:
    # 쉼표 제거 및 실수형으로 변환
    try: data[data.index(i)] = float(re.sub(',', '', i))
    # 실수형으로 변환할 수 없는 경우 예외 발생
    except:
        pass # pass는 아무것도 하지 않음

# 출력결과: 쉼표 제거 후 실수형으로 변환 가능한 값은 실수형으로 변환, 변환할 수 없는(예외)는 그대로 유지
print(data) # [151767.0, ' ', 11093.0, 27394.0, '123 456']


# pass 키워드를 사용하는 주요 이유
# 1. 임시 코드 작성: 나중에 구현할 함수나 클래스의 구조만 만들고 싶을 때
# 2. 빈 블록 유지: if, while, for 같은 제어문에서 문법 오류 없이 빈 블록을 유지하려고 할 때
# 3. 예외 처리: 특정 상황에서 아무 작업도 수행하지 않도록 만들고 싶을 때
 

 

사용자 정의 모듈 완성 코드

import csv,re

def opencsv(filename):
    f = open(filename, 'r')
    reader = csv.reader(f)
    output = []
    for line in reader:
        output.append(line)
    return output

def writecsv(filename, data):
    with open(filename, 'w', newline='') as f:
        csvobject = csv.writer(f, delimiter=',')
        csvobject.writerows(data)

def convert_csv_to_float(csv_list):
    result = []
    if not isinstance(csv_list, list):
        raise TypeError('입력값은 리스트여야 합니다.')
    if not all(isinstance(i, list) for i in csv_list):
        raise TypeError('리스트의 모든 요소는 리스트여야 합니다.')
    
    for i in csv_list:
        for j in i:
            try: 
                result.append(float(re.sub(',', '', j)))
            except ValueError:
                result.append(j)
    return result
 

 

사용자 정의 모듈 사용방법

  1. 작업 경로에 .py 파일을 복사해 놓고 필요할 때 임포트
  2. os 모듈이나 re 모듈처럼 어떤 폴더에서나 실행하려면 아나콘다가 설치된 폴더 안 Lib 폴더에 저장
  3. 파이썬만 설치한 경우(아나콘다 설치 X) Library/Frameworks/Python.framework/Versions/Current/lib/python3.xx/site-packages 폴더에 저장

 

site-packages 폴더

  • pip install을 통해 설치한 패키지가 저장되는 곳
  • Python이 자동으로 모듈을 찾는 기본 검색 경로 중 하나
  • 필요하면 직접 추가한 사용자 정의 모듈도 이 폴더에 넣어 전역적으로 사용할 수 있음
  • site-packages 폴더의 경로는 설치 방식에 따라 다름
    • 시스템 Python: /Library/Frameworks/Python.framework/Versions/X.X/lib/pythonX.X/site-packages/
    • Homebrew: /usr/local/lib/pythonX.X/site-packages/
    • 사용자 전용 패키지(둘 중 하나 선택)
      • ~/Library/Python/X.X/lib/python/site-packages/
      • python -m site --user-site

 

반응형