컴퓨터는 수 뿐만 아니라 텍스트 데이터도 취급한다. 하지만 컴퓨터는 수를 다루는 기계. 수를 이용해 텍스트를 나타내려면 어떻게 해야 할까?

4.3.1 아스키 코드와 유니코드

수로 텍스트를 표현하기 위해서는 사용할 모든 문자에 고유한 번호를 매겨 놓으면 된다. 예를 들어, 가 = 1, 져 = 2, 라 = 3 이라고 정해 두면, ‘가져가라가져가’ 라는 텍스트를 ‘1213121’이라는 수로 표현할 수 있다.

옛날에는 컴퓨터를 만드는 회사마다 제각각 번호를 매겼다. 그래서 다양한 컴퓨터가 서로 정보를 교환할 때 불편을 겪었다. 불편을 방치할 수 없었으므로 문자 부호 목록은 표준화되었다. 여러 표준안 중에서 가장 널리 쓰이는 것은 아스키 코드(ASCII code, 미국정보교환표준부호)와 유니코드(Unicode)다.

아스키 코드는 문자 하나를 비트 일곱 개로 표현한다. 7 비트로 표현할 수 있는 경우의 수는 128가지다. 그래서 아스키 코드에는 숫자, 알파벳 대소문자, 기호 등을 포함해 128가지 문자만이 할당되어 있다. 아스키 코드는 간단해서 다루기 쉽고 대부분의 컴퓨터에서 사용할 수 있을만큼 보편적이다. 하지만 미국식 영어 외의 세계 대부분의 문자는 표현할 수 없다는 한계가 있다.

알파벳 외의 문자를 사용하는 국가(또는 회사)들은 저마다 자기 나라 문자를 표현하기 위한 문자 코드 체계를 만들어 사용했다. 나라마다 사용하는 문자 코드 체계가 서로 다르면 국제적으로 정보를 교환하기가 어렵다. 인터넷이 등장한 뒤 이 문제는 더 중요해졌다. 그래서 오늘날에는 세계의 모든 문자를 통합하여 나열한 문자 코드 체계, 유니코드를 사용한다. 유니코드에는 한글, 한자, 아랍어 문자, 그리스 문자 등 다양한 문자가 망라되어 있다. 파이썬도 텍스트 데이터를 유니코드로 부호화한다.

부호화 처리는 운영 체제와 프로그래밍 언어가 자동으로 수행해주기 때문에 지금 이런 개념을 깊게 알 필요는 없다. 텍스트 데이터도 결국 수라는 것과 텍스트를 수로 나타내는 방법을 표준화한 부호 체계가 있다는 사실 정도만 알면 충분하다.

4.3.2 문자열

