파이썬 문자열 포맷

파이썬 문자열 다루기

파이썬 문자열 포맷 방식

파이썬에서 문자열 포맷을 지정하는 방식은 3가지가 있습니다.

1. % 연산자

2. format() 메서드

3. 리터럴 문자열 'f' 

 

첫 번째 % 연산자를 사용하는 것을 오래된 사용 방식 "구 방식"이라고 칭하기도 합니다. 필자가 파이썬 구 버전에서 즐겨 사용하던 포맷 방식입니다. 문자열이 길어지면 가독성이 떨어지는 단점이 있었지만 C언어의 방식과 비슷하여 애착이 가는 방식입니다. 파이썬 공식 문서에서 % 연산자를 권장하지 않는다고 표기하고 있습니다.

 

두 번째 format() 메서드는 구 방식을 개선하기 위해 파이썬 3 버전에서 새롭게 선보인 포맷 방식입니다. 하지만 이 방식 또한 % 연사자와 마찬가지로 문자열이 길어지면 가독성이 현저하게 떨어집니다.

 

세 번째 포맷 문자열 리터럴 'f'는 % 연산자와 format() 메서드의 단점을 보안하기 위해 파이썬 3.6 이상부터 지원하는 방식입니다. % 연산자와 format() 메서드처럼 문자열에 치환자를 넣고 뒤에 치환할 변수를 선언하는 방식은 위에서 언급했듯이 문자열이 길어지면 가독성이 현저하게 떨어지는 단점이 있습니다. 반면에 문자열 리터럴 방식은 문자열 안에 바로 치환할 변수명을 사용할 수 있어 문자열이 길어 저도 직관적으로 코드를 파악할 수 있습니다. 자세한 설명은 뒷부분에서 하겠습니다.

 

 

파이썬 %연산자

% 연산자를 설명을 생략할까도 생각해 봤지만 오래된 코드를 분석할 때 도움이 될 수 있어서 생략하지 않겠습니다. 다만 추천하지 않는 포맷 방식이니 새롭게 파이썬을 익히시는 분이라면 그냥 문자열 포맷을 이런 방식으로도 하는구나 정도로 눈으로 보고 넘어가시면 되겠습니다.

% 연산자를 사용하여 포맷을 하기 위해서는 문자열 포맷 코드를 알고 있어야 합니다. 마치 C언어에서 문자열을 출력하기 위해 대입하는 변수의 타입을 지정하는 것과 비슷합니다.

코드 설명
%c 한개의 문자를 나타냅니다.
%s 문자열을 나타냅니다.
%d 정수형을 나타냅니다.
%f 부동소수를 나타냅니다.
%o 8진수를 나타냅니다.
%x 16진수를 나타냅니다.
%% 리터럴 %문자를 나타냅니다.

 

 

공백과 정렬

공백은 위에 명시한 코드 사이에 넓이를 지정할 숫자를 명시하게 되면 해당 개수만큼 공백을 표현할 수 있습니다. 공백이 표현하고자 하는 데이터보다 클 때는 기본적으로 오른쪽 정렬을 합니다. 만약 왼쪽 정렬을 하고 싶다면 -부호를 사용하시면 됩니다.

# 공백 20칸과 문자열 기본 오른쪽 정렬
>>> "%20s"%("CaptainBIN")
'          CaptainBIN'

# 공백 20칸과 -부호로 문자열 왼쪽 정렬
>>> "%-20s"%("CaptainBIN")
'CaptainBIN          '

# 지정한 공백 보다 문자열이 더 크면
# 오버플로우 됩니다.
>>> "%3s"%("CaptainBIN")
'CaptainBIN'

# 문자열 자르기
>>> "%.3s"%("CaptainBIN")
'Cap'

# 공백을 주고 문자열 자르기
>>> "%10.3s"%("CaptainBIN")
'       Cap'

 

 

소수점

소수점 표현 방식이 위에서 언급한 문자열 자르기와 비슷합니다. 아래에 예제에서 사용한 %7.2f의 의미는 전체 7개의 공백 중에서 float형태의 데이터를 소수점 2자리까지 표현하라는 의미입니다. 

# 기본 소수점 표현
# 소수점은 7번째 수를 반올림하여 6번째 자리까지 표현
>>> "%f"%(123.1234567890)
'123.123457'

# 7개의 공백과 소수점 둘째 자리 까지 표현
>>> "%7.2f"%(123.1234567890)
' 123.12'

# 소수점 두 자리 표현 시 반올림
>>> "%7.2f"%(123.125)
' 123.12'
>>> "%7.2f"%(123.126)
' 123.13'

