CSV 포매터
사용자 지정 구분자 옵션으로 CSV 데이터를 형식화하고 정규화
CSV 입력
형식화된 CSV
CSV 형식화란?
CSV(Comma-Separated Values) 형식화는 구분자, 인용 부호, 공백, 줄 끝이 일관된 규칙을 따르도록 원시 표 형식 텍스트를 정규화하는 과정입니다. 2005년에 발표된 RFC 4180은 가장 널리 채택된 CSV 표준을 정의합니다. 쉼표로 구분된 필드, CRLF로 끝나는 레코드, 쉼표나 큰따옴표 또는 줄바꿈을 포함하는 필드는 큰따옴표로 감쌉니다. CSV 포매터는 지저분하거나 일관성 없는 CSV 데이터를 받아 이러한 규칙에 맞게 다시 작성합니다.
실제 CSV 파일은 깔끔한 형태로 제공되는 경우가 드뭅니다. Excel, Google Sheets, 데이터베이스 덤프 유틸리티에서 내보낸 스프레드시트는 각기 다른 인용 전략을 적용하고, 공백을 다르게 처리하며, 쉼표 대신 세미콜론이나 탭을 사용할 수 있습니다. 이러한 파일을 엄격한 RFC 4180 입력을 기대하는 파서에 제공하면 깨진 행, 열 이동, 무소음 데이터 손실이 발생하는 경우가 많습니다. 처리 전에 형식화하면 이런 문제를 조기에 발견할 수 있습니다.
CSV 포매터는 CSV 변환기와 다릅니다. 형식화는 데이터를 CSV 형태로 유지합니다. 인용 부호를 정규화하고, 불필요한 공백을 제거하고, 열을 재정렬하며, 필요 시 구분자를 변경합니다. 변환은 형식 자체를 바꾸어 JSON, HTML, SQL, Markdown 출력을 생성합니다.
이 CSV 포매터를 사용하는 이유
이 도구는 브라우저에서 CSV를 파싱하고 재직렬화합니다. 데이터는 절대 기기 밖으로 나가지 않습니다.
CSV 포매터 활용 사례
CSV 인용 및 이스케이프 규칙 (RFC 4180)
RFC 4180은 필드를 언제, 어떻게 인용해야 하는지에 대한 구체적인 규칙을 정의합니다. 아래 여섯 가지 시나리오는 인용 동작이 중요한 경우를 다룹니다.
| 시나리오 | 예시 | 규칙 |
|---|---|---|
| Field contains delimiter | name,"Smith, Jr." | Wrap in double quotes |
| Field contains newline | "line1\nline2" | Wrap in double quotes |
| Field contains double quote | "She said ""hello""" | Double the quote character |
| Field is empty | ,, | Two consecutive delimiters |
| Field has leading spaces | " value" | Quotes preserve whitespace |
| Field is numeric | 42 | No quotes required unless forced |
CSV 구분자 비교
구분자 선택은 호환성, 가독성, 인용 필드가 필요한 빈도에 영향을 미칩니다.
코드 예제
다양한 언어에서 지저분한 CSV를 파싱하고 일관된 형식으로 재직렬화하는 방법을 보여주는 예제입니다. 각 코드 조각은 공백 제거, 구분자 정규화, 인용 부호 처리를 다룹니다.
import { parse, unparse } from 'papaparse'
const messy = `name, age ,city
Alice , 30, Berlin
Bob,25 , " Tokyo "`
// Parse with trimming, then re-serialize with consistent formatting
const parsed = parse(messy, {
header: true,
skipEmptyLines: true,
transformHeader: h => h.trim(),
transform: v => v.trim(),
})
const clean = unparse(parsed.data, { quotes: true })
console.log(clean)
// → "name","age","city"
// → "Alice","30","Berlin"
// → "Bob","25","Tokyo"import csv
import io
messy = """name, age ,city
Alice , 30, Berlin
Bob,25 , " Tokyo " """
reader = csv.DictReader(io.StringIO(messy), skipinitialspace=True)
output = io.StringIO()
writer = csv.DictWriter(
output,
fieldnames=[f.strip() for f in reader.fieldnames],
quoting=csv.QUOTE_ALL,
)
writer.writeheader()
for row in reader:
writer.writerow({k.strip(): v.strip() for k, v in row.items()})
print(output.getvalue())
# → "name","age","city"
# → "Alice","30","Berlin"
# → "Bob","25","Tokyo"package main
import (
"encoding/csv"
"fmt"
"strings"
)
func main() {
input := "name,age,city\nAlice,30,Berlin\nBob,25,Tokyo"
r := csv.NewReader(strings.NewReader(input))
records, _ := r.ReadAll()
var buf strings.Builder
w := csv.NewWriter(&buf)
w.UseCRLF = true // RFC 4180 line endings
for _, record := range records {
_ = w.Write(record)
}
w.Flush()
fmt.Print(buf.String())
// → name,age,city\r\n
// → Alice,30,Berlin\r\n
// → Bob,25,Tokyo\r\n
}# Re-format a CSV file with csvkit (Python-based) csvformat -D ";" input.csv > output.csv # Convert tabs to commas csvformat -t input.tsv > output.csv # Force-quote all fields csvformat -U 1 input.csv > quoted.csv # Using Miller (mlr) to normalize mlr --icsv --ocsv --quote-all cat input.csv > clean.csv