중복 줄 제거기
텍스트에서 중복 줄을 제거하고 고유한 줄만 유지
입력 줄
고유 줄
중복 줄 제거란?
중복 줄 제거란 텍스트 블록을 줄 단위로 스캔하여 각 고유 줄의 첫 번째 등장만 유지하는 과정입니다. 온라인에서 중복 줄을 제거할 때, 도구는 입력을 줄바꿈 문자 기준으로 분리하고, Set과 같은 해시 기반 자료구조를 사용하여 이미 등장한 줄을 추적하며, 아직 나타나지 않은 줄만 출력합니다. 원래 줄 순서는 그대로 유지됩니다.
두 줄이 문자 하나하나까지 정확히 일치할 때 중복으로 처리됩니다. 그러나 실제 데이터는 정확한 일치를 보장하지 않는 경우가 많습니다. 앞뒤 공백, 일관성 없는 대소문자, 탭이나 캐리지 리턴 같은 보이지 않는 문자로 인해 겉보기엔 동일한 줄이 서로 다른 줄로 처리될 수 있습니다. 그래서 대부분의 중복 제거 도구는 비교 전 대소문자 구분 없는 비교와 공백 제거 옵션을 제공합니다.
중복 제거는 정렬과는 별개의 작업입니다. Unix 명령어 sort -u는 정렬과 중복 제거를 동시에 수행하므로 줄 순서가 바뀝니다. 원래 순서를 유지해야 한다면 seen-set 방식을 사용해야 합니다. 줄을 순서대로 순회하면서 각 줄의 정규화된 형태를 집합에 추가하고, 이미 키가 존재하는 줄은 건너뜁니다. 이 도구는 seen-set 방식을 사용하므로 첫 번째 등장하는 줄이 원래 위치를 유지합니다.
이 중복 제거기를 사용하는 이유
텍스트를 붙여넣고, 비교 옵션을 선택하면 중복 제거된 결과를 즉시 확인할 수 있습니다. 명령줄 설정, 정규식 작성, 파일 업로드가 필요 없습니다.
중복 줄 제거기 활용 사례
중복 제거 방법 비교
중복 줄을 제거하는 방법에는 여러 가지가 있으며, 각각 순서 유지, 메모리 사용량, 정확도 면에서 서로 다른 트레이드오프를 가집니다.
| 방법 | 작동 방식 | 출력 순서 | 사용 예 |
|---|---|---|---|
| Set | Hash-based, O(1) lookup | Unordered | JavaScript Set, Python set() |
| Sorted + scan | Sort then skip adjacent | Sorted output | Unix sort -u, C++ std::unique |
| Seen-set + list | Track seen, preserve order | Original order | This tool, Python dict.fromkeys() |
| Bloom filter | Probabilistic membership | May miss some | Large-scale pipelines, Redis |
| SQL DISTINCT | Database-level dedup | Query-dependent | SELECT DISTINCT col FROM table |
대소문자 구분 및 공백 처리
두 가지 옵션이 이 도구가 두 줄을 중복으로 판단하는 방식을 제어합니다. 각 옵션의 사용 시점을 이해하면 오탐(다른 줄을 중복으로 처리)과 미탐(일치해야 할 줄을 놓침)을 모두 방지할 수 있습니다.
코드 예제
JavaScript, Python, Go, 명령줄에서 프로그래밍 방식으로 중복 줄을 제거합니다. 각 예제는 순서를 유지하는 중복 제거를 보여주며 대소문자 구분을 처리합니다.
const text = `apple
banana
apple
Cherry
banana
cherry`
// Remove exact duplicates, preserve order
const unique = [...new Map(
text.split('\n').map(line => [line, line])
).values()].join('\n')
// → "apple\nbanana\nCherry\ncherry"
// Case-insensitive deduplication
const seen = new Set()
const ciUnique = text.split('\n').filter(line => {
const key = line.toLowerCase()
if (seen.has(key)) return false
seen.add(key)
return true
}).join('\n')
// → "apple\nbanana\nCherry"
// Trim whitespace before comparing
const trimDedup = text.split('\n').filter(line => {
const key = line.trim().toLowerCase()
if (seen.has(key)) return false
seen.add(key)
return true
}).join('\n')text = """apple
banana
apple
Cherry
banana
cherry"""
lines = text.splitlines()
# Remove duplicates, preserve order (Python 3.7+)
unique = list(dict.fromkeys(lines))
# → ['apple', 'banana', 'Cherry', 'cherry']
# Case-insensitive deduplication
seen = set()
ci_unique = []
for line in lines:
key = line.lower()
if key not in seen:
seen.add(key)
ci_unique.append(line)
# → ['apple', 'banana', 'Cherry']
# With whitespace trimming
seen = set()
trimmed = []
for line in lines:
key = line.strip().lower()
if key not in seen:
seen.add(key)
trimmed.append(line)package main
import (
"fmt"
"strings"
)
func removeDuplicates(text string) string {
lines := strings.Split(text, "\n")
seen := make(map[string]bool)
result := make([]string, 0, len(lines))
for _, line := range lines {
if !seen[line] {
seen[line] = true
result = append(result, line)
}
}
return strings.Join(result, "\n")
}
func main() {
text := "apple\nbanana\napple\ncherry\nbanana"
fmt.Println(removeDuplicates(text))
// → apple\nbanana\ncherry
}# Remove duplicates (sorts output — does not preserve order)
sort -u file.txt
# Remove duplicates while preserving original order
awk '!seen[$0]++' file.txt
# Case-insensitive dedup, preserve order
awk 'BEGIN{IGNORECASE=1} !seen[tolower($0)]++' file.txt
# Trim whitespace then dedup
sed 's/^[[:space:]]*//;s/[[:space:]]*$//' file.txt | awk '!seen[$0]++'
# Count duplicates before removing
sort file.txt | uniq -c | sort -rn