# 숫자 왼쪽 정렬
>>> "%-7.2f"%(123.12345)
'123.12 '

 

간단히 % 연산자 포맷을 알아보았습니다. 이 방법은 데이터 타입을 알고 있고 간단한 문자열 포맷을 생성할 때 효과적으로 사용할 수 있습니다. 그렇지만 다시 한번 강조하자면 이제는 권장 사항이 아닙니다. 이 방법을 사용할지 안 할지 선택사항은 여러분께 맡기겠습니다.

 

 

파이썬 format() 메서드

foramt() 메서드는 % 연산자와 마찬가지로 문자열에 치환할 부분을 {}를 사용하여 문자열을 포맷팅 합니다. format() 메서드 설명은 주절주절 설명하는 것보다 예제를 확인하는 것이 제일 좋을 것입니다.

###############################
## 기본 사용법
###############################
# 문자열 포매팅
>>> "{}".format("CaptainBIN")
'CaptainBIN'

# 연산 사용
>>> "{}".format(10 + 20)
'30'

# 숫자 포매팅
>>> "{}".format(123)
'123'

# 소수 포매팅
>>> "{}".format(1234567890.1234567890)
'1234567890.1234567'


###############################
## 자동 넘버링
###############################
# 넘버링을 안 하면 format 안의 값을 순차적으로 읽음
>>> "{}".format('a','b','c')
'a'
>>> "{} | {}".format('a','b','c')
'a | b'
>>> "{} | {} | {}".format('a','b','c')
'a | b | c'

# 인자값보다 많은 {}구문을 사용할 경우
# 에러 발생
>>> "{} | {} | {} | {}".format('a','b','c')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range


###############################
## 넘버링
###############################
>>> "{0} | {1} | {2}".format('a','b','CaptainBIN')
'a | b | CaptainBIN'

# 넘버링을 중간에 안 할 경우
# 에러 발생
>>> "{0} | {1} | {}".format('a','b','c')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: cannot switch from manual field specification to automatic field numbering

# 넘버링으로 순서 바꾸기
>>> "{2} | {1} | {0}".format('a','b','c')
'c | b | a'
>>> "{2} | {0} | {0}".format('a','b','c')
'c | a | a'


###############################
## 언 패킹 인수 시퀀스
###############################
# {} 개수보다 인수 값이 더 적을 경우
# 에러 발생
>>> "{} | {} | {}".format('CaptainBIN')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
>>>

# * 기호를 사용하여 인수 언패킹
>>> "{} | {} | {}".format(*'CaptainBIN')
'C | a | p'


###############################
## 인수 이름 사용
###############################
# 인수에 이름을 지정하여 사용
>>> "{name} {kind}".format(name="CaptainBIN", kind="BLOG")
'CaptainBIN BLOG'

# 이름순서와 상관없이 사용 가능
>>> "{name} {kind}".format(kind="BLOG", name="CaptainBIN")
'CaptainBIN BLOG'


###############################
## 자료형 응용
###############################
# 딕셔너리 자료형 사용
>>> fmt_dic = {'name' : 'CaptainBIN', 'kind' : 'BLOG'}
>>> "{name} {kind}".format(**fmt_dic)
'CaptainBIN BLOG'

# 리스트 자료형 사용
>>> fmt_list = ['CaptainBIN', 'BLOG']
>>> "{0} {0} {1}".format(*fmt_list)
'CaptainBIN CaptainBIN BLOG'

# 리스트와 딕셔너리 혼합형
>>> fmt_mix = [{'name' : 'CaptainBIN', 'kind' : 'BLOG'}, \
... {'name' : 'CaptainBIN_2' , 'kind' : 'BLOG_2'}]
>>> "{0[name]} {0[kind]}".format(*fmt_mix)
'CaptainBIN BLOG'
>>> "{1[name]} {1[kind]}".format(*fmt_mix)
'CaptainBIN_2 BLOG_2'
>>> "{0[name]} {1[kind]}".format(*fmt_mix)
'CaptainBIN BLOG_2'


###############################
## 공백과 정렬
###############################
# 기본 왼쪽 정렬
>>> "{:25}".format("CaptainBIN")
'CaptainBIN         '

# < 기호의 왼쪽 정렬
>>> "{:<25}".format("CaptainBIN")
'CaptainBIN               '

# > 기호의 오른쪽 정렬
>>> "{:>25}".format("CaptainBIN")
'               CaptainBIN'

# ^ 기호의 가운데 정렬
>>> "{:^25}".format("CaptainBIN")
'       CaptainBIN        '


