류나의 작은 DB

27살 류나의 바르고 다르게 살기

Programming/Basic Python

포덕의 파이썬: 1-2. 문자열 다루기

Ryuna (류준범) 2020. 7. 20. 22:44

프로그래밍을 하다 보면 어쩌면 숫자보다도 더 다뤄야 할 일이 많고 더 복잡한 것이 문자열일 겁니다. 이번 시간에는 파이썬에서 문자열을 다루는 방법에 대해 살펴볼 것입니다.

문자열 만들기

큰따옴표 또는 작은따옴표를 사용해서 문자열(string)을 만들 수 있습니다.

>>> "Pokemon"
'Pokemon'
>>> 'Pokemon'
'Pokemon' 

보다시피 파이썬에서는 큰따옴표와 작은따옴표를 똑같이 처리합니다. 큰따옴표와 작은따옴표 두 가지로 문자열을 만들 수 있는 이유는 따옴표가 포함된 문자열을 만들 필요가 있기 때문입니다. 다음과 같은 경우들 때문이죠.

>>> "Ryuna's Database"
"Ryuna's Database"
>>> 'Ryuna "Database"'
'Ryuna "Database"' 

위와 같이 큰따옴표 안에 작은따옴표가 포함된 문자열을 넣거나 작은따옴표 안에 큰따옴표가 포함된 문자열을 넣을 수 있습니다.

만약 문자열 안에 큰따옴표와 작은따옴표 모두 들어가야 한다면 어떻게 해야 할까요?

>>> speak = '\"Ryuna\'s Database is the best\", he said.'
>>> print(speak)
"Ryuna's Database is the best", he said. 

문자열 안에서 큰따옴표를 표현하기 위해 \”를, 작은따옴표를 표현하기 위해 \’를 사용했습니다. 이처럼 백슬래시(\)를 포함한 이스케이프 문자를 사용하면 따옴표를 구문에 혼란을 주지 않고 집어넣을 수 있습니다.

>>> karen = '''강한 포켓몬, 약한 포켓몬.
... 그런 건 사람이 멋대로 정하는 것.
... 정말로 강한 트레이너라면
... 자신이 좋아하는 포켓몬으로
... 이길 수 있도록 노력해야 해.
... ''' 

여러 줄의 문자열을 사용하고 싶다면 세 개의 작은따옴표나 큰따옴표를 사용합니다.

>>> friends = '피카츄 라이츄 파이리 꼬부기\n버터플 야도란 피죤투 또가스'
>>> print(friends)
피카츄 라이츄 파이리 꼬부기
버터플 야도란 피죤투 또가스 

또는, 이스케이프 문자 \n을 사용해도 됩니다.

>>> pikachu = 'Pikachu'
>>> eevee = 'Eevee'
>>> print(pikachu, eevee)
Pikachu Eevee 

여러 문자열을 한꺼번에 print() 함수를 사용해 출력할 수 있습니다. 각 문자열 사이에는 공백이 자동으로 붙습니다.

str() 함수는 값을 문자열 자료형으로 변환해 줍니다. 지난 시간에 배운 int(), float() 함수의 문자열 버전이라고 생각하면 됩니다.

문자열 연산하기

다른 언어에서는 찾아보기 어려운 기능이지만, 파이썬에서는 문자열을 더하고 곱할 수 있습니다. 어떻게 하는지 살펴볼까요?

>>> sword = 'Pokemon Sword'
>>> shield = ' & Shield'
>>> sword + shield
'Pokemon Sword & Shield' 

문자열의 덧셈은 간단합니다. 위처럼 문자열을 차례로 결합해 줍니다. 단, print() 함수를 사용해 문자열 여러 개를 출력하는 예시와 다르게 공백을 사이에 넣어 주지 않습니다.

>>> first = 'Pikachu ' * 5
>>> last = 'Eevee'
>>> first + last
'Pikachu Pikachu Pikachu Pikachu Pikachu Eevee' 

문자열에서 * 기호는 문자열을 뒤에 나오는 숫자의 횟수만큼 복제합니다.

>>> swsh = 'Pokemon Sword & Shield'
>>> len(swsh)
22 

문자열의 길이는 len() 함수를 이용하여 구할 수 있습니다. len() 함수는 앞서 여러 번 사용한 print() 함수처럼 따로 설정하지 않아도 사용할 수 있는 파이썬의 내장 함수인데, 문자열 외에도 여러 ‘시퀀스(sequence) 타입’에서 사용할 수 있습니다. 다다음 시간쯤에 이 함수의 또다른 용례를 발견하게 될 것입니다.

