"""
==============
Text Util
==============
외부에서 온 문자열에서 다음과 같은 처리를 합니다.
1. 문자열에서 모두 xxxx를 제거 후 반환 합니다.
.. code-block::
def remove_xxxx(text: str, default: Optional[str] = None):
...
2. 문자열 처음과 마지막 에서만 제거 후 반환 합니다.
.. code-block::
def strip_xxxx(text: str, default: Optional[str] = None):
...
3. 문자열에서 xxxx 형태를 추출하여 반환 합니다.
.. code-block::
def convert_xxxx(text: str, default: Optional[str] = None):
...
4. 문자열에서 xxx를 추출하여 반환 합니다.
.. code-block::
def extract_xxxx(text: str, default: Optional[str] = None):
...
"""
import re
from datetime import datetime
from decimal import Decimal
from typing import Optional, Union
from gpp.datetimes import dt
HANGUL_PATTERN = re.compile('[가-힣|ㄱ-ㅎ|ㅏ-ㅣ]+')
TRIM_CHARS_PATTERN = re.compile(r'(\s*)')
NUMBER_PATTERN = re.compile(r'[^0-9\-\.]')
WHITESPACE_CHARS = '\n\r\t\b\\\u200b '
[docs]def is_integers(*args) -> bool:
try:
if len(args) > 0 and all([float(n).is_integer() for n in args]):
return True
except TypeError:
return False
except ValueError:
return False
return False
[docs]def is_numbers(*args) -> bool:
try:
if len(args) > 0 and all([isinstance(float(n), float) for n in args]):
return True
except TypeError:
return False
except ValueError:
return False
return False
[docs]def remove_spaces(text: str) -> str:
"""
주어진 문자열에서 ('\\n', '\\r', '\\t', '\\b, '\\u200b', ' ' ) 문자들을 제거 합니다.
:param text: 외부 문자열
:type text: str
:return: 변환된 문자열
:rtype: Optional[str]
:Example:
>>> from gpp.texts import remove_spaces
>>> remove_spaces('a')
'a'
>>> remove_spaces(' ')
''
>>> remove_spaces(' a ')
'a'
>>> remove_spaces('\t\t\t\t\t\t\t\t\t\t\b a ')
'a'
>>> remove_spaces('\\n\\r\\t\\b \\u200b가\\n\\r\\t\\b \\u200b나\\n\\r\\t\\b \\u200b다\\n\\r\\t\\b \\u200b라\\n\\r\\t\\b \\u200b')
'가나다라'
>>> remove_spaces(-1)
'-1'
>>> remove_spaces(1)
'1'
"""
if not isinstance(text, str):
text = str(text or '')
for p in WHITESPACE_CHARS:
text = text.replace(p, '')
return text
[docs]def convert_words(text: str):
"""
단어들 사이에 있는 중복된 구분자들을 하나로 만듭니다.
:param text: 외부 문자열
:type text: str
:param default: 오류 발생시 기본값
:type default: Optional[str]
:return: 변환된 문자열
:rtype: Optional[str]
:Example:
>>> from gpp.texts import convert_words
>>> convert_words('가 나')
'가 나'
"""
if not isinstance(text, str):
text = str(text)
return ' '.join(
a for a in text.strip(WHITESPACE_CHARS).replace('\xa0', ' ').split() if a
)
[docs]def convert_date(text: str, default: Optional[datetime.date] = None) -> datetime.date:
"""
text에서 년월일을 추출 합니다.
:param text: 외부 문자열
:type text: str
:param default: 오류 발생시 기본값
:type default: Optional[datetime.date]
:return: 년월일
:rtype: Optional[datetime.date]
:Example:
>>> from gpp.texts import convert_date
>>> convert_date('2023-01-01')
datetime.date(2023, 1, 1)
>>> convert_date('2023.01.01')
datetime.date(2023, 1, 1)
>>> convert_date('20230101')
datetime.date(2023, 1, 1)
"""
if not isinstance(text, str):
text = str(text)
text = TRIM_CHARS_PATTERN.sub('', text)
text = remove_spaces(text)
text = text.replace('.', '-').replace('\xa0', '')
# format yyyymmdd
if is_integers(text) and len(text) == 8:
text = f'{text[:4]}-{text[4:6]}-{text[6:8]}'
ret = text.split('-')
if len(ret) == 3 and is_integers(*ret):
year, month, day = tuple(map(int, ret))
ret = dt(year=year, month=month, day=day)
if ret:
return ret.date()
return default
[docs]def convert_integer(text: str, default: Optional[int] = None):
"""
text에서 정수를 추출하여 반환 합니다.
:param text: 외부 문자열
:type text: str
:param default: 오류 발생시 기본값
:type default: Optional[int]
:return: 정수
:rtype: Optional[int]
:Example:
>>> from gpp.texts import convert_integer
>>> convert_integer('584')
584
>>> convert_integer('서울02', 11)
11
>>> convert_integer(' 02')
2
"""
number = convert_decimal(text, default)
if isinstance(number, Decimal):
return int(number)
return default
[docs]def convert_decimal(text: str, default: Optional[Decimal] = None):
"""
text에서 실수(소수점을 포함)를 추출하여 반환 합니다.
:param text: 외부 문자열
:type text: str
:param default: 오류 발생시 기본값
:type default: Optional[Decimal]
:return: decimal
:rtype: Optional[Decimal]
:Example:
>>> from gpp.texts import convert_decimal
>>> convert_decimal('584.8701610')
Decimal('584.8701610')
>>> convert_decimal('-123.456789')
Decimal('-123.456789')
"""
text = remove_spaces(text)
if is_numbers(text):
num = Decimal(str(text))
if num.is_finite():
return num
return default
[docs]def convert_money(text: str, unit: int = 1, default: Optional[int] = None) -> int:
"""
text에서 정수를 추출하여 반환 합니다.
:param text: 외부 문자열
:type text: str
:param unit: 단위
:type unit: int
:param default: 오류 발생시 기본값
:type default: Optional[int]
:return: 정수
:rtype: Optional[int]
:Example:
>>> from gpp.texts import convert_money
>>> convert_money('10,000', 1000, None) # 단위 천원
10000000
"""
try:
val = remove_spaces(text).split('.')
if len(val) <= 2:
val[0] = val[0].replace(',', '')
return int(val[0]) * unit
except Exception:
pass
return default
[docs]def convert_readable_filesize(num: Union[int, float], suffix="B") -> str:
"""
파일크기 num을 사람이 읽을 수 있는 문자열로 변환하여 반환 합니다.
:param num: 파일크기
:type num: Union[int, float
:param suffix: 문자열 끝에 붙일 첨자
:return: 사람이 읽을 수 있는 문자열
:rtype: str
:Note:
10^3이 아닌, 2^10으로 나눕니다.
:Example:
>>> from gpp.texts import convert_readable_filesize
>>> convert_readable_filesize(10000, 'B')
'9.8KiB'
>>> convert_readable_filesize(1234567890.99, 'B')
'1.1GiB'
>>> convert_readable_filesize(12345678901234.99, 'B')
'11.2TiB'
>>> convert_readable_filesize(123456789012345678.99, 'B')
'109.7PiB'
"""
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(num) < 1024.0:
return f"{num:3.1f}{unit}{suffix}"
num /= 1024.0
return f"{num:.1f} Yi{suffix}"
[docs]def convert_readable_count(num: Union[int, float]) -> str:
"""
수량 num을 사람이 읽을 수 있는 문자열로 변환하여 반환 합니다.
:param num: 수량
:type num: Union[int, float
:rtype: str
:Note:
2^10이 아닌, 10^3으로 나눕니다.
:Example:
>>> from gpp.texts import convert_readable_count
>>> convert_readable_count(10000)
'10.0K'
>>> convert_readable_count(1234567890.99)
'1.2G'
>>> convert_readable_count(1234567890123)
'1.2T'
>>> convert_readable_count(12345678901234.99)
'12.3T'
>>> convert_readable_count(123456789012345678.99)
'123.5P'
"""
for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
if abs(num) < 1000:
return f"{num:3.1f}{unit}"
num /= 1000
return f"{num:3.1f}Y"
[docs]def convert_readable_timedelta(seconds: int) -> str:
"""
seconds(시간의 초)를 사람이 읽을 수 있는 문자열로 변환하여 반환 합니다.
:param seconds: 수량
:type seconds: int
:rtype: str
:Note:
소수점은 버림 합니다.
:Example:
>>> from gpp.texts import convert_readable_timedelta
>>> convert_readable_timedelta(-1)
'1초 전'
>>> convert_readable_timedelta(0)
'지금'
>>> convert_readable_timedelta(1)
'1초 후'
>>> convert_readable_timedelta(10000)
'2시간 후'
>>> convert_readable_timedelta(10000000)
'3개월 후'
>>> convert_readable_timedelta(86400)
'1일 후'
>>> convert_readable_timedelta(86399)
'23시간 후'
>>> convert_readable_timedelta(1234567890)
'39년 후'
"""
if seconds == 0:
return '지금'
elif seconds < 0:
future = '전'
seconds = -seconds
else:
future = '후'
unit = [1, 60, 60, 24, 30, 12]
suffix = ['초', '분', '시간', '일', '개월', '년']
ret = ''
for u, s in zip(unit, suffix):
if seconds >= u:
seconds /= u
ret = '{0}{1} {2}'.format(int(seconds), s, future)
else:
break
return ret