###############################
## 채움과 정렬
###############################
# 왼쪽 정렬 후 공백 *로 채움
>>> "{:*<25}".format("CaptainBIN")
'CaptainBIN***************'

# 오른쪽 정렬 후 공백 *로 채움
>>> "{:*>25}".format("CaptainBIN")
'***************CaptainBIN'

# 가운데 정렬 후 공백 *로 채움
>>> "{:*^25}".format("CaptainBIN")
'*******CaptainBIN********'

# 정렬 기호 없이 채움 문자 단독사용 불가
>>> "{:*25}".format("CaptainBIN")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier


###############################
## 복합 사용
###############################
>>> "{0:.^{1}}".format("CaptainBIN",20)
'.....CaptainBIN.....'


###############################
## 숫자 부호 표현
###############################
# + 기호 사용으로 항상 부호 표시
>>> "{:+d} | {:+d}".format(20, -20)
'+20 | -20'

# - 기호 사용으로 음수일 때만 기호 표시
>>> "{:-d} | {:-d}".format(20, -20)
'20 | -20'

# 기호만 앞으로 정렬
>>> "{:=+5d} | {:=+5d}".format(20, -20)
'+  20 | -  20'

# 공백 0로 채우기
>>> "{:+05d} | {:+05d}".format(20, -20)
'+0020 | -0020'


###############################
## 소수점 부호 표현
###############################
# + 기호 사용과 소수점 자릿수 포맷
>>> "{:+7.3f} | {:+7.3f}".format(123.12345, -123.12345)
'+123.123 | -123.123'

# - 기호 사용과 소수점 자릿수 포맷
>>> "{:-7.3f} | {:-7.3f}".format(123.12345, -123.12345)
'123.123 | -123.123'


###############################
## 지수 표기
###############################
# 지수를 소문자 e로 표기
>>> "{:e}".format(1234567890.12345)
'1.234568e+09'

# 지수를 대문자 E로 표기
>>> "{:E}".format(1234567890.12345)
'1.234568E+09'


###############################
## 백분율 표기
###############################
# 나눗셈 연산 후 100을 곱해 백분율 표기
>>> "{0} | {0:%}".format(20/100)
'0.2 | 20.000000%'


###############################
## 의미 없는 소수점 삭제
###############################
# 기본적으로 .0이 표시됨
>>> "{}".format(1234.00000000)
'1234.0'

# 의미 없는 .0을 제거
>>> "{:g}".format(1234.00000000)
'1234'


###############################
## 숫자 천단위 구분
###############################
# 정수 천단위 구분
>>> '{:,}'.format(1234567890)
'1,234,567,890'

# 부동소수점 천단위 구분
# 소수점 둘째 자리만 표현
>>> '{:,.2f}'.format(1234567890.12345)
'1,234,567,890.12'


###############################
## 진법 변환
###############################
# 각 진법별로 십진수 52값 변환
>>> "dec={0:d} | hex={0:x} | oct={0:o} | bin={0:b}".format(52)
'dec=52 | hex=34 | oct=64 | bin=110100'

# 진법 접두어 표기
>>> "dec={0:#d} | hex={0:#x} | oct={0:#o} | bin={0:#b}".format(52)
'dec=52 | hex=0x34 | oct=0o64 | bin=0b110100'

# 헥사 값 대소문자 구분
>>> "{0:x} | {0:X}".format(255)
'ff | FF'

###############################
## 유니코드 변환
###############################
# 정수형을 유니코드 변환
>>> "{0:c}{1:c}{2:c}".format(99, 97, 112)
'cap'


###############################
## 날짜 포맷
###############################
>>> from datetime import datetime as dt
>>> '{:%Y/%m/%d %H:%M:%S}'.format(dt.now())
'2019/09/18 16:35:10'


###############################
## 중괄호 표기
###############################
# 각각 중괄호를 2회 사용하여 중괄호 표기
>>> "{{ {0} }}".format("CaptainBIN")
'{ CaptainBIN }'

 

 

파이썬 리터럴 문자열 'f'

리터럴 문자열은 위에 알아본 두 가지 방법과는 다르게 문자열 뒤에 별도로 치환하는 내용이 없습니다. 대신 문자열 리터럴이라는 것을 알리기 위해 문자열 앞에 f를 붙이는 것이 큰 차이점입니다. 자세한 사항은 예제로 알아보겠습니다.

###############################
## 기본 사용법
###############################
# 문자열 변수 치환
>>> var = "CaptainBIN"
>>> f"블로그의 이름은 {var}입니다."
'블로그의 이름은 CaptainBIN입니다.'

# 연산 가능
>>> f"1 + 1 = {1+1}"
'1 + 1 = 2'