문자열 인덱싱, 슬라이싱

본격적으로 문자열을 다루는 방법을 설명하기 전에, 이야기의 흐름과는 크게 관계없지만 오래 두고 쓸 수 있는 꿀팁을 알려드리겠습니다.

>>> import string
>>> letters = string.ascii_lowercase
>>> letters
'abcdefghijklmnopqrstuvwxyz' 

string 모듈(지금은 몰라도 됩니다.)에는 a부터 z까지 모든 알파벳이 결합된 문자열이 들어있답니다. string.ascii_lowercase 외에도 string.ascii_uppercase를 통해 A부터 Z까지를 얻을 수 있고, string.ascii_letters는 lowercase와 uppercase를 합한 전부를, string.digits는 0부터 9까지 숫자를 얻을 수 있습니다.

 

문자열에서 순서를 설명하려면 알파벳 순서가 제일 편해서, a부터 z까지 있는 문자열을 사용하고 싶었는데 손으로 일일이 입력하는 것보다 쉬운 방법이 있어서 소개드려 봤습니다 ㅎ 어쨌든 문자열 인덱싱에 대해 알아보겠습니다.

>>> letters[0]
'a'
>>> letters[1]
'b'
>>> letters[-1]
'z'
>>> letters[-2]
'y' 

문자열에서 한 문자만을 얻고 싶다면 인덱싱(indexing)을 사용합니다. 위 예시에서 볼 수 있듯, 가장 왼쪽의 번호는 0이고 그 다음은 1, 2, …와 같이 진행됩니다. (파이썬은 숫자를 0부터 센다고 기억하면 편합니다!) 또, 가장 오른쪽의 번호는 -1입니다. 그 다음은 -2, -3, …와 같이 진행되죠. 이 규칙은 문자열 외에도 후에 배울 ‘리스트’ ‘튜플’ 등의 시퀀스 타입에 동일하게 적용되니 꼭 기억합시다.

>>> letters[50]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range 

문자열의 길이 이상의 번호(맨 오른쪽은 길이 – 1이니까요)를 사용해서 인덱싱을 시도하면 예외를 얻게 됩니다.

>>> letters[7] = 'x'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment 

인덱싱을 통해 문자열을 변경하려고 시도하면 예외가 발생합니다. 왜냐하면 문자열은 불변하는 타입이기 때문입니다. 어떤 언어에서는 문자열이 변경 가능하기 때문에, 파이썬의 특징으로 기억해 두면 좋을 것입니다.

 

이번엔 문자열 슬라이싱에 대해 알아봅시다.

>>> letters[1:4]
'bcd'
>>> letters[1:]
'bcdefghijklmnopqrstuvwxyz'
>>> letters[:4]
'abcd'
>>> letters[:]
'abcdefghijklmnopqrstuvwxyz' 

문자열에서 일부를 얻고 싶다면 슬라이싱(slicing)을 사용합니다. letters[시작 번호:끝 번호]를 지정하면 시작 번호부터 끝 번호 – 1에 해당하는 번호까지의 문자열을 반환합니다. 시작 번호나 끝 번호를 지정하지 않으면 처음과 마지막 문자를 사용합니다.

>>> letters[2:9:2]
'cegi'
>>> letters[::-1]
'zyxwvutsrqponmlkjihgfedcba' 

슬라이싱에는 스텝(step)을 지정할 수 있습니다. 예를 들어 letters[2:9:2]는 2번(c)부터 8번(i)까지 문자열에서 2개마다 하나씩 꺼내와서 ‘cegi’를 반환합니다. 스텝을 음수로 주어서 백스텝도 가능하기에, letters[::-1]을 통해 맨 마지막부터 맨 처음까지 문자열을 거꾸로 뒤집을 수 있습니다.

>>> letters[:50]
'abcdefghijklmnopqrstuvwxyz' 

슬라이싱에서는, 인덱싱과 달리 문자열의 길이 이상의 번호를 사용해도 예외가 발생하지 않습니다.

문자열 함수들

문자열 내장 함수라고 불리는 함수들은 앞서 배운 len() 같은 것과는 다르게, 문자열 이름을 입력하고 .을 입력한 뒤 함수 이름을 입력하여 사용합니다.

>>> Karen = """Strong Pokémon. Weak Pokémon.
... That is only the selfish perception of people.
... Truly skilled Trainers should try to win with the Pokémon they love best.
... I like your style.
... You understand what's important.
... Go on — — the Champion is waiting.
... """
>>> Karen.count('Pokémon')
3 

