[python] 날짜, 시간관련 모듈

파이썬에서 날짜와 시간을 다루는 방법을 알아보자.

지금 현재의 날짜와 시간을 문자열로 출력하려면 strftime 메서드를 이용하면 된다.

import datetime

now = datetime.datetime.now()
print(now)          # 2015-04-19 12:11:32.669083

nowDate = now.strftime('%Y-%m-%d')
print(nowDate)      # 2015-04-19

nowTime = now.strftime('%H:%M:%S')
print(nowTime)      # 12:11:32

nowDatetime = now.strftime('%Y-%m-%d %H:%M:%S')
print(nowDatetime)  # 2015-04-19 12:11:32

날찌, 시간형식의 문자열을 datetime으로 만들려면 strptime을 이용하자.

import datetime

myDatetimeStr = '2015-04-15 12:23:38'
myDatetime = datetime.datetime.strptime(myDatetimeStr, '%Y-%m-%d %H:%M:%S')
print(type(myDatetime)) # [class 'datetime.datetime']
print(myDatetime)       # 2015-04-15 12:23:38

날짜나 시간을 변경하기 위해서는 replace 메서드를 사용하면 된다.

import datetime

myDatetime = datetime.datetime.strptime('2015-04-15 12:23:38', '%Y-%m-%d %H:%M:%S')
print(myDatetime)   # 2015-04-15 12:23:38

yourDatetime = myDatetime.replace(day=16)
print(myDatetime)   # 2015-04-15 12:23:38
print(yourDatetime) # 2015-04-16 12:23:38

날짜만을 관리하기 위해서는 datetime.date를, 시간만을 관리하기 위해서는 datetime.time을 이용하면 된다. datetime.date와 datetime.time을 합치기 위해서는 datetime.datetime.combine을 이용하자.

import datetime

d = datetime.date(2015, 4, 15)
t = datetime.time(12, 23, 38)

dt = datetime.datetime.combine(d, t)
print(dt) # 2015-04-15 12:23:38

datetime의 각 날짜와 시간에 관련된 항목값에 접근하려면 timetuple 메서드를 사용하면 된다.

import datetime

now = datetime.datetime.now()
nowTuple = now.timetuple()
print(nowTuple)         # time.struct_time(tm_year=2015, tm_mon=4, tm_mday=19, tm_hour=13, tm_min=21, tm_sec=40, tm_wday=6, tm_yday=109, tm_isdst=-1)
print(nowTuple.tm_year) # 2015

날짜, 시간 연산을 해보자. datetime에 하루(1day)를 더하고 싶다면 datetime.timedelta를 이용하자.

import datetime

now = datetime.datetime.now()
print(now)      # 2015-04-19 12:40:00.320686

tomorrow = now + datetime.timedelta(days=1)
print(tomorrow) # 2015-04-20 12:40:00.320686

timedelta에 들어갈 수 있는 인자값은 아래와 같다.
– 1주 : datetime.timedelta(weeks=1)
– 1일 : datetime.timedelta(days=1)
– 1시간 : datetime.timedelta(hours=1)
– 1분 : datetime.timedelta(minutes=1)
– 1초 : datetime.timedelta(seconds=1)
– 1밀리초 : datetime.timedelta(milliseconds=1)
– 1마이크로초 : datetime.timedelta(microseconds=1)

timedelta로 5시간 30분을 표현하면 datetime.timedelta(hours=5, minutes=30)이라고 하면 된다. 이것을 초(second) 단위로 변경하려면 total_seconds 메서드를 호출하면 초단위로 쉽게 변경할 수 있다.

datetime에서 datetime을 빼면 timedelta 값을 얻을 수 있다.

import datetime

oneDatetime = datetime.datetime.strptime('2015-04-15 00:00:00', '%Y-%m-%d %H:%M:%S')
twoDatetime = datetime.datetime.strptime('2015-04-16 00:00:10', '%Y-%m-%d %H:%M:%S')
result = twoDatetime - oneDatetime
print(result)         # 1 day, 0:00:10
print(result.days)    # 1
print(result.seconds) # 10

[python] 정규표현식 패턴 찾기

python에서 많이 사용되는 정규표현식 사용법에 대해서 간단히 알아보자.

패턴에 매치되는 하나의 텍스트만 추출하기