###############################
## 자료형 사용
###############################
# 딕셔너리 자료형 사용
>>> fmt_dic = {'name' : 'CaptainBIN', 'kind' : 'BLOG'}
>>> f"{fmt_dic['name']} | {fmt_dic['kind']}"
'CaptainBIN | BLOG'

# 리스트 자료형 사용
>>> fmt_list = ['CaptainBIN', 'BLOG']
>>> f"{fmt_list[0]} | {fmt_list[1]}"
'CaptainBIN | BLOG'

# 리스트와 딕셔너리 혼합형
>>> fmt_mix = [{'name' : 'CaptainBIN', 'kind' : 'BLOG'}, \
... {'name' : 'CaptainBIN_2' , 'kind' : 'BLOG_2'}]
>>>
>>> f"{fmt_mix[0]['name']} | {fmt_mix[0]['kind']}"
'CaptainBIN | BLOG'
>>> f"{fmt_mix[1]['name']} | {fmt_mix[1]['kind']}"
'CaptainBIN_2 | BLOG_2'
>>> f"{fmt_mix[0]['name']} | {fmt_mix[1]['kind']}"
'CaptainBIN | BLOG_2'



###############################
## 공백과 정렬
###############################
# 기본 왼쪽 정렬
>>> var = "CaptainBIN"
>>> f"{var:25}"
'CaptainBIN               '

# < 기호의 왼쪽 정렬
>>> var = "CaptainBIN"
>>> f"{var:<25}"
'CaptainBIN               '

# > 기호의 오른쪽 정렬
>>> var = "CaptainBIN"
>>> f"{var:>25}"
'               CaptainBIN'

# ^ 기호의 가운데 정렬
>>> var = "CaptainBIN"
>>> f"{var:^25}"
'       CaptainBIN        '


###############################
## 채움과 정렬
###############################
# 왼쪽 정렬 후 공백 *로 채움
>>> var = "CaptainBIN"
>>> f"{var:*<25}"
'CaptainBIN***************'

# 오른쪽 정렬 후 공백 *로 채움
>>> var = "CaptainBIN"
>>> f"{var:*>25}"
'***************CaptainBIN'

# 가운데 정렬 후 공백 *로 채움
>>> var = "CaptainBIN"
>>> f"{var:*^25}"
'*******CaptainBIN********'


###############################
## 숫자 부호 표현
###############################
# + 기호 사용으로 항상 부호 표시
>>> var_p = 20
>>> var_m = -20
>>> f"{var_p:+d} | {var_m:+d}"
'+20 | -20'

# - 기호 사용으로 음수일 때만 기호 표시
>>> var_p = 20
>>> var_m = -20
>>> f"{var_p:-d} | {var_m:-d}"
'20 | -20'

# 기호만 앞으로 정렬
>>> var_p = 20
>>> var_m = -20
>>> f"{var_p:=+5d} | {var_m:=+5d}"
'+  20 | -  20'

# 공백 0으로 채우기
>>> var_p = 20
>>> var_m = -20
>>> f"{var_p:+05d} | {var_m:+05d}"
'+0020 | -0020'


###############################
## 소수점 부호 표현
###############################
# + 기호 사용과 소수점  자릿수 포맷
>>> var_f_p = 123.12345
>>> var_f_m = -123.12345
>>> f"{var_f_p:+7.3f} | {var_f_m:+7.3f}"
'+123.123 | -123.123'

# - 기호 사용과 소수점 자릿수 표맷
>>> var_f_p = 123.12345
>>> var_f_m = -123.12345
>>> f"{var_f_p:-7.3f} | {var_f_m:-7.3f}"
'123.123 | -123.123'


###############################
## 지수 표기
###############################
# 지수를 소문자 e로 표기
>>> var_f = 1234567890.12345
>>> f"{var_f:e}"
'1.234568e+09'

# 지수를 소문자 E로 표기
>>> var_f = 1234567890.12345
>>> f"{var_f:E}"
'1.234568E+09'


###############################
## 백분율 표기
###############################
# 나눗셈 연산 후 100을 곱해 백분율 표기
>>> f"{20/100:%}"
'20.000000%'


###############################
## 의미 없는 소수점 삭제
###############################
# 기본적으로 .0이 표시가 됨
>>> var_f = 1234.00000000
>>> f"{var_f}"
'1234.0'

# 의미 없는 .0을 제거
>>> var_f = 1234.00000000
>>> f"{var_f:g}"
'1234'


###############################
## 숫자 천단위 구분
###############################
# 정수 천단위 구분
>>> var = 1234567890
>>> f"{var:,}"
'1,234,567,890'