count는 특정 문자 또는 문자열이 몇 번 나오는지를 반환합니다.

>>> Karen.find('Pokémon')
7
>>> Karen.rfind('Pokémon')
127
>>> Karen.index('Pokémon')
7
>>> Karen.find('Digimon')
-1
>>> Karen.index('Digimon')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found 

find는 특정 문자 또는 문자열이 처음으로 나온 위치를 반환하며, rfind는 마지막으로 나온 위치를 반환합니다. index도 처음으로 나온 위치를 반환하지만, 존재하지 않는 문자열을 찾으면 예외가 발생한다는 점에서 -1를 반환하는 find와 다릅니다.

>>> ryunadb = 'RyunaDB'
>>> ryunadb.upper()
'RYUNADB'
>>> ryunadb.lower()
'ryunadb'
>>> ryunadb.swapcase()
'rYUNAdb'
>>> ryunadb.ljust(11)
'RyunaDB    '
>>> ryunadb.center(11)
'  RyunaDB  '
>>> ryunadb.rjust(11)
'    RyunaDB' 

upper는 글자를 모두 대문자로, lower는 글자를 모두 소문자로 만듭니다. swapcase는 대문자와 소문자를 서로 바꿉니다. ljust, center, rjust는 지정된 공간에서 각각 왼쪽 정렬, 가운데 정렬, 오른쪽 정렬을 합니다.

>>> pikachu = ' Pikachu '
>>> pikachu.lstrip()
'Pikachu '
>>> pikachu.rstrip()
' Pikachu'
>>> pikachu.strip()
'Pikachu' 

이번엔 공백을 지우는 함수입니다. lstrip은 가장 왼쪽의 공백들을 지우고, rstrip은 가장 오른쪽의 공백들을 지웁니다. strip은 이 둘을 동시에 수행합니다.

>>> swsh = 'Pokemon Sword & Shield'
>>> swsh.replace('Pokemon', 'Digimon')
'Digimon Sword & Shield'
>>> swsh
'Pokemon Sword & Shield' 

문자열의 일부를 바꾸려면 replace를 사용합니다. 다만 문자열 그 자체가 바뀌진 않기 때문에 위에서 swsh를 바꾸고 싶다면 swsh = swsh.replace(…)을 입력해야 합니다.

 

마지막으로, 꽤나 고오급 기능인 split과 join을 살펴보겠습니다.

>>> starters = '이상해씨 파이리 꼬부기'
>>> starters.split()
['이상해씨', '파이리', '꼬부기']
>>> starters_2 = '치코리타/브케인/리아코'
>>> starters_2.split('/')
['치코리타', '브케인', '리아코'] 

split 함수는 특정 구분자를 기준으로 문자열을 나누어 리스트에 하나씩 넣어 줍니다. (리스트는 다다음 시간에 배웁니다) 만약 구분자가 명시되어 있지 않다면 공백을 기준으로 나누어 줍니다.

>>> '.'.join('피카츄')
'피.카.츄'
>>> ', '.join(['이상해씨', '파이리', '꼬부기'])
'이상해씨, 파이리, 꼬부기' 

join 함수는 split과 반대의 일을 합니다. 특정 구분자를 기준으로 문자열이나 리스트를 합칩니다.

문자열 포매팅

문자열 포매팅은 데이터 값을 문자열에 끼워 넣는 것을 말합니다. Python 2 시절부터 있었던 % 연산자를 사용한 방법, Python 3에서 추가된 {}와 format을 사용한 방법, Python 3.6부터 사용할 수 있는 f-string까지 세 가지 방법이 있습니다.

>>> name = '드래펄트'
>>> stat = 600
>>> '%s: 종족값 총합 %d' % (name, stat)
'드래펄트: 종족값 총합 600'

우선 % 연산자를 사용한 방법입니다. %s, %d 등 데이터의 타입에 따라 알맞은 문자열 포맷 코드를 끼워넣어야 한다는 단점이 있습니다.

>>> '{}: 종족값 총합 {}'.format(name, stat)
'드래펄트: 종족값 총합 600'

{}와 str.format을 사용한 방법은, 데이터 타입을 명시할 필요가 없어졌지만 가독성이 크게 개선되지는 않았다는 단점이 남아있습니다.

>>> f'{name}: 종족값 총합 {stat}'
'드래펄트: 종족값 총합 600'

f-string은 가독성이 크게 개선되었고, 속도도 가장 빠른 장점이 있습니다.