파이썬은 텍스트 데이터를 취급하기 위한 문자열(string, 줄여서 str)이라는 데이터 유형을 제공한다. 문자열이란 문자의 나열(순서 있는 묶음)이라는 뜻이다. 여러분은 이미 문자열을 사용해 보았다. 1장에서 만들어 본 첫 파이썬 프로그램에서 print('당신의 이름은 무엇인가요?')라는 코드를 작성했다. 이 때, 따옴표(')로 둘러싸인 텍스트 데이터가 바로 문자열이다. 또, 사용자에게서 input() 함수로 데이터를 입력받았을 때 반환되는 데이터도 문자열 데이터였다.

문자열은 문자를 나열한 것

프로그래밍 언어 중에는 개별 문자와 문자열을 서로 다른 유형으로 구분하는 것도 있다. 파이썬에서는 개별 문자든 문자열이든 모두 문자열로 취급한다.(‘문자 하나’는 문자가 하나만 나열된 ‘문자열’이다.) 이렇게 문자와 문자열을 동일한 유형으로 취급하면 텍스트 데이터의 취급 방법이 통일되어 편하다.

문자열 표기

파이썬 코드에 문자열 데이터를 표기하는 방법은 꽤 여러 가지가 있는데 그 중 기본이 되는 것이 다음 두 가지다.

  • 홑따옴표로 감싼 표현: '안녕'
  • 쌍따옴표로 감싼 표현: "안녕"

홑따옴표를 사용하는 표현은 이미 사용해 보았고, 쌍따옴표는 아마 처음 볼 것이다. 이 둘은 완전히 똑같은 표현이다. "Today's coffee"처럼, 문자열 속에 홑따옴표나 쌍따옴표를 넣어야 할 때만 편의성에서 차이가 난다.

코드 4-13 홑따옴표와 쌍따옴표의 차이

>>> "Today's coffee"   # 쌍따옴표로 감쌀 때는 OK
"Today's coffee"

>>> 'Today's coffee'   # 홑따옴표로 감싸면 오류가 발생한다
SyntaxError: invalid syntax

>>> 'Today\'s coffee'  # 홑따옴표를 이스케이프(\) 해주면 된다
"Today's coffee"

위 코드처럼, 홑따옴표가 들어 있는 문자열을 홑따옴표로 감싸면 오류가 발생한다. 'Today' 에서 이미 문자열이 끝났다고 해석되기 때문이다. 반대로 쌍따옴표가 들어 있는 문자열은 홑따옴표로 감싸주면 된다. 문자열 안에 홑따옴표와 쌍따옴표가 둘 다 들어있다면, 따옴표를 이스케이프해야 한다.

이스케이프

이스케이프란 일반적인 방법으로 입력하기 어려운 특별한 문자를 입력하는 방법이다. 이스케이프를 하려면 먼저 이스케이프 기호 백슬래시(\)를 쓰고 바로 붙여서 이스케이프할 문자를 적으면 된다. 이스케이프를 통해 표현해야 하는 특별한 문자로는 다음과 같은 것이 있다.

  • \\: 백슬래시
  • \': 홑따옴표 (홑따옴표 안에서)
  • \": 쌍따옴표 (쌍따옴표 안에서)
  • \n: 개행 문자 (라인 피드. 다음 행으로 바꿈)
  • \r: 개행 문자 (캐리지 리턴. 커서를 행의 앞으로 이동. 잘 사용하지 않는다.)
  • \t: 탭 문자

이 중 주로 사용할 것은 \', \\, \n, \\이다. 다음 예는 홑따옴표, 쌍따옴표, 개행 문자가 포함된 텍스트를 표현해 본 것이다.

코드 4-14 이스케이프 문자의 활용

>>> text = 'Today\'s coffee:\n"카페 라테"\n"아메리카노"'
>>> print(text)
Today's coffee:
"카페 라테"
"아메리카노"

라인 피드와 캐리지 리턴

개행 문자는 왜 두 개(\n\r)일까? 그 이유는 과거 인쇄기의 동작 방식과 연관이 있다. 개행 문자 \n은 라인 피드(line feed)로, 인쇄기에게 종이를 한 행만큼 올리라고 지시하는 기호다. 또 다른 개행 문자 \r은 캐리지 리턴(carriage return)으로, 인쇄기의 활자를 찍는 팔을 원위치로 옮기라고 지시하는 기호다. 인쇄기가 다음 행에 문자를 인쇄하려면 \r\n을 둘다 수행해야 했기 때문에 두 문자가 모두 필요했다.

지금은 인쇄기를 그런 식으로 제어하지는 않지만, 텍스트 데이터에서 개행을 나타내는 방식은 그대로 남았다. 그런데 운영 체제마다 개행을 나타내는 방식에 차이가 있다. 윈도우 계열의 OS에서는 개행을 \r\n으로 나타내고, 유닉스 계열의 OS(리눅스, 맥OS 등)에서는 개행을 \n만으로 나타낸다. 이 차이는 크게 중요하지는 않지만, 리눅스에서 작성한 텍스트 파일을 윈도우의 메모장으로 열면 개행 문자가 깨져 보이는 등 호환성 문제가 있다는 점을 알아두는 것이 좋다.

관습적으로, 운영 체제에 관계없이 프로그램 소스 코드에서는 \n 만으로 개행을 표현한다. \r은 특별한 경우가 아니면 사용하지 않는다.

이스케이프 하지 않기

이스케이프 기능을 적용하지 않고 텍스트를 표기하고 싶을 때도 있을 수 있다. 이럴 때는 다음 코드와 같이 문자열 앞에 r을 적어주면 된다. (r은 ‘날 것’을 의미하는 ‘raw’ 에서 딴 것이다.)

코드 4-15 이스케이프 하지 않기

>>> print(r'다음 행으로 넘어갈 때는 개행 문자(\n)을 쓰세요.')
다음 행으로 넘어갈 때는 개행 문자(\n)을 쓰세요.

이 기능은 주로 파이썬 안에서 다른 프로그래밍 언어를 표현해야 하거나 정규식(텍스트 검색/치환에 사용되는 도구) 같은 복잡한 텍스트를 다뤄야 할 때 사용된다.

여러 행의 문자열 표기

바로 위에서 배운 것처럼 문자열 안에서 여러 행을 표현하려면 개행 문자(\n)를 사용하면 된다. 하지만 이 방법으로는 개행이 많은 텍스트를 입력하기가 불편하다.

코드 4-16 개행 문자의 불편함

>>> poem = '죽는 날까지 하늘을 우러러\n한 점 부끄럼이 없기를,\n잎새에 이는 바람에도\n나는 괴로워했다.\n별을 노래하는 마음으로\n모든 죽어 가는 것을 사랑해야지\n그리고 나한테 주어진 길을\n걸어가야겠다.\n오늘밤에도 별에 바람이 스치운다.'

위 예에서 보듯 행바꿈이 여러 번 일어나는 텍스트를 개행 문자만으로 표현하면 불편하고 가독성이 떨어진다. 이 때 ‘세 따옴표 문자열 표기법’을 사용하면 편리하다. 세 따옴표 문자열 표기법은 문자열을 홑따옴표 세 개(''') 또는 쌍따옴표 세 개(""")로 감싸는 것이다. 이것은 3장에서 문서 문자열을 입력할 때 사용한 것과 동일한 표기법이다. 다음 예를 보자.

코드 4-17 세 따옴표 문자열 표기법의 활용

>>> poem = """죽는 날까지 하늘을 우러러
한 점 부끄럼이 없기를,
잎새에 이는 바람에도
나는 괴로워했다.
별을 노래하는 마음으로
모든 죽어 가는 것을 사랑해야지
그리고 나한테 주어진 길을
걸어가야겠다.
오늘밤에도 별에 바람이 스치운다."""

세 따옴표 문자열 속의 문자열은 개행 문자를 입력하는 대신 코드 안에서 실제로 행을 바꿈으로써 개행을 표현할 수 있다.

표기법이 다양해도 모두 문자열

지금까지 나온 문자열 표기법은 총 여덟 가지다. 정리해 보자.

  • 홑따옴표 표기법: '텍스트'
  • 쌍따옴표 표기법: "텍스트"
  • 세 홑따옴표 표기법: '''텍스트'''
  • 세 쌍따옴표 표기법: """텍스트"""

네 가지 뿐인데 왜 여덟 가지일까? 이 네 가지 표기법에 각각 r을 붙여 이스케이프 기능을 적용하지 않는 버전이 존재하기 때문이다.

어떤 표기법으로 텍스트를 표현하더라도 파이썬의 입장에서는 모두 똑같은 문자열 유형 데이터가 된다. 앞에 r을 붙였다고 다른 유형의 데이터가 되는 게 아니다. 그러므로 어떤 표현을 사용할지는 사람이 코드를 읽기 좋은 쪽을 기준으로 선택해야 한다. 표기법이 다양한 만큼 뒤죽박죽 섞어 쓰면 코드가 지저분해질 우려가 있다. 나는 대부분의 경우 홑따옴표 표기법만을 사용하고, 문서 문자열 등에서 여러 행의 문자열을 표기할 때 세 쌍따옴표 표기법을 쓴다. 그 외의 표기법은 정말로 꼭 필요할 때만 쓴다.

4.3.3 문자열 연산

데이터의 유형에 따라 적용할 수 있는 연산이 다르다고 했다. 텍스트는 수와는 전혀 다른 유형의 데이터다. 그래서 문자열에 적용할 수 있는 연산은 수에는 적용할 수 없는 것이 대부분이고, 수를 위한 연산도 문자열에는 적용할 수 없다. 문자열을 위한 연산은 여러분이 처음 보는 새로운 것이다. 자세히 살펴보자.

연결과 반복

사칙연산 연산자 중 덧셈 연산자와 곱셈 연산자를 문자열에 적용할 수 있다. 연산자가 사칙연산 기호일 뿐 적용되는 연산이 사칙연산인 것은 아니다.

코드 4-18 문자열 연결과 반복

>>> '아메' + '리카노'  # 문자열을 서로 더하면 연결된다
'아메리카노'

>>> '아메' * 3  # 문자열에 수를 곱하면 문자열이 수만큼 반복된다
'아메아메아메'

덧셈 연산자는 문자열 연결에, 곱셈 연산자는 문자열 반복에 사용된다. 반복 기능보다는 연결 기능이 자주 사용된다.

길이 세기

문자열의 길이를 조사할 때는 len() 함수를 사용한다. len 이라는 이름은 길이를 뜻하는 영어 단어 length 를 줄인 것이다.

코드 4-19 문자열 길이 세기

>>> len('아메리카노')
5

특정 위치의 문자를 확인하기

문자열에서 특정한 위치의 문자를 읽을 때는 인덱스 연산자([])를 사용한다. 이 때 위치의 번호는 맨 앞의 문자가 0부터 시작해 차례대로 부여된다.

코드 4-20 특정 위치의 문자를 확인하기

>>> text = '카페 라테'
>>> text[0]
'카'

>>> text[1]
'페'

>>> text[2]
' '

문자열 메서드

검색, 치환, 대소문자 변환 같은 문자열 전용 연산이 있다. 이들은 메서드(method)라는 형태로 제공된다. 메서드는 특정 데이터 유형에 종속된 데이터 유형별 전용 함수다. 메서드의 특징은 데이터 뒤에 점(.)을 붙인 후 호출한다는 것이다. 문자열을 대문자로 바꾸는 메서드upper()를 예로 확인해 보자.

코드 4-21 문자열 메서드의 사용예

>>> 'Today\'s coffee'.upper()  # 문자열을 대문자로
"TODAY'S COFFEE"

이처럼 데이터에 점을 붙여 부른다는 점만 제외하면 함수와 사용방법이 똑같다. 함수가 좀 더 일반적인 용도인 반면 메서드는 특정한 데이터 유형에 종속된다는 점만이 다르다. 메서드에 관해서는 8장에서 더 자세히 다룬다.

표 4-1은 문자열을 위한 메서드 중 가장 자주 사용되는 것을 간략히 설명한 것이다.

메서드 용도
count(text) text가 문자열 안에 몇 번 나오는지 센다
find(text) text가 문자열 안에서 처음 나오는 위치를 찾는다
rfind(text) text가 문자열 안에서 처음 나오는 위치를 뒤에서부터 찾는다
lower() 문자열을 소문자로 변경한 것을 반환한다
upper() 문자열을 대문자로 변경한 것을 반환한다
replace(a, b) 문자열에서 a를 b로 치환한 것을 반환한다
strip() 문자열 좌우의 공백 문자를 없앤 것을 반환한다
lstrip() 문자열 왼쪽의 공백 문자를 없앤 것을 반환한다
rstrip() 문자열 오른쪽의 공백 문자를 없앤 것을 반환한다
split(text) text를 기준으로 문자열을 여러 개로 나눈다
splitlines() 개행을 기준으로 문자열을 여러 개로 나눈다
isalpha() 문자열이 문자(알파벳, 한글 등)로만 구성되어 있는지 검사한다
isnumeric() 문자열이 숫자로만 구성되어 있는지 검사한다
isalnum() 문자열이 문자와 숫자로만 구성되어 있는지 검사한다
format() 데이터를 양식화한다 (11.2 텍스트 양식화에서 설명)

표 4-1 문자열 메서드

표에서 소개한 메서드들을 IDLE 대화식 셸에 하나씩 입력해 보자.

문자열 검색

코드 4-22 문자열 검색 메서드

>>> book = '안나 카레니나, Leo Tolstoy'
>>> book.count('나')          # '나'가 몇 번 나오는가?
2

>>> book.find('카레')         # '카레'가 처음 등장하는 위치를 찾음
3

>>> book.find('카레라이스')   # 찾는 문자열이 없을 때는 -1이 반환
-1

>>> book.rfind('나')          # '나'가 마지막에 등장하는 위치를 찾음
6

count()find() 메서드는 문자열 안에서 다른 문자열을 검색하는 함수다.

count() 메서드는 비교할 문자열을 하나 입력받아, 원본 문자열에 비교 문자열이 몇개 포함되었는지 센다. 코드 4-22에서 ‘안나 카레니나, Leo Tolstoy’에서 ‘나’는 두 번 포함되어 있으므로 2가 반환되었다.

find() 메서드는 비교할 문자열을 하나 입력받아, 원본 문자열에서 비교 문자열이 처음 등장하는 위치를 반환한다. 비교 문자열이 발견되지 않은 경우에는 -1을 반환한다.

rfind() 메서드는 find() 메서드와 하는 일이 비슷하지만 비교 문자열이 마지막에 등장하는 위치를 찾아 반환한다. 비교 문자열이 포함된 횟수가 1 이하라면 find()rfind()의 실행 결과는 같다.

검색 메서드는 여러 상황에서 자주 활용된다. 예를 들어 사용자가 입력한 비밀번호에 특정 문자가 몇 개 포함되었는지 확인하거나, 채팅 프로그램에서 대화 내용에 욕설이 포함되었는지 검사하는 경우에 사용될 수 있다.

대소문자 변환

lower() 메서드와 upper() 메서드는 문자열 안의 알파벳을 소문자, 대문자로 변환하는 메서드다. 숫자, 한글 등 알파벳이 아닌 문자는 그대로 유지된다.

코드 4-23 대소문자 변환 메서드

>>> book = '안나 카레니나, Leo Tolstoy'
>>> book.lower()              # 알파벳을 소문자로
'안나 카레니나, leo tolstoy'

>>> book.upper()              # 알파벳을 대문자로
'안나 카레니나, LEO TOLSTOY'

이 때 주의할 점은 book.upper()를 실행한다고 변수 book의 값이 대문자로 ‘수정’되는 것은 아니라는 점이다. 소문자가 대문자로 바뀐 문자열이 반환될 뿐, 변수 book의 값은 그대로다. 변수 book의 값을 대문자로 수정하려면 book = book.upper() 처럼 메서드가 반환한 값을 다시 대입해야 한다.

문자열 치환

replace() 메서드는 문자열 안의 내용을 다른 것으로 치환할 때 사용한다. 매개변수 두 개를 입력받는데, 첫번째 매개변수는 찾을 문자열이고, 두번째 매개변수는 찾은 문자열을 이것으로 치환할 문자열이다.

코드 4-24 문자열 치환 메서드

>>> book = '안나 카레니나, Leo Tolstoy'

>>> book.replace(' ', '-')    # 공백 문자를 - 기호로 치환
'안나-카레니나,-Leo-Tolstoy'

>>> book.replace('니', '라이스 먹')  # '니'를 '라이스 먹'으로 치환
'안나 카레라이스 먹나, Leo Tolstoy'

위 코드에서 ' ''-'로 치환한 예처럼, 문자열이 여러 번 발견되면 모두 치환된다. 그리고 '나''라이스 먹'으로 치환한 예처럼 찾을 문자열과 치환할 문자열의 길이가 달라도 된다.

문자열 치환 메서드는 텍스트 오류(오타) 수정, 데이터 정리, 템플릿 기법(미리 정형화된 틀을 만들어 둔 뒤 내용을 교체하는 기법) 등 다양한 상황에서 사용된다.

좌우 공백 정리

strip() 메서드, lstrip() 메서드, rstrip() 메서드는 문자열 좌우의 공백을 제거할 때 사용된다. 예를 들어, abc d 라는 문자열에서 좌우의 공백을 제거해 abc d만 남기는 것이다. 문자열 중간의 공백은 제거되지 않는다.

코드 4-25 좌우 공백 정리 메서드

>>> text = ' 여백의 미   '
>>> text.strip()   # 좌우의 공백 제거
'여백의 미?'

>>> text.lstrip()  # 왼쪽의 공백만 제거
'여백의 미   '

>>> text.rstrip()  # 오른쪽의 공백만 제거
' 여백의 미'

좌우 공백 정리는 사용자가 입력한 데이터, 책에서 광학 스캐닝한 텍스트 등 불필요한 공백이 끼기 쉬운 데이터를 정리할 때 사용된다.

문자열 나누기

문자열 하나를 규칙에 따라 여러 개로 나누어야 할 때 split() 메서드와 splitlines() 메서드를 사용한다.

split() 메서드는 문자열을 구분하는 기준이 될 문자열을 매개변수로 입력받아 문자열을 나눈다.

코드 4-26 split() 메서드

>>> book = '안나 카레니나, Leo Tolstoy'
>>> book.split(',')  # ','를 기준으로 문자열을 나눈다
['안나 카레니나', ' Leo Tolstoy']

splitlines() 메서드는 개행을 기준으로 문자열을 나눈다.

코드 4-27 splitlines() 메서드

>>> poem = """죽는 날까지 하늘을 우러러
한 점 부끄럼이 없기를,
잎새에 이는 바람에도
나는 괴로워했다."""

>>> poem.splitlines()
['죽는 날까지 하늘을 우러러', '한 점 부끄럼이 없기를, ', '잎새에 이는 바람에도', '나는 괴로워했다.']

문자열을 나누는 메서드는 ['안나 카레니나', ' Leo Tolstoy'] 와 같이 대괄호로 둘러싸인 문자열들을 반환한다. 이것은 리스트라는 것으로 5장에서 배울 것이다.

문자열 검사

마지막으로 살펴볼 메서드는 문자열에 포함된 문자에 숫자가 포함되어 있는지, 기호가 포함되 있는지 등을 검사할 때 사용하는 메서드다.

isalpha() 메서드는 문자열이 알파벳, 한글 등 언어학적인 문자로만 구성되어 있는지 검사한다. 문자열에 숫자나 기호가 포함되어 있다면 반환값은 False다.

isnumeric() 메서드는 문자열이 숫자로만 구성되어 있는지 검사한다. 문자열에 알파벳이나 기호가 포함되어 있다면 반환값은 False다. 참고로 소수점('.')이나 마이너스(-) 기호가 있어도 False가 된다.

isalnum() 메서드는 문자열이 문자와 숫자로만 구성되어 있는지 검사한다. 문자열에 기호가 포함되어 있다면 반환값은 False다.

코드 4-28 문자열 검사 메서드

>>> ('한글').isalpha()      # 문자인가
True

>>> ('1024').isalpha()      # 문자인가
False

>>> ('1024').isnumeric()    # 숫자인가
True

>>> ('3.1415').isnumeric()  # 숫자인가
False

>>> ('1학년').isalnum()     # 문자 또는 숫자인가
True

문자열 검사 메서드는 사용자가 입력한 텍스트가 올바른 형식(특히, 텍스트가 숫자여야 할 때)인지 확인할 때 많이 사용된다. 사용자의 비밀번호에 다양한 기호가 포함되었는지 검사할 때도 쓰일 수 있다.

연습문제

연습문제 4-4 문자열 찾아 출력하기

‘오늘은 기분이 좋아.’라는 텍스트 데이터를 변수에 저장하고, 이 문자열에서 ‘분’의 위치를 찾은 후 그 바로 앞의 문자를 출력하는 프로그램을 작성하라.

힌트: 문자열을 검색할 때는 find() 메서드를 사용한다.

힌트: 특정 위치의 문자를 읽을 때는 인덱싱 연산자([])를 사용한다.

연습문제 4-5 영문자 세기

'I think, therefore I am.' 이라는 문자열에서 대소문자 구별 없이 ‘i’가 몇 번 나오는지 세어 화면에 출력하는 프로그램을 작성하라.

연습문제 4-6 문자열 치환

'I think, therefore I am.' 이라는 문자열을 'I eat, therefore I am.'으로 치환하여 화면에 출력하는 프로그램을 작성하라.