코딩 AI 추천 순위
코딩 AI 공동 1위는 제미나이·클로드 등 3개(100점)입니다(API 측정).2026.10.07 실측
코딩 AI 공동 1위는 제미나이·클로드 등 3개(100점)입니다(API 측정).2026.10.07 실측
같은 프롬프트를 API로 실행해 채점한 결과입니다. 앱의 파일 업로드·편집 같은 사용 편의는 포함하지 않습니다. 측정 방법
| 순위 | 도구 | 점수 /100 | API 응답 시간 | API 1회 비용 | 앱 월 구독료 |
|---|---|---|---|---|---|
| 1공동 | 10099~100 | 1.4초 | 7원 | Google AI Pro 29,000원/월Google AI Plus 7,500원부터 | |
| 1공동 | 100 | 1.6초 | 85원 | Pro 26,682원/월($20 환산) | |
| 1공동 | 100 | 2.6초 | 38원 | SuperGrok 40,023원/월($30 환산) | |
| 4 | 9484~100 | 2.3초 | 28원 | Pro 26,682원/월($20 환산) | |
| 5 | 9380~100 | 0.7초 | 3원 | — | |
| 6 | 8877~100 | 1.7초 | 1원 미만 | Plus 29,000원/월Go 13,000원부터 | |
| 7 | 660~100 | 0.3초 | 8원 | 무료 |
프롬프트 1. 파이썬 스크립트를 하나 작성해 주세요. 표준 라이브러리만 사용합니다(re, csv, json, argparse, collections 등은 표준 라이브러리입니다). 입력: 웹 서버 접속 로그 파일. 한 줄 형식은 다음과 같습니다. 2026-09-20T13:05:41+09:00 203.0.113.7 GET /api/items 200 123 (공백으로 구분: 시각(ISO 8601), IP, 메서드, 경로, 상태코드, 응답시간ms) 요구사항: 1. 시간대(0~23시)별 요청 수를 00시부터 23시까지 24개 모두 출력(요청이 없는 시간은 0) 2. 요청 수 상위 5개 IP를 요청 수 내림차순으로, 같은 수면 IP 문자열 오름차순으로 3. 상태코드 500 이상인 요청의 비율(%)을 소수점 첫째 자리까지 4. 열 개수가 6개가 아니거나 상태코드·응답시간이 정수가 아닌 줄은 건너뛰고, 건너뛴 줄 수를 기록 5. 결과는 JSON 하나로 표준 출력에 출력. 키는 정확히 hourly, top_ips, error_rate, skipped 네 개. hourly 는 "00"~"23" 키의 객체, top_ips 는 [ip, count] 배열의 배열 조건: - 파일 경로는 argparse 로 받고 기본값은 access.log. 인코딩은 UTF-8 - 함수를 3개 이상으로 나누고 각 함수에 타입 힌트를 붙이세요 - 코드 블록 하나로, 주석은 한국어 - JSON 출력은 ensure_ascii=False, indent 2. print 는 마지막 JSON 한 번만 - 코드 아래에 실행 방법을 존댓말 두 문장 이내로 - 요구사항 외 기능(그래프, 파일 저장, 로깅 설정, 진행 표시) 추가 금지
프롬프트 2. Node.js(18 이상)에서 외부 패키지 없이 동작하는 자바스크립트 모듈을 하나 작성해 주세요. 파일 하나(krw.mjs)에 함수 2개와 테스트를 함께 넣습니다. 함수 1. formatKRW(n) - 숫자를 "1,234,567원" 형태의 문자열로 반환 - 소수는 반올림하지 않고 버림(1234.9 → "1,234원") - 음수는 앞에 "-" (−1234 → "-1,234원") - 숫자가 아니거나 NaN·Infinity 면 null 반환 - Intl.NumberFormat 을 써도 되고 직접 구현해도 됩니다 함수 2. parseKoreanDate(s) - "2026년 9월 26일" 또는 "2026년 09월 06일" 형태의 문자열을 "2026-09-26" 형식(YYYY-MM-DD, 두 자리 0 채움)으로 반환 - 앞뒤 공백은 무시 - 형식이 다르거나 존재하지 않는 날짜(예: 2026년 2월 30일)는 null 반환 테스트 - node:test 와 node:assert 만 사용 - 두 함수 각각 최소 4개, 그중 null 을 반환하는 경우가 함수마다 최소 1개 조건: - 코드 블록 하나, 주석은 한국어, export 는 named export - 코드 아래에 실행 방법을 존댓말 한 문장으로 - 요구사항 외 함수·옵션(통화 선택, 로케일 옵션 등) 추가 금지
프롬프트 3. 파이썬 함수 하나와 테스트를 작성해 주세요. 표준 라이브러리만 사용합니다. 함수: parse_krw(text: str) -> int | None 한국어로 적힌 금액 문자열을 원 단위 정수로 바꿉니다. 규칙: 1. 숫자와 쉼표: "1,234원" → 1234, "1234" → 1234 ("원"은 있어도 없어도 됨) 2. 한글 단위: 만(10^4), 억(10^8), 조(10^12). 큰 단위부터 조합 가능: "3억 5천만원" → 350000000, "1조 2억" → 1000200000000 3. 단위 앞의 천·백·십: "5천만" → 50000000, "2백만" → 2000000, "3십만" → 300000, "천원" → 1000 (천·백·십 앞에 숫자가 없으면 1) 4. 소수는 만·억·조 앞에만 허용: "1.5만" → 15000, "2.35억" → 235000000. 결과가 정수가 아니면 None ("1.23456만" → None) 5. 공백은 어디에 있어도 무시: " 3 억 5천 만 원 " → 350000000 6. 음수는 맨 앞 "-" 또는 "마이너스"만 허용: "-1,000원" → -1000, "마이너스 2만" → -20000 7. 해석할 수 없으면 None: 빈 문자열, "만만", "억3"(단위 뒤에 단위 없는 숫자가 오면서 앞 단위보다 큰 수가 되는 경우 포함), "1,23원"(쉼표 위치 오류), "abc" 8. 같은 단위가 두 번 나오거나("3억 2억") 작은 단위 뒤에 큰 단위가 오면("5만 3억") None 테스트: - unittest 로 작성, 위 예시를 모두 포함하고 None 이 나와야 하는 경우를 5개 이상 - if __name__ == "__main__": unittest.main() 조건: - 코드 블록 하나, 주석은 한국어 - 정규식을 쓴다면 각 정규식 위에 한국어 주석 한 줄로 무엇을 잡는지 설명 - 코드 아래에 실행 방법을 존댓말 한 문장으로 - 요구사항 외 기능(통화 기호 처리, 영어 단위, 로깅) 추가 금지
파이썬 함수 하나와 테스트를 작성해 주세요. 표준 라이브러리만 사용합니다. 함수: parse_krw(text: str) -> int | None 한국어로 적힌 금액 문자열을 원 단위 정수로 바꿉니다. 규칙: 1. 숫자와 쉼표: "1,234원" → 1234, "1234" → 1234 ("원"은 있어도 없어도 됨) 2. 한글 단위: 만(10^4), 억(10^8), 조(10^12). 큰 단위부터 조합 가능: "3억 5천만원" → 350000000, "1조 2억" → 1000200000000 3. 단위 앞의 천·백·십: "5천만" → 50000000, "2백만" → 2000000, "3십만" → 300000, "천원" → 1000 (천·백·십 앞에 숫자가 없으면 1) 4. 소수는 만·억·조 앞에만 허용: "1.5만" → 15000, "2.35억" → 235000000. 결과가 정수가 아니면 None ("1.23456만" → None) 5. 공백은 어디에 있어도 무시: " 3 억 5천 만 원 " → 350000000 6. 음수는 맨 앞 "-" 또는 "마이너스"만 허용: "-1,000원" → -1000, "마이너스 2만" → -20000 7. 해석할 수 없으면 None: 빈 문자열, "만만", "억3"(단위 뒤에 단위 없는 숫자가 오면서 앞 단위보다 큰 수가 되는 경우 포함), "1,23원"(쉼표 위치 오류), "abc" 8. 같은 단위가 두 번 나오거나("3억 2억") 작은 단위 뒤에 큰 단위가 오면("5만 3억") None 테스트: - unittest 로 작성, 위 예시를 모두 포함하고 None 이 나와야 하는 경우를 5개 이상 - if __name__ == "__main__": unittest.main() 조건: - 코드 블록 하나, 주석은 한국어 - 정규식을 쓴다면 각 정규식 위에 한국어 주석 한 줄로 무엇을 잡는지 설명 - 코드 아래에 실행 방법을 존댓말 한 문장으로 - 요구사항 외 기능(통화 기호 처리, 영어 단위, 로깅) 추가 금지
import re
import unittest
def parse_krw(text: str) -> int | None:
# 전체 금액 문자열에서 허용된 문자만 사용하는지 확인합니다.
if not isinstance(text, str):
return None
value = text.replace(" ", "")
if not value:
return None
negative = False
if value.startswith("마이너스"):
negative = True
value = value[4:]
elif value.startswith("-"):
negative = True
value = value[1:]
if not value or value.startswith("-") or "원" in value[:-1]:
return None
if value.endswith("원"):
value = value[:-1]
if not value:
return None
# 쉼표가 올바른 천 단위 표기인지 확인합니다.
if "," in value:
if not re.fullmatch(r"[0-9]{1,3}(?:,[0-9]{3})+", value):
return None
value = value.replace(",", "")
elif not re.fullmatch(r"[0-9]+(?:\.[0-9]+)?|[가-힣0-9.,]+", value):
return None
# 숫자만 있는 금액을 처리합니다.
if re.fullmatch(r"[0-9]+", value):
return -int(value) if negative else int(value)
units = {"조": 10**12, "억": 10**8, "만": 10**4}
small_units = {"천": 1000, "백": 100, "십": 10}
# 한글 금액을 숫자와 단위 토큰으로 나눕니다.
token_pattern = re.compile(r"(?:[0-9]+(?:\.[0-9]+)?|천|백|십|조|억|만)")
tokens = token_pattern.findall(value)
if not tokens or "".join(tokens) != value:
return None
result = 0
index = 0
previous_large = float("inf")
seen_large = set()
while index < len(tokens):
token = tokens[index]
if token in units:
return None
if token in small_units:
number = 1
if index > 0 and re.fullmatch(r"[0-9]+(?:\.[0-9]+)?", tokens[index - 1]):
number = float(tokens[index - 1])
index -= 1
result += int(number * small_units[token])
index += 2
continue
if re.fullmatch(r"[0-9]+(?:\.[0-9]+)?", token):
number = float(token)
index += 1
else:
number = 1
if index >= len(tokens) or tokens[index] not in units:
return None
unit = tokens[index]
multiplier = units[unit]
if unit in seen_large or multiplier >= previous_large:
return None
if "." in token:
decimal_places = len(token.split(".", 1)[1])
if number * multiplier != int(number * multiplier):
return None
if decimal_places > 4 and unit == "만":
return None
result += int(number * multiplier)
previous_large = multiplier
seen_large.add(unit)
index += 1
return -result if negative else result
class TestParseKrw(unittest.TestCase):
def test_numbers(self):
self.assertEqual(parse_krw("1,234원"), 1234)
self.assertEqual(parse_krw("1234"), 1234)
self.assertEqual(parse_krw("-1,000원"), -1000)
def test_korean_units(self):
self.assertEqual(parse_krw("3억 5천만원"), 350000000)
self.assertEqual(parse_krw("1조 2억"), 1000200000000)
self.assertEqual(parse_krw("5천만"), 50000000)
self.assertEqual(parse_krw("2백만"), 2000000)
self.assertEqual(parse_krw("3십만"), 300000)
self.assertEqual(parse_krw("천원"), 1000)
self.assertEqual(parse_krw(" 3 억 5천 만 원 "), 350000000)
def test_decimals_and_negative(self):
self.assertEqual(parse_krw("1.5만"), 15000)
self.assertEqual(parse_krw("2.35억"), 235000000)
self.assertEqual(parse_krw("마이너스 2만"), -20000)
def test_invalid_values(self):
invalid_values = [
"",
"만만",
"억3",
"1,23원",
"abc",
"1.23456만",
"3억 2억",
"5만 3억",
]
for value in invalid_values:
with self.subTest(value=value):
self.assertIsNone(parse_krw(value))
if __name__ == "__main__":
unittest.main()
파일로 저장한 뒤 python 파일명.py 명령으로 실행하시면 됩니다.
import re
import unittest
from decimal import Decimal
def _쉼표_정수(조각: str) -> int | None:
# 천 단위 쉼표가 올바른 정수, 또는 쉼표 없는 정수
if re.fullmatch(r"\d{1,3}(,\d{3})+|\d+", 조각) is None:
return None
return int(조각.replace(",", ""))
def _천백십_수(조각: str) -> int | None:
"""천·백·십과 숫자로 된 계수를 읽습니다. 순서가 어긋나면 None입니다."""
if not 조각:
return None
순서 = ["천", "백", "십"]
값 = {"천": 1000, "백": 100, "십": 10}
한도 = {"천": 1000, "백": 100, "십": 10}
합 = 0
이전 = -1
i = 0
n = len(조각)
while i < n:
j = i
while j < n and 조각[j].isdigit():
j += 1
숫자 = int(조각[i:j]) if j > i else None
i = j
if i < n and 조각[i] in 값:
단위 = 조각[i]
순위 = 순서.index(단위)
if 순위 <= 이전:
return None
이전 = 순위
합 += (1 if 숫자 is None else 숫자) * 값[단위]
i += 1
continue
if 숫자 is None or i != n:
return None
if 이전 == -1:
합 += 숫자
elif 숫자 >= 한도[순서[이전]]:
return None
else:
합 += 숫자
break
return 합 if i == n else None
def parse_krw(text: str) -> int | None:
"""한국어 금액 문자열을 원 단위 정수로 바꿉니다. 해석 불가면 None입니다."""
if not isinstance(text, str):
return None
본문 = "".join(text.split())
if not 본문:
return None
부호 = 1
if 본문.startswith("마이너스"):
부호 = -1
본문 = 본문[len("마이너스") :]
elif 본문.startswith("-"):
부호 = -1
본문 = 본문[1:]
if not 본문:
return None
if 본문.endswith("원"):
본문 = 본문[:-1]
if not 본문:
return None
큰단위 = {"조": 10**12, "억": 10**8, "만": 10**4}
단위순서 = ["조", "억", "만"]
합계 = 0
이전순위 = -1
위치 = 0
읽음 = False
while 위치 < len(본문):
다음위치 = None
다음단위 = None
for p in range(위치, len(본문)):
if 본문[p] in 큰단위:
다음위치 = p
다음단위 = 본문[p]
break
if 다음단위 is None:
조각 = 본문[위치:]
if "." in 조각:
return None
if any(글자 in "천백십" for 글자 in 조각):
값 = _천백십_수(조각)
else:
값 = _쉼표_정수(조각)
if 값 is None:
return None
합계 += 값
읽음 = True
break
순위 = 단위순서.index(다음단위)
if 순위 <= 이전순위:
return None
조각 = 본문[위치:다음위치]
if not 조각:
return None
배수 = 큰단위[다음단위]
if "." in 조각:
# 만·억·조 바로 앞의 소수 (숫자.숫자)
if re.fullmatch(r"\d+\.\d+", 조각) is None:
return None
환산 = Decimal(조각) * Decimal(배수)
if 환산 != 환산.to_integral_value():
return None
합계 += int(환산)
elif any(글자 in "천백십" for 글자 in 조각):
if "," in 조각:
return None
값 = _천백십_수(조각)
if 값 is None:
return None
합계 += 값 * 배수
else:
값 = _쉼표_정수(조각)
if 값 is None:
return None
합계 += 값 * 배수
이전순위 = 순위
읽음 = True
위치 = 다음위치 + 1
if not 읽음:
return None
return 부호 * 합계
class ParseKrwTest(unittest.TestCase):
def test_examples(self) -> None:
사례 = [
("1,234원", 1234),
("1234", 1234),
("3억 5천만원", 350000000),
("1조 2억", 1000200000000),
("5천만", 50000000),
("2백만", 2000000),
("3십만", 300000),
("천원", 1000),
("1.5만", 15000),
("2.35억", 235000000),
("1.23456만", None),
(" 3 억 5천 만 원 ", 350000000),
("-1,000원", -1000),
("마이너스 2만", -20000),
("", None),
("만만", None),
("억3", None),
("1,23원", None),
("abc", None),
("3억 2억", None),
("5만 3억", None),
]
for 입력, 기대 in 사례:
with self.subTest(입력=입력):
self.assertEqual(parse_krw(입력), 기대)
if __name__ == "__main__":
unittest.main()
이 파일을 저장한 뒤 python 파일명.py로 실행하시면 됩니다.
import re
import unittest
def parse_krw(text: str) -> int | None:
# 공백 및 기본 정리
if not text or not text.strip():
return None
sign = 1
t = text.strip()
# 음수 부호 처리: 맨 앞 "-" 또는 "마이너스"만 허용
if t.startswith('-'):
sign = -1
t = t[1:].strip()
elif t.startswith('마이너스'):
sign = -1
t = t[3:].strip()
# 모든 공백 제거
t = re.sub(r'\s+', '', t)
# 끝의 '원' 제거 (있어도 없어도 됨)
if t.endswith('원'):
t = t[:-1]
if not t:
return None
# 단위/승수 앞에 숫자가 없으면 1로 간주하도록 전처리
# (?<!\d) : 숫자 바로 뒤가 아닌 위치
t = re.sub(r'(?<!\d)(천|백|십|조|억|만)', r'1\1', t)
# 전체 문자열이 유효한 항(term)들의 연속인지 확인하며 파싱
# 항 패턴: 숫자(쉼표 포함, 소수 가능) + 선택적 승수(천/백/십) + 선택적 단위(조/억/만)
term_re = re.compile(r'(\d+(?:,\d{3})*(?:\.\d+)?)\s*(천|백|십)?\s*(조|억|만)?')
terms = []
pos = 0
for m in term_re.finditer(t):
if m.start() != pos:
# 매칭되지 않은 문자가 존재하면 무효 (예: "억3"에서 3 앞이 비어있거나 단위 뒤에 숫자만 있는 경우)
return None
pos = m.end()
num_str, mul, unit = m.groups()
terms.append((num_str, mul, unit))
if pos != len(t):
return None
if not terms:
return None
# 쉼표 위치 검증 및 정수/소수 분리
total = 0
last_mag = float('inf') # 단위 크기 순서 검증용 (큰 단위부터 작은 단위 순으로 와야 함)
mag_map = {'조': 12, '억': 8, '만': 4, '천': 3, '백': 2, '십': 1}
for num_str, mul, unit in terms:
# 쉼표 유효성 검사: 1~3자리 숫자 뒤에 3자리씩 쉼표로 이어지는 형태만 허용
if not re.fullmatch(r'\d{1,3}(?:,\d{3})*', num_str.replace('.', ',').split(',')[0] + '.' + ','.join(num_str.split('.')[1:])) if '.' in num_str else re.fullmatch(r'\d{1,3}(?:,\d{3})*', num_str):
# 간단히 쉼표 포함 숫자 전체를 검증
pass
# 쉼표 검증 로직 단순화: 소수점을 기준으로 정수부와 소수부 분리 후 각각 검증
if '.' in num_str:
int_part, frac_part = num_str.split('.', 1)
else:
int_part, frac_part = num_str, ''
# 정수부 쉼표 검증
if not re.fullmatch(r'\d{1,3}(?:,\d{3})*', int_part):
return None
# 소수부는 쉼표 없어야 함 (규칙상 소수점은 만·억·조 앞에만 허용되므로 쉼표와 혼용 불가)
if ',' in frac_part:
return None
# 소수 처리: 만·억·조 앞에만 허용
if frac_part:
if unit not in ('만', '억', '조'):
return None
# 결과가 정수가 아니면 None
# 예: 1.23456만 -> 12345.6 원 -> 정수 아님
# 소수부가 0으로 끝나지 않으면 정수 변환 시 소수점 발생
if frac_part[-1] != '0':
# 실제로 1.5만 -> 15000 (정수), 1.23456만 -> 12345.6 (정수 아님)
# 소수부 길이가 단위 배율과 맞아떨어져야 정수가 됨
# 만(10^4)이므로 소수부 4자리까지 0이어야 정수? 아님.
# 1.5만 = 1.5 * 10000 = 15000 (정수)
# 1.23456만 = 1.23456 * 10000 = 12345.6 (정수 아님)
# 일반적으로 소수부 * 10^단위지수 가 정수여야 함.
# 간단히: float(num_str) * (10**mag) 가 정수인지 확인
pass
# 단위/승수 조합 계산
base = float(num_str.replace(',', ''))
mag = 0
if unit:
mag += mag_map[unit]
if mul:
mag += mag_map[mul]
# 소수부 처리 및 정수 여부 확인
if frac_part:
# 소수부가 있으면 단위가 만/억/조여야 함 (위에서 이미 체크)
val = base * (10 ** mag)
if val != int(val):
return None
val = int(val)
else:
val = int(base) * (10 ** mag)
# 단위 크기 순서 검증: 큰 단위 → 작은 단위 순이어야 함
# 같은 단위 중복 체크
if mag > last_mag:
return None
last_mag = mag
total += val
return sign * total
class TestParseKRW(unittest.TestCase):
def test_examples(self):
# 규칙 1: 숫자와 쉼표
self.assertEqual(parse_krw("1,234원"), 1234)
self.assertEqual(parse_krw("1234"), 1234)
# 규칙 2: 한글 단위 조합
self.assertEqual(parse_krw("3억 5천만원"), 350000000)
self.assertEqual(parse_krw("1조 2억"), 1000200000000)
# 규칙 3: 단위 앞 천·백·십
self.assertEqual(parse_krw("5천만"), 50000000)
self.assertEqual(parse_krw("2백만"), 2000000)
self.assertEqual(parse_krw("3십만"), 300000)
self.assertEqual(parse_krw("천원"), 1000)
# 규칙 4: 소수
self.assertEqual(parse_krw("1.5만"), 15000)
self.assertEqual(parse_krw("2.35억"), 235000000)
# 규칙 5: 공백 무시
self.assertEqual(parse_krw(" 3 억 5천 만 원 "), 350000000)
# 규칙 6: 음수
self.assertEqual(parse_krw("-1,000원"), -1000)
self.assertEqual(parse_krw("마이너스 2만"), -20000)
def test_none_cases(self):
# 규칙 7: 해석 불가
self.assertIsNone(parse_krw(""))
self.assertIsNone(parse_krw(" "))
self.assertIsNone(parse_krw("만만"))
self.assertIsNone(parse_krw("억3"))
self.assertIsNone(parse_krw("1,23원")) # 쉼표 위치 오류
self.assertIsNone(parse_krw("abc"))
self.assertIsNone(parse_krw("1.23456만")) # 결과 정수 아님
# 규칙 8: 같은 단위 중복, 작은 단위 뒤 큰 단위
self.assertIsNone(parse_krw("3억 2억"))
self.assertIsNone(parse_krw("5만 3억"))
# 추가 무효 케이스
self.assertIsNone(parse_krw("1.5천")) # 소수는 만·억·조 앞에만 허용
self.assertIsNone(parse_krw("마이너스")) # 숫자 없음
self.assertIsNone(parse_krw("-")) # 부호만 있음
self.assertIsNone(parse_krw("1,2,3원")) # 쉼표 오류
if __name__ == "__main__":
unittest.main()
위 코드를 .py 파일로 저장하신 후 터미널에서 python 파일명.py 명령어로 실행하시면 unittest 결과가 출력됩니다.
from fractions import Fraction
import re
import unittest
def parse_krw(text: str) -> int | None:
"""한국어 금액 문자열을 원 단위 정수로 변환합니다."""
if not isinstance(text, str):
return None
value = "".join(text.split())
if not value:
return None
negative = False
if value.startswith("-"):
negative = True
value = value[1:]
elif value.startswith("마이너스"):
negative = True
value = value[len("마이너스"):]
if not value:
return None
if value.endswith("원"):
value = value[:-1]
if not value:
return None
# 올바른 쉼표 묶음이 있는 숫자 또는 쉼표 없는 숫자를 찾습니다.
number_pattern = re.compile(
r"(?:[0-9]{1,3}(?:,[0-9]{3})+|[0-9]+)(?:\.[0-9]+)?"
)
# 숫자와 한글 단위를 순서대로 찾습니다.
token_pattern = re.compile(
r"(?:[0-9]{1,3}(?:,[0-9]{3})+|[0-9]+)(?:\.[0-9]+)?|천|백|십|만|억|조"
)
tokens = []
position = 0
while position < len(value):
match = token_pattern.match(value, position)
if match is None:
return None
tokens.append(match.group())
position = match.end()
total = Fraction(0)
section = Fraction(0)
pending = None
pending_decimal = False
has_unit = False
last_small_rank = 4
last_large_rank = 4
small_units = {"천": (1000, 3), "백": (100, 2), "십": (10, 1)}
large_units = {"만": (10**4, 3), "억": (10**8, 2), "조": (10**12, 1)}
for token in tokens:
if number_pattern.fullmatch(token):
if pending is not None:
return None
normalized = token.replace(",", "")
pending_decimal = "." in normalized
try:
pending = Fraction(normalized)
except (ValueError, ZeroDivisionError):
return None
continue
has_unit = True
if token in small_units:
if pending_decimal:
return None
multiplier, rank = small_units[token]
if rank >= last_small_rank:
return None
coefficient = pending if pending is not None else Fraction(1)
section += coefficient * multiplier
pending = None
pending_decimal = False
last_small_rank = rank
continue
multiplier, rank = large_units[token]
if rank >= last_large_rank:
return None
if pending is not None and section != 0:
return None
coefficient = pending if pending is not None else section
if coefficient == 0:
coefficient = Fraction(1)
total += coefficient * multiplier
section = Fraction(0)
pending = None
pending_decimal = False
last_small_rank = 4
last_large_rank = rank
if has_unit:
if pending is not None:
return None
result = total + section
else:
if pending is None or pending.denominator != 1:
return None
result = pending
if result.denominator != 1:
return None
integer = int(result)
return -integer if negative else integer
class ParseKrwTests(unittest.TestCase):
def test_examples(self):
examples = {
"1,234원": 1234,
"1234": 1234,
"3억 5천만원": 350000000,
"1조 2억": 1000200000000,
"5천만": 50000000,
"2백만": 2000000,
"3십만": 300000,
"천원": 1000,
"1.5만": 15000,
"2.35억": 235000000,
" 3 억 5천 만 원 ": 350000000,
"-1,000원": -1000,
"마이너스 2만": -20000,
}
for text, expected in examples.items():
with self.subTest(text=text):
self.assertEqual(parse_krw(text), expected)
def test_invalid_inputs(self):
invalid = [
"",
"만만",
"억3",
"1,23원",
"abc",
"1.23456만",
"3억 2억",
"5만 3억",
"1.5천",
"1.2",
]
for text in invalid:
with self.subTest(text=text):
self.assertIsNone(parse_krw(text))
if __name__ == "__main__":
unittest.main()
파일로 저장한 뒤 python 파일명.py를 실행하시면 테스트를 확인하실 수 있습니다.
import re
import unittest
from decimal import Decimal, InvalidOperation
def parse_sub_10000(s: str) -> int | None:
"""만 단위 미만(천, 백, 십 포함 또는 순수 정수)을 파싱합니다."""
if not s:
return 0
# 쉼표를 포함하는 숫자인 경우 3자리 단위 쉼표 형식 검증
if "," in s:
# 천 단위 쉼표가 올바르게 들어간 숫자 형식 검사
if not re.fullmatch(r"\d{1,3}(,\d{3})+", s):
return None
s = s.replace(",", "")
# 순수 아라비아 숫자인 경우
if s.isdigit():
return int(s)
# 소수점이 포함되어 있다면 만 단위 미만에서는 허용되지 않음
if "." in s:
return None
# 천·백·십 조합 처리
units = {"천": 1000, "백": 100, "십": 10}
total = 0
curr_s = s
for u_char, u_val in units.items():
if u_char in curr_s:
parts = curr_s.split(u_char, 1)
coef_str = parts[0]
curr_s = parts[1]
if coef_str == "":
coef = 1
elif coef_str.isdigit():
coef = int(coef_str)
else:
return None
if coef >= 10:
return None
total += coef * u_val
if curr_s:
if curr_s.isdigit():
val = int(curr_s)
if val >= 10:
return None
total += val
else:
return None
return total
def parse_krw(text: str) -> int | None:
"""한국어로 적힌 금액 문자열을 원 단위 정수로 변환합니다."""
if not isinstance(text, str):
return None
# 모든 공백 제거
s = text.replace(" ", "")
if not s:
return None
# 음수 부호 처리
is_negative = False
if s.startswith("-"):
is_negative = True
s = s[1:]
elif s.startswith("마이너스"):
is_negative = True
s = s[len("마이너스") :]
if not s:
return None
# 끝의 '원' 제거
if s.endswith("원"):
s = s[:-1]
if not s:
return None
# 큰 단위(조, 억, 만)
major_units = [("조", 10**12), ("억", 10**8), ("만", 10**4)]
total = 0
last_unit_pos = -1
for u_char, u_val in major_units:
count = s.count(u_char)
if count > 1:
return None
if count == 1:
pos = s.find(u_char)
if last_unit_pos != -1 and pos < last_unit_pos:
return None
last_unit_pos = pos
rem = s
for u_char, u_val in major_units:
if u_char in rem:
parts = rem.split(u_char, 1)
prefix = parts[0]
rem = parts[1]
if not prefix:
return None
# 소수점이 포함된 경우
if "." in prefix:
# 쉼표는 소수점과 함께 사용될 수 없음
if "," in prefix:
return None
try:
dec = Decimal(prefix)
except InvalidOperation:
return None
calc = dec * Decimal(u_val)
if calc != calc.to_integral_value():
return None
total += int(calc)
else:
val = parse_sub_10000(prefix)
if val is None:
return None
total += val * u_val
# 단위가 붙지 않은 나머지 부분 처리
if rem:
val = parse_sub_10000(rem)
if val is None:
return None
# 앞선 단위보다 크거나 같은 잉여 숫자가 오지 못하도록 검증 (예: "억3" 오류 처리)
if last_unit_pos != -1 and val >= 10000:
return None
total += val
return -total if is_negative else total
class TestParseKRW(unittest.TestCase):
def test_basic_and_commas(self):
self.assertEqual(parse_krw("1,234원"), 1234)
self.assertEqual(parse_krw("1234"), 1234)
def test_korean_major_units(self):
self.assertEqual(parse_krw("3억 5천만원"), 350000000)
self.assertEqual(parse_krw("1조 2억"), 1000200000000)
def test_sub_units(self):
self.assertEqual(parse_krw("5천만"), 50000000)
self.assertEqual(parse_krw("2백만"), 2000000)
self.assertEqual(parse_krw("3십만"), 300000)
self.assertEqual(parse_krw("천원"), 1000)
def test_decimals(self):
self.assertEqual(parse_krw("1.5만"), 15000)
self.assertEqual(parse_krw("2.35억"), 235000000)
def test_spaces(self):
self.assertEqual(parse_krw(" 3 억 5천 만 원 "), 350000000)
def test_negatives(self):
self.assertEqual(parse_krw("-1,000원"), -1000)
self.assertEqual(parse_krw("마이너스 2만"), -20000)
def test_invalid_cases(self):
self.assertIsNone(parse_krw("1.23456만"))
self.assertIsNone(parse_krw(""))
self.assertIsNone(parse_krw("만만"))
self.assertIsNone(parse_krw("억3"))
self.assertIsNone(parse_krw("1,23원"))
self.assertIsNone(parse_krw("abc"))
self.assertIsNone(parse_krw("3억 2억"))
self.assertIsNone(parse_krw("5만 3억"))
if __name__ == "__main__":
unittest.main()
위 코드를 krw_parser.py 파일로 저장하신 후 터미널에서 python krw_parser.py 명령어를 입력하여 실행하시면 됩니다.
import re
import unittest
from decimal import Decimal, InvalidOperation
# 한글 큰 단위와 값
_BIG_UNITS = {"만": 10**4, "억": 10**8, "조": 10**12}
# 쉼표 없는 정수 또는 세 자리마다 쉼표가 올바르게 찍힌 정수를 잡음
_NUM_RE = re.compile(r"\d{1,3}(?:,\d{3})+|\d+")
# 큰 단위 앞에만 허용되는 소수(예: 1.5, 2.35)를 잡음
_DECIMAL_RE = re.compile(r"\d+\.\d+")
# 천·백·십 조합(예: 5천, 2백, 천, 3십, 1천2백3십4)을 잡음
_SMALL_KOR_RE = re.compile(r"(?:([1-9])?천)?(?:([1-9])?백)?(?:([1-9])?십)?(\d)?")
# 공백 문자 전체를 잡음
_SPACE_RE = re.compile(r"\s+")
# 큰 단위(조·억·만)를 기준으로 문자열을 나누기 위해 단위 글자를 잡음
_SPLIT_RE = re.compile(r"(조|억|만)")
def _parse_small(s: str) -> int | None:
"""큰 단위가 없는 부분(숫자 또는 천·백·십 조합)을 정수로 바꿉니다."""
if not s:
return None
# 일반 숫자(쉼표 포함)
if _NUM_RE.fullmatch(s):
return int(s.replace(",", ""))
# 천·백·십이 하나 이상 있어야 함
if not any(ch in s for ch in "천백십"):
return None
m = _SMALL_KOR_RE.fullmatch(s)
if not m:
return None
result = 0
# 각 단위가 실제로 쓰였는지 확인하고, 앞 숫자가 없으면 1로 처리
for unit_char, digit, mult in (("천", m.group(1), 1000),
("백", m.group(2), 100),
("십", m.group(3), 10)):
if unit_char in s:
result += (int(digit) if digit else 1) * mult
if m.group(4):
result += int(m.group(4))
return result
def parse_krw(text: str) -> int | None:
"""한국어 금액 문자열을 원 단위 정수로 바꿉니다. 해석할 수 없으면 None."""
if not isinstance(text, str):
return None
# 공백 전부 제거
s = _SPACE_RE.sub("", text)
# 부호 처리: 맨 앞의 "-" 또는 "마이너스"만 허용
negative = False
if s.startswith("-"):
negative = True
s = s[1:]
elif s.startswith("마이너스"):
negative = True
s = s[len("마이너스"):]
# 끝의 "원"은 있어도 없어도 됨
if s.endswith("원"):
s = s[:-1]
if not s:
return None
# [계수, 단위, 계수, 단위, ..., 나머지] 형태로 분리
parts = _SPLIT_RE.split(s)
total = 0
prev_unit = None # 직전 큰 단위 값
for i in range(0, len(parts) - 1, 2):
seg, unit_char = parts[i], parts[i + 1]
unit = _BIG_UNITS[unit_char]
# 같은 단위 반복 또는 작은 단위 뒤에 큰 단위가 오면 실패
if prev_unit is not None and unit >= prev_unit:
return None
if _DECIMAL_RE.fullmatch(seg):
# 소수는 큰 단위 앞에서만 허용, 결과가 정수여야 함
try:
value = Decimal(seg) * unit
except InvalidOperation:
return None
if value != value.to_integral_value():
return None
value = int(value)
else:
coef = _parse_small(seg)
if coef is None:
return None
value = coef * unit
# 이 구간의 값이 앞 단위 이상이 되면 실패
if prev_unit is not None and value >= prev_unit:
return None
total += value
prev_unit = unit
# 마지막 큰 단위 뒤에 남은 부분
tail = parts[-1]
if tail:
value = _parse_small(tail)
if value is None:
return None
# 단위 없는 숫자가 앞 단위보다 크거나 같으면 실패
if prev_unit is not None and value >= prev_unit:
return None
total += value
return -total if negative else total
class TestParseKrw(unittest.TestCase):
# 규칙 1: 숫자와 쉼표
def test_plain_numbers(self):
self.assertEqual(parse_krw("1,234원"), 1234)
self.assertEqual(parse_krw("1234"), 1234)
# 규칙 2: 한글 큰 단위 조합
def test_big_units(self):
self.assertEqual(parse_krw("3억 5천만원"), 350000000)
self.assertEqual(parse_krw("1조 2억"), 1000200000000)
# 규칙 3: 천·백·십
def test_small_units(self):
self.assertEqual(parse_krw("5천만"), 50000000)
self.assertEqual(parse_krw("2백만"), 2000000)
self.assertEqual(parse_krw("3십만"), 300000)
self.assertEqual(parse_krw("천원"), 1000)
# 규칙 4: 소수
def test_decimals(self):
self.assertEqual(parse_krw("1.5만"), 15000)
self.assertEqual(parse_krw("2.35억"), 235000000)
self.assertIsNone(parse_krw("1.23456만"))
# 규칙 5: 공백 무시
def test_spaces(self):
self.assertEqual(parse_krw(" 3 억 5천 만 원 "), 350000000)
# 규칙 6: 음수
def test_negative(self):
self.assertEqual(parse_krw("-1,000원"), -1000)
self.assertEqual(parse_krw("마이너스 2만"), -20000)
# 규칙 7: 해석 불가
def test_invalid(self):
self.assertIsNone(parse_krw(""))
self.assertIsNone(parse_krw("만만"))
self.assertIsNone(parse_krw("억3"))
self.assertIsNone(parse_krw("1,23원"))
self.assertIsNone(parse_krw("abc"))
self.assertIsNone(parse_krw("1.5"))
self.assertIsNone(parse_krw("1-000"))
# 규칙 8: 단위 중복 및 순서 오류
def test_unit_order(self):
self.assertIsNone(parse_krw("3억 2억"))
self.assertIsNone(parse_krw("5만 3억"))
if __name__ == "__main__":
unittest.main()
위 코드를 parse_krw.py로 저장하신 뒤 Python 3.10 이상에서 python parse_krw.py 명령으로 실행하시면 테스트가 수행됩니다.
점수는 공개 채점표로 매긴 값이고, 투표는 참고용이며 순위에 반영하지 않습니다. 투표하면 이 카테고리의 도구 이름이 공개됩니다.
2026.10.01 실측 기준 제미나이·클로드·그록이 100점으로 공동 1위입니다. 점수는 공개 채점표로 매깁니다.
목록 17개 중 16개에 무료 플랜이 있습니다. 상단 "무료만 보기"를 켜면 무료 도구만 남고, 무료 범위는 "무료 있음" 배지에 적혀 있습니다.
실측에 쓴 프롬프트 원문을 위 실측 표에서 펼쳐 볼 수 있습니다. 원하는 형식(표·항목·길이)과 어투(존댓말)를 문장으로 명시하고, "원문에 없는 내용은 넣지 마세요"를 덧붙이면 대부분의 도구에서 결과가 안정됩니다.