PEP 305 – CSV 파일 API
- Author:
- Kevin Altis <altis at semi-retired.com>, Dave Cole <djc at object-craft.com.au>, Andrew McNamara <andrewm at object-craft.com.au>, Skip Montanaro <skip at pobox.com>, Cliff Wells <LogiplexSoftware at earthlink.net>
- Discussions-To:
- Csv list
- Status:
- Final
- Type:
- Standards Track
- Created:
- 26-Jan-2003
- Python-Version:
- 2.3
- Post-History:
- 31-Jan-2003, 13-Feb-2003
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
쉼표로 구분된 값(CSV) 파일 형식은 스프레드시트와 데이터베이스에서 가장 일반적으로 사용되는 가져오기 및 내보내기 형식입니다. 많은 CSV 파일은 구문 분석이 간단하지만, 이 형식은 안정적인 사양으로 공식 정의되어 있지 않으며, line.split(",")와 같은 방식으로 CSV 파일의 행을 구문 분석하면 결국 실패할 수밖에 없을 만큼 미묘한 특성이 있습니다. 이 PEP는 CSV 파일을 읽고 쓰기 위한 API를 정의합니다. 이 API를 구현하는 해당 모듈도 함께 제공됩니다.
할 일 (관심 있고 의욕적인 분들을 위한 참고 사항)
- 파일 객체를 생성자에 전달하는 선택에 대한 더 나은 근거가 필요합니다. 다음을 참조하십시오: https://mail.python.org/pipermail/csv/2003-January/000179.html
- 유니코드. 으흠.
적용 영역
이 PEP는 한 가지 일을 잘 수행하는 것, 즉 다양한 필드 구분자, 인용 문자, 인용 이스케이프 메커니즘 및 줄 끝을 사용할 수 있는 표 형식 데이터를 구문 분석하는 것에 관한 것입니다. 저자들은 제안된 모듈이 이 한 가지 구문 분석 문제를 효율적으로 해결하도록 하려고 합니다. 저자들은 다음과 같은 관련 주제를 다루려고 하지 않습니다.
- 데이터 해석 (문자열 “10”을 포함하는 필드는 문자열, 실수 또는 정수여야 합니까? 이는 10진수, 16진수 또는 2진수의 숫자입니까? 따옴표 안의 숫자는 숫자입니까, 문자열입니까?)
- 로케일별 데이터 표현 (숫자 1.23은 “1.23”, “1,23” 또는 “1 23”으로 기록해야 합니까?) – 이는 향후 다루어질 수 있습니다.
- 고정 너비 표 형식 데이터 - 이미 신뢰성 있게 구문 분석할 수 있습니다.
근거
CSV 파일은 필드를 구분하는 쉼표를 기준으로 한 줄씩 읽고 분할해도 될 만큼 단순하게 형식이 지정되는 경우가 많습니다. 읽는 데이터가 모두 숫자인 경우에는 특히 그렇습니다. 이 방법은 한동안 작동할 수 있지만, 누군가 데이터에 쉼표 같은 예상치 못한 것을 넣으면 결국 뒤통수를 칠 수 있습니다. 문제를 파고들다 보면 정규 표현식을 사용하여 해결할 수 있다는 결론에 이르게 될 수도 있습니다. 이 방법도 한동안 작동하다가 어느 날 아무런 설명 없이 이상하게 깨질 것입니다. 문제가 커지므로 더 깊이 파고들다 보면 결국 이 형식을 위해 특별히 만들어진 구문 분석기가 필요하다는 사실을 깨닫게 됩니다.
CSV 형식은 잘 정의되어 있지 않으며, 구현마다 미묘한 예외 상황이 많이 존재합니다. 약어의 “V”가 “Values”가 아니라 “Vague”를 의미한다는 말도 있습니다. 서로 다른 구분자와 인용 문자는 시작에 불과합니다. 일부 프로그램은 각 구분자 뒤에 공백을 생성하며, 이 공백은 뒤따르는 필드의 일부가 아닙니다. 다른 프로그램들은 포함된 인용 문자를 두 번 사용하여 인용하고, 또 다른 프로그램들은 이스케이프 문자로 앞에 붙여 인용합니다. 일을 처리하는 기묘한 방식의 목록은 끝이 없어 보일 수 있습니다.
이러한 모든 가변성 때문에 프로그래머가 해당 소스와 프로그램을 철저히 이해하지 않고서는 여러 소스에서 가져온 CSV 파일을 안정적으로 구문 분석하거나 특정 외부 프로그램에 입력하도록 설계된 CSV 파일을 생성하기가 어렵습니다. 이 PEP와 이를 수반하는 소프트웨어는 이 과정을 덜 취약하게 만들려고 합니다.
기존 모듈
이 문제는 이전에도 다루어진 적이 있습니다. 현재 Python 커뮤니티에서 사용할 수 있는 모듈 중 최소 세 가지가 프로그래머가 CSV 파일을 읽고 쓸 수 있도록 합니다:
각각 API가 다르므로 프로그래머가 이들 사이에서 전환하기가 다소 어렵습니다. 더 큰 문제는 이들이 CSV의 일부 경계 사례를 서로 다르게 해석한다는 점일 수 있으며, 따라서 서로 다른 모듈 API 간의 차이를 극복한 후에도 프로그래머는 패키지 간의 의미적 차이도 처리해야 합니다.
모듈 인터페이스
이 PEP는 세 가지 기본 API를 지원합니다. 하나는 CSV 파일을 읽고 구문 분석하기 위한 것이고, 하나는 CSV 파일을 쓰기 위한 것이며, 하나는 리더와 라이터에 서로 다른 CSV 다이얼렉트를 식별하기 위한 것입니다.
CSV 파일 읽기
CSV 리더는 리더 팩토리 함수로 생성합니다.:
obj = reader(iterable [, dialect='excel']
[optional keyword args])
리더 객체는 줄을 반환하는 이터러블 객체를 유일한 필수 매개변수로 받는 이터레이터입니다. 바이너리 모드를 지원하는 경우(파일 객체가 이에 해당합니다), 리더 함수에 전달하는 이터러블 인자는 바이너리 모드로 열려 있어야 합니다. 이를 통해 리더 객체가 파일 내용의 해석을 완전히 제어할 수 있습니다. 선택적 다이얼렉트 매개변수에 대해서는 아래에서 설명합니다. 리더 함수는 파서의 특정 형식 설정을 정의하는 여러 선택적 키워드 인자도 받습니다(“Formatting Parameters” 절을 참조하십시오). 리더는 일반적으로 다음과 같이 사용합니다.:
csvreader = csv.reader(file("some.csv"))
for row in csvreader:
process(row)
리더 객체가 반환하는 각 행은 문자열 또는 유니코드 객체의 리스트입니다.
다이얼렉트 매개변수와 개별 형식 매개변수를 모두 생성자에 전달하면, 먼저 다이얼렉트에서 형식 매개변수를 조회한 다음 개별 형식 매개변수를 검사합니다.
CSV 파일 쓰기
라이터 생성도 비슷합니다.:
obj = writer(fileobj [, dialect='excel'],
[optional keyword args])
라이터 객체는 바이너리 모드로 쓰기 위해 열린 파일과 유사한 객체를 감싸는 래퍼입니다(이러한 구분이 있는 경우). 라이터는 리더 생성자와 동일한 선택적 키워드 인자를 받습니다.
라이터는 일반적으로 다음과 같이 사용합니다.:
csvwriter = csv.writer(file("some.csv", "w"))
for row in someiterable:
csvwriter.writerow(row)
CSV 파일의 첫 번째 행으로 필드 이름 집합을 생성하려면 프로그래머가 이를 명시적으로 작성해야 합니다. 예를 들면 다음과 같습니다.:
csvwriter = csv.writer(file("some.csv", "w"), fieldnames=names)
csvwriter.write(names)
for row in someiterable:
csvwriter.write(row)
또는 기록할 이터러블에서 이것이 첫 번째 행이 되도록 구성해야 합니다.
서로 다른 다이얼렉트 관리
CSV는 다소 엄밀하게 정의되지 않은 형식이므로, 두 CSV 파일이 서로 달라 보이면서도 정확히 동일한 데이터를 포함할 수 있는 방법은 많습니다. 표 형식 데이터를 가져오거나 내보낼 수 있는 많은 도구에서는 사용자가 필드 구분자, 인용 문자, 줄 종결자 및 파일의 기타 특성을 지정할 수 있습니다. 이러한 항목은 비교적 쉽게 결정할 수 있지만, 알아내는 과정은 여전히 약간 성가시고, 개별적으로 지정하면 함수 호출이 상당히 길어집니다.
서식 매개변수를 일일이 파악하고 지정해야 하는 어려움을 최소화하기 위해, 리더 및 라이터 객체는 이러한 하위 수준 매개변수 그룹을 편리하게 참조하는 핸들인 dialect 인자를 지원합니다. dialect가 문자열로 지정되면 해당 문자열은 등록 함수를 통해 모듈에 알려진 dialect 중 하나를 식별하며, 그렇지 않으면 아래에 설명된 Dialect 클래스의 인스턴스여야 합니다.
dialect는 일반적으로 특정한 형식 제약 집합을 정의하는 애플리케이션이나 조직의 이름을 따서 지정됩니다. 현재 작성 시점에는 모듈에 두 가지 dialect가 정의되어 있습니다. “excel”은 Excel 97 및 Excel 2000에서 CSV 파일을 내보낼 때 사용하는 기본 형식 제약을 설명하며, “excel-tab”은 “excel”과 동일하지만 필드 구분자로 ASCII TAB 문자를 지정합니다.
dialect는 속성만 포함하는 클래스로 구현되어 사용자가 서브클래싱을 통해 변형 dialect를 구성할 수 있습니다. “excel” dialect는 Dialect의 서브클래스이며 다음과 같이 정의됩니다.:
class Dialect:
# placeholders
delimiter = None
quotechar = None
escapechar = None
doublequote = None
skipinitialspace = None
lineterminator = None
quoting = None
class excel(Dialect):
delimiter = ','
quotechar = '"'
doublequote = True
skipinitialspace = False
lineterminator = '\r\n'
quoting = QUOTE_MINIMAL
“excel-tab” dialect는 다음과 같이 정의됩니다.:
class exceltsv(excel):
delimiter = '\t'
(개별 서식 매개변수에 대한 설명은 “서식 매개변수” 절을 참조하십시오.)
특정 dialect를 문자열로 참조할 수 있도록 모듈은 여러 함수를 정의합니다.:
dialect = get_dialect(name)
names = list_dialects()
register_dialect(name, dialect)
unregister_dialect(name)
get_dialect()는 지정된 이름에 연결된 dialect 인스턴스를 반환합니다. list_dialects()는 등록된 모든 dialect 이름의 목록을 반환합니다. register_dialects()는 문자열 이름을 dialect 클래스에 연결합니다. unregister_dialect()는 이름과 dialect의 연결을 삭제합니다.
서식 매개변수
dialect 인자 외에도 리더와 라이터 생성자는 키워드 매개변수로 지정되는 몇 가지 특정 서식 매개변수를 받습니다. 이해되는 서식 매개변수는 다음과 같습니다.
quotechar는 인용 문자로 사용할 한 문자 문자열을 지정합니다. 기본값은 ‘“‘입니다. 이를 None으로 설정하면 quoting을 csv.QUOTE_NONE으로 설정한 것과 동일한 효과가 있습니다.delimiter는 필드 구분자로 사용할 한 문자 문자열을 지정합니다. 기본값은 ‘,’입니다.escapechar는 quotechar가 None으로 설정된 경우 delimiter를 이스케이프하는 데 사용하는 한 문자 문자열을 지정합니다.skipinitialspace는 delimiter 바로 뒤에 오는 공백을 해석하는 방법을 지정합니다. 기본값은 False이며, 이는 delimiter 바로 뒤에 오는 공백이 다음 필드의 일부임을 의미합니다.lineterminator는 행을 종료하는 문자 시퀀스를 지정합니다.quoting은 라이터가 따옴표를 생성하는 시점을 제어합니다. 다음 모듈 상수 중 하나를 사용할 수 있습니다.- csv.QUOTE_MINIMAL은 필요한 경우에만, 예를 들어 필드에 quotechar 또는 delimiter가 포함된 경우에만 따옴표를 사용한다는 의미입니다.
- csv.QUOTE_ALL은 필드를 항상 따옴표로 묶는다는 의미입니다.
- csv.QUOTE_NONNUMERIC은 숫자가 아닌 필드를 항상 따옴표로 묶는다는 의미입니다.
- csv.QUOTE_NONE은 필드를 절대 따옴표로 묶지 않는다는 의미입니다.
doublequote는 필드 내부의 따옴표 처리를 제어합니다. True인 경우 읽을 때 연속된 두 따옴표를 하나로 해석하며, 쓸 때는 각 따옴표를 두 개의 따옴표로 기록합니다.
dialect 설정과 하나 이상의 다른 선택적 매개변수를 처리할 때, 개별 서식 매개변수보다 dialect 매개변수를 먼저 처리합니다. 이를 통해 새 dialect 클래스를 정의하지 않고도 dialect를 쉽게 선택한 다음 하나 이상의 설정을 재정의할 수 있습니다. 예를 들어, Excel 2000에서 작은따옴표를 인용 문자로 사용하고 콜론을 구분 기호로 사용하여 CSV 파일을 생성했다면 다음과 같은 리더를 만들 수 있습니다.:
csvreader = csv.reader(file("some.csv"), dialect="excel",
quotechar="'", delimiter=':')
“excel” 방언을 참조하므로 Excel이 CSV 파일을 생성하는 방식의 다른 세부 사항은 자동으로 처리됩니다.
리더 객체
리더 객체는 next() 메서드가 행의 각 필드에 해당하는 문자열 하나씩의 시퀀스를 반환하는 이터러블입니다.
라이터 객체
라이터 객체에는 writerow()와 writerows()라는 두 메서드가 있습니다. 전자는 출력에 기록할 필드의 이터러블(일반적으로 리스트)을 받습니다. 후자는 이터러블의 리스트를 받아 각 항목에 대해 writerow()를 호출합니다.
구현
샘플 구현을 사용할 수 있습니다. [1] 이 구현의 목표는 PEP에 설명된 API를 효율적으로 구현하는 것입니다. 이는 Object Craft csv 모듈을 크게 기반으로 합니다. [2]
테스트
샘플 구현 [1]에는 테스트 사례 집합이 포함되어 있습니다.
문제
- 연속된 구분 기호를 해석하는 방식을 매개변수로 제어해야 합니까? 이에 대한 저희의 생각은 “아니요”입니다. 연속된 구분 기호는 항상 빈 필드를 나타내야 합니다.
- 유니코드는 어떻습니까? codecs.open()에서 얻은 파일 객체를 전달하는 것으로 충분합니까? 예를 들면 다음과 같습니다.:
csvreader = csv.reader(codecs.open("some.csv", "r", "cp1252")) csvwriter = csv.writer(codecs.open("some.csv", "w", "utf-8"))
첫 번째 예에서는 텍스트가 cp1252로 인코딩되었다고 가정합니다. 시스템이 유니코드로 적극적으로 변환해야 합니까, 아니면 필요한 경우에만 유니코드 문자열을 반환해야 합니까?
두 번째 예에서는 파일이 디스크에 쓰기 전에 유니코드 문자열을 utf-8로 자동 인코딩합니다.
참고: 이 글을 작성하는 현재, csv 모듈은 유니코드 데이터를 처리하지 않습니다.
- 다른 이스케이프 규칙은 어떻습니까? 사용 중인 dialect에 None이 아닌
escapechar매개변수가 포함되어 있고quoting매개변수가 QUOTE_NONE으로 설정된 경우, 필드 안에 나타나는 구분 기호는 기록할 때 이스케이프 문자로 앞에 표시되며 읽을 때도 이스케이프 문자가 앞에 표시될 것으로 예상됩니다. - 기록을 위한 “완전 인용” 모드가 있어야 합니까? “숫자 값만 제외하고 완전 인용”하는 경우는 어떻습니까? 두 모드 모두 구현되어 있습니다(각각 QUOTE_ALL 및 QUOTE_NONNUMERIC).
- 줄 끝(end-of-line)은 어떻습니까? Unix 시스템에서 CSV 파일을 생성하면, Excel이 LF만 있는 줄 종결자를 제대로 인식합니까? 파일은 읽기든 쓰기든 상황에 맞게 반드시 바이너리 모드로 열어야 합니다.
lineterminator시퀀스를'\r\n'으로 지정하십시오. 그 결과로 생성되는 파일은 올바르게 작성됩니다. - 리더에서 딕셔너리를 생성하고 라이터가 딕셔너리를 받아들이는 옵션은 어떻습니까? csv.py의 DictReader와 DictWriter 클래스를 참조하십시오.
- 인용 문자와 구분자는 단일 문자로 제한됩니까? 지금으로서는 그렇습니다.
- 길이가 서로 다른 행은 어떻게 처리해야 합니까? 데이터의 해석은 애플리케이션의 몫입니다. 이 수준에서는 “짧은 행”이나 “긴 행” 같은 것은 존재하지 않습니다.
참고 자료
웹에는 다른 CSV 관련 프로젝트에 대한 참조가 많이 있습니다. 여기에는 그중 일부가 포함되어 있습니다.
Copyright
This document has been placed in the public domain.