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

4.3.1 아스키 코드와 유니코드

수로 텍스트를 표현하는 방법은 간단하다. 사용할 문자에 고유한 번호를 매겨 놓으면 된다. 예를 들어, 가 = 1, 져 = 2, 라 = 3이라고 정해 두면, 가져가라가져가라는 텍스트를 1213121이라는 수로 표현할 수 있다.

옛날에는 컴퓨터를 만드는 회사마다 문자의 번호를 제각각 다르게 매겼다. 하지만 그러면 컴퓨터가 서로 다를 때 정보를 교환하기가 어려워진다. 이런 불편을 없애기 위해 문자 부호 표준안이 마련되었다. 표준안 중에서 아스키 코드(ASCII code, 미국정보교환표준부호)와 유니코드(Unicode)가 가장 널리 사용되고 있다.

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

알파벳 외의 문자를 사용하는 나라들은 저마다 문자 코드 체계를 만들어 사용했다. 하지만 정보는 국제적으로 교환할 필요가 있다. 인터넷이 등장한 뒤 이 문제는 더 중요해졌다. 그래서 오늘날에는 세계의 모든 문자를 망라하는 문자 코드 체계, 유니코드를 사용한다. 유니코드는 한글, 한자, 아랍어 문자, 그리스 문자 등 모든 문자를 표현할 수 있다. 유니코드는 오늘날 국제 표준 텍스트 부호이며 파이썬도 텍스트 데이터를 유니코드로 부호화한다.

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

4.3.2 문자열

파이썬은 텍스트 데이터를 취급하기 위한 문자열(string, 줄여서 str)이라는 데이터 유형을 제공한다. 문자열이란 문자의 나열(순서 있는 묶음)이라는 뜻이다. 여러분은 이미 문자열을 사용해 보았다. 1장에서 print('당신의 이름은 무엇인가요?')라는 코드를 작성했는데, 여기서 따옴표(')로 둘러싸인 텍스트 데이터가 바로 문자열이다.

문자열은 문자를 나열한 것

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

문자열 표기

파이썬 코드에서 문자열 데이터를 나타내려면 텍스트를 작은따옴표 또는 큰따옴표로 감싸 표기하면 된다.

  • 작은따옴표로 감싼 표현: '안녕'
  • 큰따옴표로 감싼 표현: "안녕"

이 두 표현은 완전히 똑같은 데이터를 나타낸다. 일반적으로는 작은따옴표를 이용해 텍스트를 나타내면 된다. 다만 "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() 개행을 기준으로 문자열을 여러 개로 나눈다
join(strs) 시퀀스(strs)에 포함된 문자열들을 이 문자열을 구분자로 연결한다 (5.2절)
isalpha() 문자열이 문자(알파벳, 한글 등)로만 구성되어 있는지 검사한다
isnumeric() 문자열이 숫자로만 구성되어 있는지 검사한다
isalnum() 문자열이 문자와 숫자로만 구성되어 있는지 검사한다
format() 데이터를 양식화한다 (11.2절)

표 4-1 문자열 메서드

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

문자열 검색

코드 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() 메서드와 하는 일이 비슷하다. 차이는 비교 문자열이 마지막에 등장하는 위치를 찾아 반환한다는 점이다.

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

대소문자 변환

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() 메서드도 있다.

코드 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 틀린 단어 고치기

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

연습문제 4-5 메모 읽기

손님들의 음료 주문 내용을 메모하여 다음과 같이 문자열로 기록해 두었다.

order_memo = """주문1: 아메리카노
주문2: 카페 라테
주문3: 아메리카노, 아메리카노
주문4: 아메리카노, 카페 라테
주문5: 카페 라테, 카페 라테
"""

이 메모에서 주문을 몇 번 받았는지, 아메리카노가 몇 잔 주문되었는지 각각 세어 화면에 출력하는 프로그램을 작성하라.