사전형과 문자열

이 장에서는 자료 분석을 할 때 자료를 저장하고 불러오고 가공하는데 편리하게 사용할 수 있는 파이썬 객체들에 대해서 알아봅니다.

사전형(Dictionary)

사전(dictionary)은 다양한 종류의 정보들을 저장하는데 매우 유연한 객체입니다. 특히 파일을 읽어들여서 처리할 때 유용합니다.

리스트는 인덱스를 정수를 이용해 접근할 수 있었지만 사전은 문자열을 이용하여 접근할 수 있습니다.

사전 만들기

사전은 {키:값} 형태로 만들어집니다. 는 일반적으로 문자열이 많이 쓰이고 불변하는 객체가 올 수 있습니다. 은 어떤 파이썬 객체도 가능합니다.

리스트를 이용하여 오슬로, 런던, 파리의 온도를 저장하는 한다면 다음과 같이 만들 수 있을 겁니다.

temps = [13, 15.4, 17.5]

하지만 이것은 순서대로 오슬로, 런던, 파리라는 것을 기억하고 있어야 합니다.

사전을 이용해서 온도를 저장할 때는 다음과 같이 합니다. 여기서는 리스트의 인덱스에 해당하는 것이 도시 이름이 되어 따로 기억할 필요가 없습니다.

In [1]: temps = {'Oslo': 13, 'London': 15.4, 'Paris': 17.5}

또는 다음과 같이 dict 함수를 이용해서 만들수도 있습니다.

In [2]: temps = dict(Oslo=13, London=15.4, Paris=17.5)

추가할 항목이 생기면 다음과 같이 하면 됩니다.

In [3]: temps['Seoul'] = 17

따라서 temps 사전은 다음과 같이 4개의 항목을 가지는 것을 알 수 있습니다.

In [4]: temps
Out[4]: {'Oslo': 13, 'London': 15.4, 'Paris': 17.5, 'Seoul': 17}

사전에 접근하려면 를 이용합니다.

In [5]: temps['Seoul']
Out[5]: 17

사전 연산

사전에서 인덱스에 해당하는 것이 입니다. 사전의 키들을 순회하기 위해서는 for 문을 사용합니다.

In [6]: for city in temps:
   ...:   print('{}의 온도는 {}'.format(city, temps[city]))
   ...: 
Oslo의 온도는 13
London의 온도는 15.4
Paris의 온도는 17.5
Seoul의 온도는 17

가 사전에 있는지 확인하기 위해서는 in을 사용할 수 있습니다.

In [7]: 'Berlin' in temps
Out[7]: False
In [8]: 'Seoul' in temps
Out[8]: True

에 대한 리스트는 keys()values() 메소드를 각각 사용합니다.

In [9]: temps.keys()
Out[9]: dict_keys(['Oslo', 'London', 'Paris', 'Seoul'])
In [10]: temps.values()
Out[10]: dict_values([13, 15.4, 17.5, 17])

내장함수 sorted 를 사용하면 정렬을 할 수 있습니다.

In [11]: for city in sorted(temps):
   ....:   print(city)
   ....: 
London
Oslo
Paris
Seoul

또한 입력된 순서로 사전을 만들 수 있는 collections 모듈의 OrderedDict 함수가 있습니다.

del를 이용해서 사전의 항목을 제거할 수 있습니다.

In [12]: del temps['Oslo']
   ....: temps
   ....: 
Out[12]: {'London': 15.4, 'Paris': 17.5, 'Seoul': 17}

copy() 메소드를 이용해서 새로운 복사 사전을 만들 수 있습니다.

In [13]: temps_copy = temps.copy()
   ....: del temps_copy['Paris']
   ....: print(temps_copy)
   ....: print(temps)
   ....: 
{'London': 15.4, 'Seoul': 17}
{'London': 15.4, 'Paris': 17.5, 'Seoul': 17}

직접하기

  1. 개인정보를 저장하는 사전형 person을 만들어 보세요. 사람의 성 last_name, 이름 first_name, 나이 age, 도시 city 를 포함하도록 만드세요. 그리고 각각의 정보를 출력해보세요.

사전형 축약

리스트 축약과 같이 사전형도 for 문을 이용한 축약을 사용할 수 있습니다.

