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로 실행하시면 됩니다.