패턴에 매치되는 텍스트가 여러 개가 있어도 처음에 나오는 하나만 추출해야 되는 경우가 있다. search를 이용하면 패턴에 매치되는 제일 처음의 텍스트를 추출할 수 있다.

import re

pattern = 'book'
text = 'This is a book. That is a book.'

match = re.search(pattern, text)
startIndex = match.start()
endIndex = match.end()

result = 'startIndex: {}, endIndex: {}, find: {}'.format(
	startIndex,
	endIndex,
	text[startIndex:endIndex]
)
print(result)

반복적인 매칭 작업에는 패턴을 미리 컴파일해서 시간을 단축시킬 수 있다. 아래 예제는 위와 동일하다.

import re
 
pattern = 'book'
text = 'This is a book. That is a book.'

r = re.compile(pattern)
 
match = r.search(text)
startIndex = match.start()
endIndex = match.end()
 
result = 'startIndex: {}, endIndex: {}, find: {}'.format(
    startIndex,
    endIndex,
    text[startIndex:endIndex]
)
print(result)

패턴에 매치되는 여러 텍스트 추출하기

여러 텍스트를 추출하기 위해서는 findall이나 finditer 메서드를 사용해야 한다. findall을 사용하면 매치되는 텍스트들을 리스트 형태로 리턴해 준다. finditer를 사용하면 match 객체를 iterator로 리턴해 준다.

findall을 사용하는 예제부터 살펴보자.

import re
 
pattern = '\d+'
text = '10, 20, 30, 40'

r = re.compile(pattern)

results = r.findall(text)
print(results)

만약 패턴 내용에 그룹을 지어주는 괄호를 사용했다면 리스트 요소들은 문자열이 아닌 문자열을 묶고 있는 튜플을 리턴한다.

finditer를 사용하는 예제는 아래와 같다.

import re
 
pattern = '\d+'
text = '10, 20, 30, 40'

r = re.compile(pattern)

matches = r.finditer(text)
for m in matches:
	startIndex = m.start()
	endIndex = m.end()
    print(m.span(), '->', text[startIndex:endIndex])

검색될 내용의 앞 부분부터 패턴이 일치해야 하는 경우

검색될 내용의 앞 부분부터 패턴이 일치해야 하는 경우 위치지정자인 캐럿(^)을 패턴의 제일 첫 부분에 사용하여 조건을 지정할 수 있다. 앞을 의미하는 위치지정자 캐럿(^)을 사용하지 않고 간단히 match를 사용하면 동일한 결과를 얻을 수 있다.

import re

pattern = 'is'
text = 'This is a book'

r = re.compile(pattern)

match = r.match(text)
print(match)

결과값을 출력해 보면 None이 출력되는데 아무 것도 매칭된 것이 없다는 것이다. 패턴을 “is”가 아닌 “This”로 변경하여 실행해 보면 검색될 내용의 첫 부분과 일치하기 때문에 match의 값이 출력되는 것을 확인할 수 있다.

패턴 일부분을 그룹화하여 추출하기

패턴에 괄호를 이용하여 특정 부분의 문자열을 쉽게 추출할 수 있다. 휴대폰 전화번호 형식에서 앞자리를 제외한 중간자리 숫자와 마지막자리 숫자를 추출해야 된다고 가정해보자. 010-1234-2345라는 번호에서 중간자리인 1234와 마지막자리인 2345가 필요한 경우이다. 괄호를 이용해서 그룹화하고 매치된 결과에서 groups()를 이용해서 튜플형태로 추출할 수 있다.

import re

pattern = r'\d+-(\d+)-(\d+)'
text = '010-1234-2345 010-2345-3456'

r = re.compile(pattern)
match = r.search(text)
print(match.groups()) # ('1234', '2345')
print(match.group(0)) # 010-1234-2345
print(match.group(1)) # 1234
print(match.group(2)) # 2345

[python] 문자열 템플릿

바로 이전 포스트에서 문자열 포맷에 대해서 알아봤다.
문자열 포맷에서는 형식지정자를 이용하면 다양한 형태의 문자열을 얻을 수 있다.

문자열의 템플릿을 이용하면 문자열 포맷처럼 형식지정자를 지정할 수 없지만 특정 위치의 글자를 쉽게 치환할 수 있다.