In [14]: {k: k ** 2 for k in [1, 2, 3, 4, 5]}
Out[14]: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

사전형을 이용한 다항함수

int, float, complex, str, 과 tuple 등은 내용을 변경할 수 없는 객체로 불변(immutable) 객체라고 합니다. 리스트와 사전은 원소들을 바꿀 수 있는 가변(mutable) 객체입니다. 앞에서 말했듯이 사전의 키는 문자열 뿐아니라 불변 객체들이 올 수 있습니다.

정수형 를 사용해 다항함수를 사전으로 만들 수 있습니다.

\[p(x) = -1 + x^2 + 3x^7\]

다항함수 \(p(x)\)를 차수를 키로 계수를 값으로 설정해서 만들 수 있습니다.

In [15]: p = {0: -1, 2: 1, 7: 3}

물론 리스트를 이용해서도 다항함수를 만들수 있습니다. 리스트의 인덱스를 차수로 놓고 대응되는 원소를 계수로 설정할 수 있습니다.

In [16]: p = [-1, 0, 1, 0, 0, 0, 0, 3]

해당하는 차수가 없는 경우는 계수를 0으로 채워야 합니다. 따라서 \(1 + x^{100}\)인 경우는 101개의 원소로 이루어진 리스트를 만들어야합니다.

다음과 같이 다항함수값을 구하는 함수를 만들 수 있습니다.

In [17]: def eval_poly_dict(poly, x):
   ....:   s = 0
   ....:   for pow in poly:
   ....:     s += poly[pow] * x ** pow
   ....:   return s
   ....: 

poly 인자는 위에서 정의한 사전형이어야 하고 poly[pow]는 계수를 의미합니다.

리스트축약과 sum 함수를 이용해서 더 간단히 표현할 수 있습니다.

In [18]: def eval_poly_dict2(poly, x):
   ....:   return sum([poly[pow] * x ** pow for pow in poly])
   ....: 

사전을 사용하면 음수 차수에 대한 함수도 표현할 수 있습니다. 예를 들면 \(\frac{1}{2}x^{-3} + 2x^4\) 은 다음과 같이 표현할 수 있습니다.

p = {-3: 0.5, 4: 2}

기본값 설정 및 순서

\(2x^{-3} -1.5x^{-1} -2x^2\) 에 대한 사전

In [19]: p1 = {-3: 2, -1: -1.5, 2: -2}
   ....: 

이 있다고 할 때 \(p1[1]\) 을 접근하려고 하면 키가 없어서 에러가 발생합니다. 이것을 방지하려면 다음과 같이 해야 합니다.

if key in p1:
  value = p1[key]

또는

value = p1.get(key, 0)

p1.get()은 키가 존재하면 그 값을 반환하고 그렇지 않으면 두번째 인자를 반환합니다.

순서있는 사전

파이썬 버전 3.7 미만에서는 사전은 기본적으로 입력 순서와 상관없이 출력되었습니다. 하지만 3.7 이상에서는 입력된 순서대로 키들을 순회할 수 있게 되었습니다. 순서가 있는 사전을 위해서 collections 모듈에 OrderedDict 객체가 마련되어 있습니다.

In [20]: from collections import OrderedDict
   ....: p2 = OrderedDict({-3: 2, -1: -1.5, 2: -2})
   ....: for key in p2:
   ....:   print(key, p2[key])
   ....: 
-3 2
-1 -1.5
2 -2

파일을 사전으로 저장

다음은 여러 물질의 밀도들을 나타내는 표입니다. src/densities.dat 파일로 저장하세요.

air 0.0012
gasoline 0.67
ice 0.9
pure water 1.0
seawater 1.025
human body 1.03
limestone 2.6
granite 2.7
iron 7.8
silver 10.5
mercury 13.6
gold 18.9
platinium 21.4
Earth mean 5.52
Earth core 13
Moon 3.3
Sun mean 1.4
Sun core 160
proton 2.3E+14

이 파일을 읽어 키: 값 형태의 사전으로 저장하려고 합니다.