# 부동소수점 천단위 구분
# 소수점 둘째 자리만 표현
>>> var_f = 1234567890.12345
>>> f"{var_f:,.2f}"
'1,234,567,890.12'


###############################
## 진법 변환
###############################
# 각 진법별로 십진수 52값 변환
>>> var = 52
>>> f"dec={var:d} | hex={var:x} | oct={var:o} | bin={var:b}"
'dec=52 | hex=34 | oct=64 | bin=110100'

# 진법 접두어 표기
>>> var = 52
>>> f"dec={var:#d} | hex={var:#x} | oct={var:#o} | bin={var:#b}"
'dec=52 | hex=0x34 | oct=0o64 | bin=0b110100'

# 헥사 값 대소문자 구분
>>> var = 255
>>> f"{var:x} | {var:X}"
'ff | FF'

###############################
## 유니코드 변환
###############################
# 정수형을 유니코드 변환
>>> var_1 = 99
>>> var_2 = 97
>>> f"{var_1:c}{var_2:c}{112:c}"
'cap'


###############################
## 날짜 포맷
###############################
>>> from datetime import datetime as dt
>>> f"{dt.now():%Y/%m%d %H:%M:%S}"
'2019/0918 21:25:03'


###############################
## 중괄호 표기
###############################
# 각각 중괄호를 2회 사용하여 중괄호 표기
>>> var = "CaptainBIN"
>>> f"{{ {var} }}"
'{ CaptainBIN }'

 

 

파이썬 timeit을 활용한 각 문자열 포맷 실행속도 테스트

이상으로 파이썬에서 문자열 포맷을 할 수 있는 3가지 방법을 알아보았습니다. 예제를 작성하다 보니 format() 메서드에서 사용한 예제의 결과 값을 거의 동일하게 문자열 리터럴 포맷 예제로 만들었습니다.

노파심에서 말씀드리지만 지금까지 알려드린 내용을 파이썬 프로그래밍에서 문자열 포맷을 어떻게 사용하는지 혹시나 궁금해하실 분들이 있을 것 같습니다.

이 포스팅 제목이 "파이썬 문자열 포맷"입니다. 즉, 문자열을 만든 것입니다. 이 문자열은 변수에 저장할 수도 있고 print문을 사용해 콘솔 화면이나 파일에 생성한 문자열을 출력할 수 있습니다.

위의 리터럴 문자열 예제 중 날짜 포맷의 코드를 통해 간단히 사용방법을 알아보겠습니다.

from datetime import datetime as dt

now_date = f"{dt.now():%Y/%m%d %H:%M:%S}"

print(now_date)
print(f"{dt.now():%Y/%m%d %H:%M:%S}")

 

참고로 format() 메서드는 PEP 3101에서 문자열 리터럴은 PEP 498 문서에서 정보를 확인할 수 있습니다.

 

마지막으로 이 글을 작성하다가 문득 궁금해진 사항이 있습니다. 바로 다른 형태의 포맷팅이 과연 실행 속도는 어떻게 다른지 말이죠. 그래서 각각의 방식을 3번씩 수행한 코드와 결과를 보여 드리면서 본 포스팅을 마치겠습니다.

속도 체크는 timeit 모듈을 사용했습니다.

from timeit import timeit as tt

# % 연산자 문자열 속도 체크 코드
tt('''var_1="Captin";var_2="BIN";var_3="BLOG";"%s%s %s"%(var_1, var_2, var_3)''')

# format() 메서드 속도 체크 코드
tt('''var_1="Captin";var_2="BIN";var_3="BLOG";"{0}{1} {2}".format(var_1, var_2, var_3)''')

# 리터럴 문자열 속도 체크 코드
tt('''var_1="Captin";var_2="BIN";var_3="BLOG";f"{var_1}{var_2} {var_3}"''')

################################
## 각각의 코드를 3회 실행 결과
################################
# % 연산자 문자열 속도 체크 3회 실행 결과
0.2824199000024237
0.28454950000013923
0.2820252999954391

# format() 메서드 속도 체크 3회 실행 결과
0.44093229999998584
0.4497973999968963
0.43604079999931855

# 리터럴 문자열 속도 체크 3회 실행 결과
0.12911259999964386
0.1376771000068402
0.12878590000036638

 

속도 체크 테스트를 위한 코드 길이가 리터럴 문자열이 가장 짧습니다. 또한 실행 결과도 리터럴 문자열이 가장 빠른 것으로 확인됩니다. 앞으로 이 리터럴 문자열을 애용해야겠습니다.

 

MORE