import string

p = {'name': 'John', 'age': 21}
tpl = string.Template('''
    name: $name
    age: $age - ${age}s
''')
n = tpl.substitute(p)
print(n)

예제를 보면 알겠지만 웹서버 언어인 PHP에서 문자열 내에 변수값을 넣는 방법과 동일한 것을 알 수 있다.

문자열 템플릿의 safe_substitute 메서드를 사용하면 매칭되지 않는 템플릿 변수가 있어도 에러를 내지 않고 있는 그대로 출력해준다.(아래 예제에서 email이라는 변수값이 없기때문에 substitute를 사용하면 에러가 난다.)

import string

p = {'name': 'John', 'age': 21}
tpl = string.Template('''
    name: $name
    age: $age - ${age}s
    email: $email
''')
n = tpl.safe_substitute(p)
print(n)

[python] 다양한 문자열 포맷

python은 여러 형태의 인수를 받을 수 있는 문자열 format 메서드를 가지고 있다.
그러면 하나씩 알아보자.

  • 인자값의 순서에 맞게 포맷팅

    print('{0}, {1}!'.format('Hello', 'World'))
    print('{}, {}!'.format('Hello', 'World'))
    # 'Hello, World!'
    
    print('{2}, {1}, {0}'.format(100, 200, 300))
    # '300, 200, 100'
    
    print('{:06d}'.format(10))
    # '000010'
    
    coord = (10, 20)
    print('x: {0[0]}, y: {0[1]}'.format(coord))
    # 'x: 10, y: 20'
    
    person = {'name': 'John', 'age': 21}
    print("{0[name]}'s age is {0[age]} years old".format(person))
    # "John's age is 21 years old"
    
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    p = Person('John', 21)
    print("{0.name}'s age is {0.age} years old".format(p))
    # "John's age is 21 years old"
    
  • 인자명에 맞게 포맷팅

    print('x: {x}, y: {y}'.format(x=10, y=20))
    coord = {'x': 10, 'y': 20}
    print('x: {x}, y: {y}'.format(**coord))
    # 'x: 10, y: 20'
    
  • % 연산자를 이용한 dictionary 데이터 연결

    p = {'name': 'John', 'age': 21}
    print("%(name)s's age is %(age)d years old" % p)
    # "John's age is 21 years old"
    

[python] 여러 개의 문자(character) 변환하기

프로그래밍을 하다가 보면 여러 개의 문자(character)를 변환해야 하는 경우가 있다. python에서는 str.translate 메서드로 이러한 작업을 간단하게 할 수 있게 해준다.

‘a’를 ‘@’로 바꿔야 하는 간단한 예제를 살펴보자.

s = 'apple'
n = s.translate({ ord('a'): '@' })

str.translate 메서드의 인자값은 dictionary를 넣으면 되는데, dictionary의 키(key)에는 변환시킬 문자(old)를 넣고 값(value)에는 대치시킬 문자(new)를 넣으면 된다. 한 가지 주의해야할 사항은 dictionary의 키(key)에 값을 넣을 때는 해당 문자의 유니코드값(Unicode ordinals)을 넣어야 한다.(예제에서 ‘a’ 문자에 ord 함수를 사용한 것에 대해서 주목하라.)

 

그러면 여러 개의 문자를 변환해야 되는 예제를 보도록 하자.

  • a ➔ @
  • c ➔ ©
  • p ➔ ℗
  • r ➔ ®
s = 'apple copyright'
n = s.translate({ ord('a'): '@', ord('c'): '©', ord('p'): '℗', ord('r'): '®' })

 

ord 함수가 dictionary에 반복적으로 나옴으로써 코드의 가독성을 떨어뜨린다.
아래와 같이 zip 함수와 for문을 이용하여 반복을 제거하도록 하자.

oldChars = 'acpr'
newChars = '@©℗®'
s = 'apple copyright'
n = s.translate({ ord(x): y for (x, y) in zip(oldChars, newChars) })

변환해야 하는 문자가 늘어난다고 해도 더 이상 ord 함수를 반복적으로 작성할 필요가 없다. 이제는 oldChars 변수와 newChars 변수에 변환할 문자를 순서에 맞게 추가하기만 하면 된다.