In [21]: def read_densities(filename):
   ....:   with open(filename, 'r') as infile:
   ....:     densities = {}
   ....:     for line in infile:
   ....:       words = line.split()
   ....:       density = float(words[-1])
   ....:       if len(words[:-1]) == 2:
   ....:         substance = words[0] + ' ' + words[1]
   ....:       else:
   ....:         substance = words[0]
   ....: 
   ....:       densities[substance] = density
   ....: 
   ....:   return densities
   ....: 

함수를 이용해서 파일을 읽어옵니다.

In [22]: densities = read_densities('src/densities.dat')
   ....: densities
   ....: 
Out[22]: 
{'air': 0.0012,
 'gasoline': 0.67,
 'ice': 0.9,
 'pure water': 1.0,
 'seawater': 1.025,
 'human body': 1.03,
 'limestone': 2.6,
 'granite': 2.7,
 'iron': 7.8,
 'silver': 10.5,
 'mercury': 13.6,
 'gold': 18.9,
 'platinium': 21.4,
 'Earth mean': 5.52,
 'Earth core': 13.0,
 'Moon': 3.3,
 'Sun mean': 1.4,
 'Sun core': 160.0,
 'proton': 230000000000000.0}

파일을 중첩 사전으로 저장

다음은 A, B, C, D 이름을 가진 성질의 측정값입니다. prop_table.dat 라는 이름으로 저장하세요.

A B C D
1 11.7 0.035 2017 99.1
2 9.2 0.037 2019 101.2
3 12.2 no no 105.2
4 10.1 0.031 no 102.1
5 9.1 0.033 2009 103.3
6 8.7 0.036 2015 101.9

파일을 읽어 data['A'][i] 형식으로 중첩 사전으로 저장을 하여 각 성질의 평균도 함께 계산해서 저장하려고 합니다.

In [23]: with open('src/prop_table.dat', 'r') as infile:
   ....:   lines = infile.readlines()
   ....: 
   ....: data = {} # data[property][measurement_no] = propertyvalue
   ....: first_line = lines[0]
   ....: properties = first_line.split()
   ....: for p in properties:
   ....:   data[p] = {}
   ....: 
   ....: for line in lines[1:]:
   ....:   words = line.split()
   ....:   i = int(words[0]) # measurement number
   ....:   values = words[1:] # values of properties
   ....:   for p, v in zip(properties, values):
   ....:     if v != 'no':
   ....:       data[p][i] = float(v)
   ....: 
   ....: # Compute mean values
   ....: for p in data:
   ....:   values = data[p].values()
   ....:   data[p]['mean'] = sum(values) / len(values)
   ....: 
   ....: for p in sorted(data):
   ....:   print('Mean value of property {} = {:.2f}'.format(p, data[p]['mean']))
   ....: 
Mean value of property A = 10.17
Mean value of property B = 0.03
Mean value of property C = 2015.00
Mean value of property D = 102.13

pprint 모듈의 pprint 함수를 이용해 data를 보기좋게 출력해봅니다.

In [24]: import pprint
   ....: 
   ....: pprint.pprint(data)
   ....: 
{'A': {1: 11.7,
       2: 9.2,
       3: 12.2,
       4: 10.1,
       5: 9.1,
       6: 8.7,
       'mean': 10.166666666666666},
 'B': {1: 0.035, 2: 0.037, 4: 0.031, 5: 0.033, 6: 0.036, 'mean': 0.0344},
 'C': {1: 2017.0, 2: 2019.0, 5: 2009.0, 6: 2015.0, 'mean': 2015.0},
 'D': {1: 99.1,
       2: 101.2,
       3: 105.2,
       4: 102.1,
       5: 103.3,
       6: 101.9,
       'mean': 102.13333333333334}}

문자열

많은 프로그램들이 문자열을 다루는 것이 필요합니다.

파이썬 문자열은 열거형으로 인덱스를 이용해서 문자 하나 하나에 접근할 수 있습니다.

문자열에 많이 쓰이는 연산들

부분 문자열

문자열은 슬라이싱을 이용해 부분 문자열을 만들 수 있습니다.

In [25]: s = 'Seoul: 18.4 C at 4 pm'
   ....: s[2:]
   ....: 
Out[25]: 'oul: 18.4 C at 4 pm'
In [26]: s[8:-1]
   ....: 
Out[26]: '8.4 C at 4 p'

문자열 검색

find 메소드를 이용해서 찾고자 하는 문자열의 시작 인덱스를 찾을 수 있습니다.

In [27]: s.find('Seoul')
   ....: 
Out[27]: 0
In [28]: s.find('pm')
   ....: 
Out[28]: 19

검색 문자열이 존재하지 않으면 -1을 반환합니다.

In [29]: s.find('Berlin')
   ....: 
Out[29]: -1

특정 문자열이 속해있는지를 알고 싶으면 in 을 사용할 수 있습니다.

In [30]: 'Berlin' in s
   ....: 
Out[30]: False
In [31]: 'Seoul' in s
   ....: 
Out[31]: True

startswith, endswith 메소드를 이용하여 시작하는 문자열 또는 끝나는 문자열을 찾을 수 있습니다.

In [32]: s.startswith('Seoul')
   ....: 
Out[32]: True
In [33]: s.endswith('am')
   ....: 
Out[33]: False

문자열 대체

replace 메소드를 이용해서 문자열을 대체할 수 있습니다.

In [34]: s.replace(' ', '_')
   ....: 
Out[34]: 'Seoul:_18.4_C_at_4_pm'
In [35]: s.replace('Seoul', 'Jochiwon')
   ....: 
Out[35]: 'Jochiwon: 18.4 C at 4 pm'

여러 연산을 합쳐서 사용할 수 있습니다. 다음은 콜론 앞에 있는 문자열을 대체하는 코드입니다.

In [36]: s.replace(s[:s.find(':')], 'Jochiwon')
   ....: 
Out[36]: 'Jochiwon: 18.4 C at 4 pm'

문자열 분리

split 메소드를 이용해서 문자열을 리스트로 분리할 수 있습니다.

In [37]: s.split()
   ....: 
Out[37]: ['Seoul:', '18.4', 'C', 'at', '4', 'pm']

구분자를 지정해서 구분자를 기준으로 분리할 수 있습니다.

In [38]: s.split(':')
   ....: 
Out[38]: ['Seoul', ' 18.4 C at 4 pm']

splitlines 메소드를 이용해서 줄 단위로 분리할 수 있습니다.

In [39]: t = '1st line\n2nd line\n3rd line'
   ....: t.splitlines()
   ....: 
Out[39]: ['1st line', '2nd line', '3rd line']

또는 \n 특수 문자를 이용해서도 분리해도 됩니다.

In [40]: t.split('\n')
   ....: 
Out[40]: ['1st line', '2nd line', '3rd line']

대/소문자 변환

In [41]: s.lower()
   ....: 
Out[41]: 'seoul: 18.4 c at 4 pm'
In [42]: s.upper()
   ....: 
Out[42]: 'SEOUL: 18.4 C AT 4 PM'

문자열은 불변 객체

문자열은 불변 객체이기 때문에 한번 정해지면 내용을 바꿀 수 없습니다.

In [43]: s[18] = 5
   ....: 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-43-99e183842b4b> in <module>
----> 1 s[18] = 5

TypeError: 'str' object does not support item assignment

앞에서 했던 replace 메소드에 의해서 변경된 문자열은 새로 만들어진 문자열입니다.

숫자 문자열

isdigit 메소드는 문자열이 오직 숫자들만 포함하고 있는지를 판단합니다.

In [44]: '214'.isdigit()
   ....: 
Out[44]: True
In [45]: ' 214'.isdigit()
   ....: 
Out[45]: False

다음은 소수점을 포함하는 문자열이기때문에 False가 반환됩니다.

In [46]: '2.14'.isdigit()
   ....: 
Out[46]: False

공백 문자열

isspace 메소드는 문자열이 공백 문자열로만 이루어졌는지를 판단합니다. 공백문자열은 스페이스(space), 새로운 줄(new line), 탭(tab) 문자등이 있습니다.

In [47]: '   '.isspace()
   ....: 
Out[47]: True
In [48]: '  \n'.isspace()
   ....: 
Out[48]: True
In [49]: '   \t'.isspace()
   ....: 
Out[49]: True

다음은 빈문자열(empty string)이기때문에 False 입니다.

In [50]: ''.isspace()
   ....: 
Out[50]: False

isspace 를 이용하면 쉽게 빈 줄을 확인할 수 있습니다. 빈 줄인 것을 확인하는 방법으로 다음과 같은 것도 사용할 수 있습니다.

In [51]: line = '     \n'
   ....: line.strip() == ''
   ....: 
Out[51]: True

strip 메소드는 문자열의 시작과 끝 부분의 공백문자들을 모두 제거한 문자열을 반환합니다.

In [52]: '  \n\t 앞 뒤의 공백 문자열을 제거합니다. \n  '.strip()
   ....: 
Out[52]: '앞 뒤의 공백 문자열을 제거합니다.'

앞 부분 또는 뒷 부분의 공백 문자열을 제거하려면 lstrip 또는 rstrip 메소드를 이용합니다.

In [53]: '  \n\t 앞 뒤의 공백 문자열을 제거합니다. \n  '.lstrip()
   ....: 
Out[53]: '앞 뒤의 공백 문자열을 제거합니다. \n  '
In [54]: '  \n\t 앞 뒤의 공백 문자열을 제거합니다. \n  '.rstrip()
   ....: 
Out[54]: '  \n\t 앞 뒤의 공백 문자열을 제거합니다.'

문자열 합치기

join 메소드를 이용해서 문자열 리스트를 구분 문자열과 함께 합칠 수 있습니다.

In [55]: s = ['Newton', 'Secant', 'Bisection']
   ....: ', '.join(s)
   ....: 
Out[55]: 'Newton, Secant, Bisection'

split 메소드와 join 메소드를 이용해서 문자열 중에서 원하는 단어들만 뽑아낼 수도 있습니다.

In [56]: line = 'This is a line of words separated by space'
   ....: words = line.split()
   ....: line2 = ' '.join(words[2:])
   ....: line2
   ....: 
Out[56]: 'a line of words separated by space'

문자열 함수에 대해서 더 알고 싶으면 help(str), help(str.lower) 또는 dir(str) 함수를 이용하면 됩니다.

예제: 순서쌍 읽기

다음과 같은 내용의 파일을 읽어 float 형의 숫자 순서쌍 튜플들의 리스트로 저장하는 프로그램을 작성하려고 합니다. 아래 내용을 read_pairs1.dat 파일에 저장하세요.

(1.3,0) (-1,2) (3,-1.5)
(0,1) (1,0) (1,1)
(0,-0.01) (10.5,-1) (2.5,-2.5)

풀이

  • 줄단위로 파일을 읽습니다.

  • 각 줄에서 공백 기준으로 문자열을 분리합니다.

  • 각 단어에서 소괄호를 제거합니다.

  • 소괄호가 제거된 문자열에서 쉼표를 기준으로 두 숫자를 분리하여 튜플로 저장합니다.

  • 튜플을 리스트에 추가합니다.

In [57]: # 파일에서 줄단위로 읽어옵니다.
   ....: with open('src/read_pairs1.dat', 'r') as infile:
   ....:   lines = infile.readlines()
   ....: 
   ....: # 각 라인별 내용 분석
   ....: pairs = [] # (n1, n2) 순서쌍 리스트
   ....: for line in lines:
   ....:   words = line.split()
   ....:   for word in words:
   ....:     word = word[1:-1] # 소괄호 제거
   ....:     n1, n2 = word.split(',')
   ....:     n1 = float(n1)
   ....:     n2 = float(n2)
   ....:     pair = (n1, n2)
   ....:     pairs.append(pair) # 리스트에 추가
   ....: 
   ....: pairs
   ....: 
Out[57]: 
[(1.3, 0.0),
 (-1.0, 2.0),
 (3.0, -1.5),
 (0.0, 1.0),
 (1.0, 0.0),
 (1.0, 1.0),
 (0.0, -0.01),
 (10.5, -1.0),
 (2.5, -2.5)]

위 코드는 데이터의 소괄호 안에 공백 문자가 들어있으면 제대로 작동을 하지 않습니다. 이럴 때는 각 줄에서 먼저 공백 문자열을 모두 제거하고 )( 구분자를 이용하여 문자열을 분리하고 처음과 마지막에 남아있는 괄호를 제거하는 방법으로 진행할 수 있습니다.

다음 내용을 read_pairs2.dat 파일에 저장합니다.

  (1.3 , 0) (-1 , 2 ) (3, -1.5)
(0 , 1)   ( 1, 0)   ( 1 , 1 )
(0,-0.01)   (10.5,-1)   (2.5, -2.5)
In [58]: # 파일에서 줄단위로 읽어옵니다.
   ....: with open('src/read_pairs2.dat', 'r') as infile:
   ....:   lines = infile.readlines()
   ....: 
   ....: # 각 라인별 내용 분석
   ....: pairs = [] # (n1, n2) 순서쌍 리스트
   ....: for line in lines:
   ....:   line = line.strip() # new line 문자 제거
   ....:   line = line.replace(' ', '') # 공백 제거
   ....:   words = line.split(')(') # )( 구분 분리
   ....:   words[0] = words[0][1:] # 첫 ( 제거
   ....:   words[-1] = words[-1][:-1] # 마지막 ) 제거
   ....:   for word in words:
   ....:     n1, n2 = word.split(',')
   ....:     n1 = float(n1)
   ....:     n2 = float(n2)
   ....:     pair = (n1, n2)
   ....:     pairs.append(pair) # 리스트에 추가
   ....: 
   ....: pairs
   ....: 
Out[58]: 
[(1.3, 0.0),
 (-1.0, 2.0),
 (3.0, -1.5),
 (0.0, 1.0),
 (1.0, 0.0),
 (1.0, 1.0),
 (0.0, -0.01),
 (10.5, -1.0),
 (2.5, -2.5)]

정규표현식

문자열 검색 및 변경을 위해서 정규표현식(regular expression)이라는 강력한 도구도 있습니다. 여기서는 예제를 통해서 간단한 소개만 합니다.

파이썬에서 정규식을 사용하려면 import re 를 해야합니다.

In [59]: import re

메타문자 meta character 들은 다음과 같습니다. 이 문자들은 특별한 의미가 있습니다.

. ^ $ * + ? { } [ ] \ | ( )

문자클래스

[] 은 문자클래스 character class 라고 하며 대괄호 안에 있는 문자들을 찾습니다. 대괄호 안에서 ^, - 만 특별한 의미를 갖고 다른 메타문자들은 문자 그대로를 의미합니다.

다음은 각각의 문자 a, b, c를 찾습니다.

In [60]: re.findall("[abc]", "acde")
Out[60]: ['a', 'c']

문자클래스 안에서 맨 처음에 ^ 가 나오면 대괄호 안에 있는 문자들을 제외한 모든 문자들을 찾습니다.

In [61]: re.findall("[^abc]", "abcdef")
Out[61]: ['d', 'e', 'f']

문자클래스 안에 - 기호는 문자와 문자 사이에 위치하면 앞에 문자와 뒤에 문자를 포함한 모든 문자들 찾습니다.

In [62]: re.findall("[a-c2-5]", "a1b2c3d4")
Out[62]: ['a', 'b', '2', 'c', '3', '4']

백슬래시 \

백슬래시는 문자열 리터럴에서 탈출문자 역할을 했듯이 정규식 패턴에서 비슷한 역할을 합니다. 만일 대괄호 [를 찾고자 하면 \[를 이용해서 대괄호가 가지는 문자클래스 의미를 제거할 수 있습니다.

  • \d 는 0-9 까지의 숫자 하나를 의미하고 \D 는 0-9 까지의 숫자가 아닌 문자를 의미합니다.

  • \s 는 공백문자들을 의미합니다. 즉, 스페이스, 탭등을 의미합니다. \S 는 공백문자가 아닌 것을 의미합니다.

  • \w 는 알파벳과 숫자 alphanumeric 들을 의미하고 \W는 여집합을 의미합니다.

이밖에도 여러 가지 특수 문자들이 주어집니다.

.

. 은 임의의 문자 하나를 의미합니다.

In [63]: re.findall(".", "abc d-1^*#")
Out[63]: ['a', 'b', 'c', ' ', 'd', '-', '1', '^', '*', '#']

캐럿 caret ^

캐럿은 문자열의 시작을 의미합니다.

In [64]: re.findall("^ab", "ab abc def")
Out[64]: ['ab']

달러 dollar $

달러 문자는 문자열의 끝을 의미합니다.

In [65]: re.findall("ef$", "ab abc ef def")
Out[65]: ['ef']

별표 *

별표는 별표 앞에 있는 패턴이 0번 이상 반복되는 문자들을 의미합니다.

In [66]: re.findall("ab*", "ab abb daf")
Out[66]: ['ab', 'abb', 'a']

별표가 패턴의 맨 앞에 오면 에러가 발생합니다.

더하기 +

더하기 기호는 앞의 패턴이 한 개 이상 반복되는 것을 찾습니다.

In [67]: re.findall("ab+", "ab abb daf")
Out[67]: ['ab', 'abb']

물음표 ?

물음표 기호는 앞에 있는 패턴이 0개 또는 1개인 문자들을 찾습니다.

In [68]: re.findall("ab?", "ab abb daf cabbb")
Out[68]: ['ab', 'ab', 'a', 'ab']

중괄호 {}

중괄호는 중괄호 안에 자연수를 넣어 중괄호 앞에 있는 패턴이 숫자만큼 반복되는 것을 찾습니다.

In [69]: re.findall("ab{2}", "ab abb daf abbbbbc")
Out[69]: ['abb', 'abb']

{m, n} 을 사용하면 앞에 있는 패턴이 최소 m 번부터 최대 n 번까지 반복되는 것을 찾습니다.

더 자세한 것은 파이썬 문서를 참조하세요.

다음과 같은 문자열이 주어졌다고 가정합니다.

In [70]: pairs = """    (1.3 , 0) (-1 , 2 ) (3, -1.5)
   ....: (0 , 1)   ( 1, 0)   ( 1 , 1 )
   ....: (0,-0.01)   (10.5,-1)   (2.5, -2.5)"""
   ....: 

이 문자열을 숫자 튜플의 리스트로 변환하는 코드를 정규표현식을 이용하면 다음과 같이 구할 수 있습니다.

In [71]: s = re.sub('\s+', '', pairs)
   ....: s1 = re.findall('\(([-\d\.]*),([-\d\.]*)?\)', s)
   ....: [(float(x), float(y)) for x, y in s1]
   ....: 
Out[71]: 
[(1.3, 0.0),
 (-1.0, 2.0),
 (3.0, -1.5),
 (0.0, 1.0),
 (1.0, 0.0),
 (1.0, 1.0),
 (0.0, -0.01),
 (10.5, -1.0),
 (2.5, -2.5)]
  • import re 를 이용하여 정규표현식 모듈을 부릅니다.

  • re.sub 메소드는 세번째 인자의 문자열에서 첫번째 인자 문자열 패턴을 찾아 두번째 인자 문자열로 바꿉니다.

  • re.findall 메소드는 두번째 인자 문자열에서 첫번째 문자열 패턴에 해당하는 문자열을 검색하여 리스트로 반환합니다.

  • \s+ : 공백 문자열 1개 이상 매치

  • \( : ( 문자 매치

  • [-\d\.]* : 음수부호 -, 숫자 또는 소숫점 .이 0개 이상 매치

  • ?\) : 가장 가까운 ) 문자 매치

연습문제

  1. 다음 내용을 파일 human_evolution.txt로 저장하세요.

    Species              Lived when      Adult        Adult       Brain volume
                         (mill. yrs)     height (m)   mass (kg)   (cm**3)
    -------------------------------------------------------------------------------
    H.habilis           2.2-1.6          1.0-1.5      33-55       660
    H.erectus           1.4-0.2          1.8            60        850(early)-1100(late)
    H.ergaster          1.9-1.4          1.9            NA        700-850
    H.heidelbergensis   0.6-0.35         1.8            60        1100-1400
    H.neanderthalensis  0.35-0.03        1.6          55-70       1200-1700
    H.sapiens sapiens   0.2-present      1.4-1.9      50-100      1000-1850
    H.floresiensis      0.10-0.012       1.0            25        400
    -------------------------------------------------------------------------------
    
    Source: http://en.wikipedia.org/wiki/Human_evolution
    

    저장된 파일을 읽어서 중첩된 사전 humans에 저장하고 출력해보세요. humans의 키(key)는 인류의 종류로 하고 대응되는 값은 해당하는 행의 정보들(when, height, weight, brain)을 키로하는 사전으로 하세요. 예를 들어 humans['H.erectus']['weight']60이 되어야 합니다.