Strings
사용자 입력이나 filename, text chunk 처리와 같은 text handling은 programming에서 일반적인 작업이다. Python은 text를 취급하고 format하는 강력한 tool들을 포함하고 있다. 여기에서는 Python의 표준 문자열(string)과 문자열(string)관련 연산들에 대해서 다룰 것이다.
문자열(string)은 문자(character)들의 순서열(sequence)이다.
문자(character)들과 부분 문자열(substring)을 추출하는데 있어서 문자열(string)은 문자(character)들의 순서열(sequence)로 간주할 수 있다. 즉 index나 slice 표기법을 사용할 수 있다는 것이다.
>>> x = "Hello" >>> x[0] 'H' >>> x[-1] 'o' >>> x[1:] 'ello' |
문자열(string)에서 slice 표기법의 한 가지 사용법은 file을 읽어올 때 문자열의 끝에 newline을 잘라내는데 사용할 수 있다.
>>> x = "Goodbye\n" >>> x = x[:-1] >>> x 'Goodbye' |
Python strings는 원치않는 문자들을 떼어내는데 더 낳은 다른 방법들을 가지고 있지만, 위의 예제는slicing의 유용함을 설명하기 위해서 기술한 것이다.
또한 list 내에서 요소들의 숫자를 알아내는 것처럼 len 함수를 사용하여 문자열 내에 문자들이 얼마나 존재하는 지를 알아볼 수도 있다.
>>> len("Goodbye") 7 |
하지만 문자열(string)은 문자들의 list가 아니다. string과 list 사이에 가장 큰 차이점은 list와는 다르게 string은 수정할 수 없다는 것이다. string.append('c') 이나 string[0] = 'H' 같은 code를 입력하면error를 발생시킬 것이다. 이전의 예제에서 문자열의 newline을 떼어내는 것은 이전의 문자열을 바로 수정하는 것이 아니라 이전의 문자열의 slice를 새로운 문자열로 생성하는 것이라는 것을 알 수 있을 것이다. 이는 효율성 때문에 도입된 Python의 기본 제한 사항이다.
기본 문자열(string) 연산
Python 문자열(string)들을 합치는데 가장 단순하고 일반적인 방식은 문자열 연속 연산자 “+”를 사용하는 것이다.
>>> x = "Hello " + "World" >>> x 'Hello World' |
그리고 자주 사용되지는 않지만 유용한 문자열 곱셈 연산자 “*”도 존재한다.
>>> 8 * "x" 'xxxxxxxx' |
특수 문자와 확장 문자(escape sequence)
당신은 이미 문자열들과 같이 사용될 때 Python에서 특별하게 인식되는 몇몇 character sequence들을 본 적이 있을 것이다. “\n”은 newline 문자를 나타내며, “\t”는 tab 문자를 나타낸다. backslash(\)로시작하며 다른 문자들을 나타내는데 사용되는 문자들을 확장 문자(escape sequence)라고 부른다. 확장 문자(escape sequence)는 일반적으로 tab이나 newline 같이 하나의 문자로 표현할 수 없는 특수 문자들을 나타내는데 사용된다. 여기에서는 확장 문자(escape sequence)와 특수 문자들을 설명하고 추가로 관련된 상세 주제들도 다룰 것이다.
기본 확장 문자(escape sequence)들
Python은 문자열 내에서 사용되며 두 문자로 이루어진 확장 문자(escape sequence)들에 대해서 간략한 표를 제공하고 있다.
표) 확장 문자(escape sequence)
확장 문자(escape sequence) | 나타내는 문자 |
\' | 따옴표 문자 |
\" | 쌍따옴표 문자 |
\\ | backslash 문자 |
\a | bell 문자 |
\b | backslash 문자 |
\f | Formfeed 문자 |
\n | newline 문자 |
\r | carriage return 문자(\n와 동일하지 않다.) |
\t | tab 문자 |
\v | vertical tab 문자 |
Python에서 사용되며 거의 모든 computer 상에서 표준 character set인 ASCII character set은 상당수의 특수 문자들을 더 정의하고 있다. 이들은 숫자 형식 확장 문자(escape sequence)들로 접근할 수 있으며 다음 절에서 설명할 것이다.
숫자 형식 (8진법과 16진법) 확장 문자(escape sequence)들과 Unicode 확장 문자(escape sequence)들
8진수 또는 16진수 확장 문자(escape sequence)를 사용하여 ASCII 문자를 포함시킬 수 있다. 8진수 확장 문자(escape sequence)는 backslash(\)와 세 자릿수 8진수 숫자를 사용한다. 결과로는 8진수 확장 문자(escape sequence)에 해당하는 ASCII 문자가 출력된다. 16진수 확장 문자(escape sequence)도 이와 비슷하지만 “\”가 아니라 “\x”로 시작되며 16진수 숫자로 구성되는 점이 다르다. 이 확장 문자(escape sequence)는 문자가 더 이상 16진수가 아닐 때 종료된다. 예를 들어 문자 m은 ASCII 문자표에서 십진수 값 109이다. 이는 8진수로 하면 155이며 16진수로 하면 6D 이다.
>>> 'm' 'm' >>> '\155' 'm' >>> '\x6D' 'm' |
위의 세 가지 표현 모두 단일 문자 m을 포함하는 문자열로 출력된다. 이 양식은 출력을 할 수 없는 문자를 나타내는데도 사용할 수 있다. 예를 들면 newline 문자 “\n”은 8진수 값으로는 012이며 16진수 값으로는 0A이다.
>>> '\n' '\n' >>> '\012' '\n' >>> '\x0A' '\n' |
Python 3 내의 모든 문자열은 Unicode 문자열(string)이므로 모든 language에서 사용 가능한 대부분의 문자를 포함할 수 있다. 여기에서 설명하지는 않지만 다음 예제는 위에서 설명한 숫자 형식이나Unicode 명으로 Unicode 문자를 escape할 수도 있다는 것을 보여준다.
>>> unicode_a ='\N{LATIN SMALL LETTER A}' # Unicode 명으로 escape >>> unicode_a # Unicode character set은 일반 ASCII 문자들도 포함한다. 'a' >>> unicode_a_with_acute = '\N{LATIN SMALL LETTER A WITH ACUTE}' # Unicode 명으로 escape >>> unicode_a_with_acute 'á' >>> "\u00E1" # \u를 사용한 숫자 형식으로 escape 'á' >>> |
특수 문자를 포함한 문자열을 출력 및 감정
Python 표현식을 대화식으로 감정하는 것과 print 함수를 사용하여 결과를 출력하는 것의 차이점에 대해서 전에 언급한 적이 있다. 동일한 문자열을 포함하고 있더라도 이 두 연산은 다르게 screen에 출력될 수 있다. 대화형 Python session의 최상위에서 감정하는 문자열은 8진수 확장 문자(escape sequence)로 된 특수 문자들 모두를 명확하게 보여줄 것이다. 한편, print 함수는 특수한 방식으로 특수 문자들을 해석하는 terminal program에 바로 전달한다. 예를 들어, 다음 예제는 a 다음에 newline이 오고 그 다음에 tab, b가 오는 문자열이 어떻게 되는지를 보여준다.
>>> 'a\n\tb' 'a\n\tb' >>> print('a\n\tb') a b |
첫 번째 경우에는 newline과 tab이 문자열 내에서 명확하게 표시된다. 두 번째는 이들이 newline과tab 문자들로 사용된다.
일반적으로 print 함수도 문자열 마지막에 newline을 추가한다. 가끔씩 마지막에 이미 newline으로 끝나는 file에서 line을 읽어올 때 위와 같은 동작을 하지 않기를 원할 수도 있다. print 함수에 end parameter를 ""으로 주면 print 함수는 newline을 추가하지 않는다.
>>> print("abc\n") abc >>> print("abc\n", end="") abc >>> |
string method
대부분의 Python string method들은 표준 Python string class에 내장되어져 있으므로, 모든 string object들은 자동으로 이 method들을 가지게 된다. 표준 string module은 몇몇 유용한 상수들도 보유하고 있다. module에 대해서는 추후 설명하도록 하겠다.
여기에서 당신이 기억해두어야 할 것은 대부분의 string method들은 x.upper()와 같이 string object에 점(.)으로 연결하여 연산되는 형식이라는 것이다. 즉, 이들은 string object 뒤의 점(.) 다음에 붙여진다.
문자열(string)은 변경이 불가능하므로 string method는 반환 값을 취득하는데만 사용되며, 붙여지는string object에 아무런 수정도 할 수 없다.
여기서는 가장 유용하며 일반적으로 사용되는 문자열 연산을 설명하고, 그 다음 일반적으로 사용되지는 않지만 유용한 연산에 대해서 설명하도록 하겠다. 그리고 마지막으로 문자열에 관련있는 다양한 부분들을 몇 가지 설명할 것이다. 모든 string method를 여기에서 다루지는 않는다. string method의 전체 list는 documentation을 참조하라.
string method split, join
문자열을 다루는 사람이라면 split과 join method가 매우 유용하다는 것을 알고 있을 것이다. 이들은 서로 다른 역활으로 기능한다. split는 문자열의 부분 문자열(substring)의 list를 반환하며 join은 문자열들의 list를 가지고 원본 문자열을 각 요소 사이에 넣는 형식으로 합하여 하나의 문자열을 반환한다.일반적으로 split는 공백을 기본 구분자로 사용하여 문자열을 나누지만 선택 사항으로 argument를 사용하여 이 구분자를 변경할 수동 있다.
“+”를 사용한 문자열 연결은 유용하지만 많은 수의 문자열을 하나의 문자열로 합치는데 효율적이지 않다. “+”가 적용될 때 마다 새로운 string object가 생성되기 때문이다. 더 낳은 방식은 join 함수를 사용하는 것이다.
>>> " ".join(["join", "puts", "spaces", "between", "elements"]) 'join puts spaces between elements' |
join에 사용되는 문자열을 변경하여 연결되는 문자열 사이에 들어가는 문자열을 바꿀 수도 있다.
>>> "::".join(["Separated", "with", "colons"]) 'Separated::with::colons' |
list 내의 항목들을 연결하는데 빈 문자열 ""도 사용이 가능하다.
>>> "".join(["Separated", "by", "nothing"]) 'Separatedbynothing' |
split의 가장 일반적인 사용법은 text file 내에 문자열로 구분된 record들의 단순한 parsing mechanism이다. 기본적으로 split는 단일 공백 문자가 아니라 공백 그 자체로 나누지만, option argument를 전달하여 특정 순서열(sequence)로 구분하여 나눌 수도 있다.
>>> x = "You\t\t can have tabs\t\n \t and newlines \n\n " \ "mixed in" >>> x.split() ['You', 'can', 'have', 'tabs', 'and', 'newlines', 'mixed', 'in'] >>> x = "Mississippi" >>> x.split("ss") ['Mi', 'i', 'ippi'] |
가끔씩은 유용하다 연결된 문자열 내에서 마지막 field를 허용하는 것은 임의의 text를 포함하기 위해서 아마 부분 문자열(substring)을 포함하는 split가 나누는데 일치하는 해당 data를 읽어올 때
data를 읽어와서 split으로 나누면서 마지막 field를 특정 부분 문자열(substring)로 반환하도록 해주는 것은 가끔씩 유용하게 사용될 때가 있다. 이는 split 함수의 두 번째 option argument에 몇 번을 나눌 것인지 지정하는 것으로 원하는 결과를 얻을 수 있다. split에 n번 나누도록 지정하면, 입력 문자열을n번 나누어 n+1개의 항목들을 가진 list를 반환한다. 나누는 중간에 문자열이 끝나면 모두 나눠진 항목들을 가진 list를 반환한다. 다음 예제는 이를 설명하고 있다.
>>> x = 'a b c d' >>> x.split(' ', 1) ['a', 'b c d'] >>> x.split(' ', 2) ['a', 'b', 'c d'] >>> x.split(' ', 9) ['a', 'b', 'c', 'd'] |
split 함수를 두 번째 argument와 함께 사용하려면 반드시 첫 번째 argument를 기재해야 한다. split함수를 두 번째 argument를 사용하면서 공백으로 나누려면, 첫 번째 argument를 None으로 사용해야 한다.
일반적으로 다른 program들이 생성한 text file들을 다루면서 split과 join을 광범위하게 사용한다. 하지만 당신의 Python program들에서만 사용되는 형식의 data file을 정의할 수 있다면 text file들 내에data를 저장하는데 더 낳은 것도 존재한다는 것을 명심해야 한다. data 저장에 사용되는 Pickle module은 나중에 설명하도록 하겠다.
문자열을 숫자로 변환
int와 float 함수를 사용하여 문자열을 정수나 부동소수점 숫자로 각각 변환할 수 있다. 만약 이들 함수에 주어진 type의 숫자로 해석될 수 없는 문자열을 전달하면 ValueError exception이 발생한다. exception에 대해서는 후에 설명할 것이다. 추가적으로 int 함수에 두 번째 option argument를 전달하면 입력 문자열을 해석할 때 두 번째 option argument에 지정된 숫자 진법으로 결과를 반환받을 수 있다.
>>> float('123.456') 123.456 >>> float('xxyy') Traceback (innermost last): File "<stdin>", line 1, in ? ValueError: invalid literal for float(): xxyy >>> int('3333') 3333 >>> int('123.456') # 정수 내에서 소수점을 가질 수 없다. Traceback (innermost last): File "<stdin>", line 1, in ? ValueError: invalid literal for int() with base 10: 123.456 >>> int('10000', 8) # 10000을 8진수로 해석한다. 4096 >>> int('101', 2) 5 >>> int('ff', 16) 255 >>> int('123456', 6) # 123456를 6진수로 해석을 할 수 없다. Traceback (innermost last): File "<stdin>", line 1, in ? ValueError: invalid literal for int() with base 6: '123456' |
마지막 error의 이유를 당신은 아는가? 마지막에 문자열을 6진수로 해석하라는 요청을 했지만Python은 영리하게도 십진수 6이 6진수에서 존재할 수 없다는 알고 있으므로 해당 error를 발생시키는 것이다.
여분의 공백을 제거
strip, lstrip, rstrip 이렇게 세 가지 method는 매우 유용하게 사용된다. strip은 원래 문자열에서 처음과 끝의 공백을 제거하여 이를 새로운 문자열로 반환한다. lstrip은 원래 문자열에서 왼쪽의 공백을 제거하여 이를 새로운 문자열로 반환하고 rstrip은 원래 문자열에서 오른쪽의 공백을 제거하여 이를 새로운 문자열로 반환한다.
>>> x = " Hello, World\t\t " >>> x.strip() 'Hello, World' >>> x.lstrip() 'Hello, World\t\t ' >>> x.rstrip() ' Hello, World' |
위의 예제에서 tab 문자들도 공백으로 인식한다는 것을 알 수 있다. 정확하게는 운영체제마다 달라질 수 있다는 것이다. 하지만 string.whitespace 상수에 접근하여 Python에서 공백으로 인식하는 것을 알아볼 수 있다. 예를 들면 mac에서는 다음과 같이 인식값을 반환했다.
>>> import string >>> string.whitespace '\t\n\x0b\x0c\r ' >>> " \t\n\r\v\f" ' \t\n\r\x0b\x0c' |
backslash 16진수 형식(\xnn)의 문자들은 vertical tab과 formfeed 문자를 나타낸다. 공백 문자는 이 상수에 있는 값으로 사용된다. 만약 strip 등에 사용되는 이 변수의 값을 변경하여 사용하려고 할 수 있다. 하지만 이와 같은 행동을 하지 않기 바란다. 이 결과로 당신에게 전달되는 결과값들이 어떻게 될 지 보장을 할 수 없기 때문이다.
그 대신에 strip, rstrip, lstrip에 추가 parameter로 제거할 문자열을 전달하여 이 문자들을 변경할 수 있다.
>>> x = "www.python.org" >>> x.strip("w") # 모든 "w" 문자를 제거한다. '.python.org' >>> x.strip("gor") # 모든 "g", "o", "r" 문자들을 제거한다. 'www.python.' >>> x.strip(".gorw") # 모든 "g", "o", "r", "w" 문자들을 제거한다. 'python' |
strip은 추가 parameter 문자열 내의 모든 문자들을 순서에 상관없이 모두 제거한다는 것을 주의하라.
이 함수들의 가장 일반적인 사용법은 읽어들인 문자열을 빠르게 정리하는 방법으로 사용하는 것이다.이는 file들에서 line을 읽어들일 때 특히 유용하다. Python은 언제나 newline 문자까지 그대로 포함하여 전체 line을 읽어들이기 때문이다. 당신이 line을 읽어들여서 처리할 때 일반적으로 newline 문자를 포함하지 않고 처리해야 할 때가 있을 것이다. rstrip은 이를 제거할 때 편리하게 사용될 수 있다.
문자열 검색
string object는 단순한 문자열 검색을 수행하는 method를 다수 제공하고 있다.
네 가지 기본 문자열 검색 method(find, rfind, index, rindex) 모두 유사하다. 이와 관련된 method인count는 문자열 내에서 부분 문자열(substring)을 몇 번이나 찾을 수 있는 지를 센다. 여기에서 find를 상세하게 알아본 후에 다른 method들은 find와 어떻게 다른 지를 알아볼 것이다.
find는 찾으려는 부분 문자열(substring)을 argument로 받아야 한다. find는 string object에서 부분 문자열(substring)의 첫 번째 instance의 첫 번째 문자의 위치를 반환하며 부분 문자열(substring)을 찾지 못하면 -1을 반환한다.
>>> x = "Mississippi" >>> x.find("ss") 2 >>> x.find("zz") -1 |
find는 하나나 둘의 추가 option argument들도 입력 받을 수 있다. 첫 번째 추가 option argument는start 정수를 입력 받는다. 이 값은 find가 start 지점 이후부터 부분 문자열(substring)을 검색하도록 한다. 두 번째 추가 option argument는 end 정수를 입력 받는다. 이 값은 find가 end 지점까지만 부분 문자열(substring)을 검색하게 한다.
>>> x = "Mississippi" >>> x.find("ss", 3) 5 >>> x.find("ss", 0, 3) -1 |
rfind는 문자열을 끝에서 검색을 시작하며 문자열에서 마지막으로 일치하는 부분 문자열(substring)의 첫 번째 문자의 위치를 반환한다는 점을 제외하면 find와 거의 동일하다.
>>> x = "Mississippi" >>> x.rfind("ss") 5 |
rfind는 find와 동일하게 하나나 둘의 추가 option argument들도 입력 받을 수 있다.
index와 rindex는 한 가지 차이점을 제외하면 find, rfind와 동일하다. 여기서 차이점은 index, rindex가 문자열에서 부분 문자열(substring)을 찾지 못한다면 -1을 반환하는 것이 아니라 ValueError exception을 발생시키는 것이다.
count는 이전의 네 가지 함수들과 동일하게 사용되지만 문자열에서 부분 문자열(substring)을 겹치지 않고 찾은 횟수를 반환한다.
>>> x = "Mississippi" >>> x.count("ss") 2 |
startswith, endswith string method를 사용하여 문자열을 검색할 수 있다. 이들 method는parameter로 주어진 문자열들에서 하나로 시작하거나 끝나는지에 따라서 True나 False를 반환한다.
>>> x = "Mississippi" >>> x.startswith("Miss") True >>> x.startswith("Mist") False >>> x.endswith("pi") True >>> x.endswith("p") False |
startswith, endswith 둘 다 한번에 하나 이상의 문자열을 찾을 수 있다. 만약 parameter를 문자열들의 tuple로 주면 두 method들은 tuple 내의 문자열을 모두 조사하여 이들 중에 하나라도 발견한다면True를 반환한다.
>>> x.endswith(("i", "u")) True |
startswith와 endswith는 단순 검색에 유용하게 사용할 수 있다.
문자열 수정
문자열은 수정할 수 없지만 string object는 문자열을 조작하여 원본 문자열에서 수정된 version의 새로운 문자열을 반환하는 많은 method들을 가지고 있다. 이는 원래 하려는 직접적인 수정과 동일한 효과를 제공한다. 이 method들에 대해서 좀 더 완전한 설명은 documentation을 참조하기 바란다.
replace method를 사용하여 문자열 내에서 부분 문자열(첫 번째 argument)을 새로운 문자열(두 번째 argument)로 교체를 할 수 있다. 이 method는 세 번째 option argument도 입력받을 수 있다.(상세 내용은 documentation 참조)
>>> x = "Mississippi" >>> x.replace("ss", "+++") 'Mi+++i+++ippi' |
문자열 검색 함수들 중에서 re module은 더욱 강력한 부분 문자열(substring) 교체 방식을 제공한다.
string.maketrans와 string.translate 함수는 문자열 내의 문자들을 다른 문자들로 변경하기 위해서 같이 사용된다. 거의 사용되지는 않지만 이 함수들이 필요할 때 유용하게 사용될 수 있다.
예를 들어 당신이 문자열 표현식을 하나의 computer language에서 다른 language로 번역하는program을 개발한다고 가정해보자. 첫 번째 language는 논리적인 not으로 ~을 사용하며, 두 번째language는 !를 사용한다. 첫 번째 language는 논리적인 and로 ^를 사용하며, 두 번째 language는 &를 사용한다. 첫 번째 language는 ()를 사용하며, 두 번째 language는 []를 사용한다. 주어진 문자열 표현식에서 ~는 !로, ^는 &로, (는 [로, )는 ]로 모든 instance를 변경해야 한다. 이를 수행하기 위해서replace의 다중 호출을 사용할 수도 있지만 다음 방식을 사용하면 더욱 쉽게 변환이 가능하다.
>>> x = "~x ^ (y % z)" >>> table = x.maketrans("~^()", "!&[]") >>> x.translate(table) '!x & [y % z]' |
첫 line은 문자열 argument 둘로 해석 table을 형성하도록 maketrans를 사용한다. 두 argument들은 반드시 각각 동일한 갯수의 문자들을 포함해야 한다. 첫 번째 argument의 n번째 문자를 검색하고 두 번째 argument의 n번째 문자로 변환하여 해석 table을 생성할 것이다.
다음으로 maketrans로 생성한 해석 table을 translate에 전달한다. 그러면 translate는 string object의 문자들을 각각 검토하여 argument로 주어진 table에서 일치하는 문자를 찾을 수 있는 지를 check한다. 만약 해석 table에서 일치하는 문자를 찾는다면 translate는 해석된 문자열을 생성하기 위해서 해당 문자를 table에서 검색된 대치 문자로 변경한다.
translate에 문자열에서 완전히 제거하려는 문자들을 option argument로 명시할 수도 있다. 상세한 내용은 documentation을 참조하라.
string module 내의 다른 함수들은 더욱 특화된 task들을 수행한다. string.lower는 모든 alphabet 문자들을 소문자로 변환하고, upper는 반대로 대문자 변환을 수행한다. capitalize는 문자열의 첫 문자를 대문자로 변환하며, titile은 문자열 내의 모든 단어들에서 첫 문자를 대문자로 변환한다. swapcase는 동일 string 내에서 소문자를 대문자로, 대문자를 소문자로 변경한다. expandtabs는 문자열에서 tab문자들을 지정된 숫자의 공백들로 변경한다. ljust, rjust, center는 공백을 이용하여 특정 field 너비에 맞출 수 있도록 공백을 사용하여 왼쪽 정렬, 오른쪽 정렬, 중앙 정렬을 수행한다. zfill은 문자열의 좌측에 지정한 길이만큼 0들을 추가한다. 이들 method들에 대한 상세사항은 documentation을 참조하라.
list 조작을 통한 문자열 수정
문자열은 수정할 수 없는 object이므로 list와 동일한 방식으로는 직접적으로 수정할 수 없다. 원본 문자열은 그대로 두고 새로운 문자열을 생성하는 연산들도 여러 면에서 유용하지만 가끔씩 문자들로 된list 처럼 문자열을 조작할 수 있다면 편리할 것이다. 이 경우에는 문자들의 list로 변경한 후에 수행하려는 작업을 하고 list를 문자열로 다시 변환한다.
>>> text = "Hello, World" >>> wordList = list(text) >>> wordList[6:] = [] # 쉼표 뒤의 모든 것들을 제 >>> wordList.reverse() >>> text = "".join(wordList) # 아무것도 없는 문자열과 연결 >>> print(text) ,olleH |
문자열을 문자들의 list로 변환하기 위해서 split을 사용할 수도 있지만 list 형변환 함수는 더 쉽게 사용하고 기억하기 좋다. 그리고 도움이 될 지 모르겠지만 내장 tuple 함수를 사용하여 문자열을 문자들의tuple로 변경할 수도 있다. list를 문자열로 변경하려면 "".join을 사용한다.
이 방식은 신규 string object들을 생성하고 제거하는데 overhead를 발생시키므로 과용하지 않아야 한다. 백이나 천 단위로 이 방식을 사용하여 문자열 처리를 하면 program 상에 큰 영향을 미치지 않지만 백만 단위가 된다면 문제가 될 수 있다.
유용한 method들과 상수들
string object들은 문자열이 숫자나 alphabet으로 된 문자들로 구성되는지, 모두 대문자인지 소문자인지, 등등을 평가할 수 있는 유용한 몇몇 method들도 가지고 있다.
>>> x = "123" >>> x.isdigit() True >>> x.isalpha() False >>> x = "M" >>> x.islower() False >>> x.isupper() True |
사용 가능한 모든 string method들의 전체 list는 공식 Python documentation에서 string section을 참조하라.
마지막으로 string module은 유용한 몇 가지 상수들을 정의하고 있다. string.whitespace는 해당system에서 Python이 공백으로 취급하는 문자들로 구성된 문자열이라는 것은 이미 알고 있을 것이다. string.digits는 문자열 ‘0123456789’이며, string.hexdigits는 ‘0123456789abcdefABCDEF’ 문자열이며 16진수를 표현하는데 사용된다. string.octdigits는 ‘01234567’ 문자열이며 8진수를 표현하는데 사용된다. string.lowercase는 모든 소문자 alphabet 문자를 포함하고 있으며 string.uppercase는 모든 대문자 alphabet 문자를 포함하고 있다. 당신은 이 상수들을 할당하여 이 language의 동작을 변경하려고 할 수도 있다. Python은 당신이 이런 시도를 할 수 있게 허락하지만 이는 좋은 생각이 아니라는 것을 명심하라.
문자열은 문자들의 순서열(sequence)이라는 것을 기억하기 바란다. 이 특성으로 인해 (일반적으로 기존 string method들이 더 단순하고 쉽지만) Python의 in 연산자를 이용하여 문자열에서 문자가 존재유무를 쉽게 확인하는 것도 가능하다.
가장 일반적인 string 연산자들을 다음 표에 정리하였다.
표) string 연산자들
string 연산자 | 설명 | 예제 |
+ | 두 문자열을 합친다. | x = "hello " + "world" |
* | 문자열을 복제한다. | x= " " * 20 |
upper | 문자열을 대문자로 변경한다. | x.upper() |
lower | 문자열을 소문자로 변경한다. | x.lower() |
title | 문자열 내의 모든 단어의 첫 문자를 대문자로 변경한다. | x.title() |
find, index | 문자열 내에서 argument의 위치를 검색한다. | x.find(y) x.index(y) |
rfind, rindex | 문자열의 끝에서 부터argument 위치를 검색한다. | x.rfind(y) x.rindex(y) |
startswith, endswith | 문자열이 argument로 시작 또는 끝나는 지 검사한다. | x.startswith(y) x.endswith(y) |
replace | 문자열을 첫 번째 argument를 두 번째 argument로 변경한다. | x.replace(y, z) |
strip, rstrip, lstrip | 문자열의 끝부분에서 공백이나 지정 문자들을 제거한다. | x.strip() |
encode | Unicode 문자열을 bytes object로 변경한다. | x.encode("utf_8") |
이 method들은 문자열 자체를 변경하지 않지만 문자열 위치나 신규 문자열을 반환한다는 것을 명심하라.
object에서 string으로 변경
Python에서는 repr 내장 함수를 사용하여 거의 모든 것을 문자열 표현의 종류로 변경할 수 있다. 다음은 친숙한 복합 Python data type인 list를 string으로 표현을 변경하는 예제이다.
>>> repr([1, 2, 3]) '[1, 2, 3]' >>> x = [1] >>> x.append(2) >>> x.append([3, 4]) >>> 'the list x is' + repr(x) 'the list x is[1, 2, [3, 4]]' |
위의 예제에서는 list x를 repr을 사용하여 문자열 표현으로 변경하면서 다른 문자열과 연결하였다. repr을 사용하지 않으면 위의 예제는 동작하지 않을 것이다. "string" + [1, 2] + 3와 같은 표현식에서문자열, list, 혹은 숫자를 더하려고 시도해본 적이 있는가? Python은 이 상황에서 당신이 원하는 바를 인식하지 못하고 추정하는 것이 아니라 안전한 조치(error 발생)를 할 것이다. 위의 예제에서는 모든 항목들이 문자열 표현으로 변경된 후에 문자열 연결이 진행된다.
list는 우리가 잘 알고 있는 복합 Python object이지만 repr은 거의 대부분의 Python object의 몇 가지의 문자열 표현을 취득하는데 사용될 수 있다. 실제로 Python 함수인 내장 복합 object에 repr을 수행하면 다음과 같이 출력되는 것을 볼 수 있다.
>>> repr(len) '<built-in function len>' |
Python은 len 함수를 구현하는 code를 포함하는 문자열을 생성하지는 못하지만 최소한 이 함수가 무엇인지를 설명하는 “<built-in function len>” 문자열을 반환은 해준다. 당신이 repr 함수의 이 특징을 인지하고 Python의 각 data type(dictionary, tuple, class 등)에 이를 시험해본다면 어떤 type이라고 해도 해당 object에 관련된 어떤 문자열을 볼 수 있을 것이다.
이는 debugging program에는 매우 유용하다. 만약 당신의 program 내의 특정 변수의 진행 내용이 알고 싶다면 repr를 사용하여 해당 변수의 contents를 출력할 수 있다.
Python은 이를 두 가지 다른 방식으로 object를 해당 object를 설명하는 문자열로 변경할 수 있다. repr 함수는 언제나 이를 Python object의 정형 문자열 표현(formal string representation)이라는 형태로 반환한다. 더 명확하게 말하면 repr은 원본 object를 rebuild할 수 있는 Python object의 문자열 표현을 반환한다. 넓게 보면 복합 object는 debugging 출력이나 status report들을 볼 수 있는 종류가 아니라고 할 수 있다.
Python은 str 내장 함수도 제공한다. repr과 비교해서 str은 출력할 수 있는 문자열 표현을 생성하도록 설계되었으며 어떤 Python object에도 적용이 가능하다. str은 해당 object의 비정형 문자열 표현(informal string representation)이라는 형태를 반환한다. str에 의해서 반환되는 문자열은 object를 완전히 정의할 필요가 없으며 Python code가 아니라 인간이 읽을 수 있도록 설계되었다.
당신이 처음 repr과 str을 사용한다면 이들의 차이를 알 수 없을 것이다. Python의 객체 지향(object-oriented) 기능들을 사용해야지 비로소 이들의 차이점을 알 수 있기 때문이다. 내장 Python object에 적용하는 str은 결과값을 계산하기 위해서 항상 repr을 호출한다. 당신이 자신의 class들을 정의하기 시작할 때만 str과 repr의 차이점이 중요해진다. 이는 추후 다루도록 하겠다.
repr를 사용하여 background에서 일어나는 과정을 알아보고 쉽게 debugging을 위한 print 함수를 작성이 가능하다는 것을 알려주려는 목적에서 여기에서 설명을 했었다. good style을 위해서는 정보를 표시하는 문자열을 생성할 때 repr보다 str을 사용하는 습관을 들이는 것이 좋다.
format method 사용
Python 3에서는 두 가지 방식으로 문자열의 format을 정할 수 있다. Python에서 문자열의 format을 정하는 새로운 방식은 string class의 format method를 사용하는 것이다. format method는 {}로 표시된 교체 field를 포함하는 형태의 문자열과 format 명령어 내에 주어진 parameter들로 교체할 값들을 결합한 형태로 사용된다. 만약 {과 }을 문자 그대로 사용해야 한다면 {{과 }}처럼 2중으로 사용해야 한다. format 명령어는 강력한 string-formatting mini-language이며 문자열 formatting을 조작할 수 있는 많은 기능들을 제공한다. 하지만 일반적으로는 꽤나 단순하게 사용할 수 있다. 여기에서는 몇 가지 기본 pattern들을 살펴볼 것이다. 만약 당신이 고급 option들을 사용하려고 한다면 standard library documentation의 string-formatting section을 참조할 수 있다.
format method와 위치와 관련된 parameter들
format method의 가장 단순한 사용법은 숫자로 표시된 교환 field들을 사용하는 것이다. 이 교환field들은 format 함수의 각각의 parameter들에서 값을 전달 받는다.
>>> "{0} is the {1} of {2}".format("Ambrosia", "food", "the gods") 'Ambrosia is the food of the gods' >>> "{{Ambrosia}} is the {0} of {1}".format("food", "the gods") '{Ambrosia} is the food of the gods' |
format method는 문자열 format에 적용된다. {} 문자들을 이중으로 사용하면 escape 되므로 교환field를 저정할 필요가 없다.
위의 예제는 세 개의 교환 field {0}, {1}, {2}를 사용하여 첫 번째, 두 번째, 세 번째 parameter의 값을 반환한다. 문자열 형식에서 첫 번째 parameter의 값으로 교환되는 {0}의 위치는 어디든지 상관 없다.
format method에서 위치와 관련된 parameter들도 사용할 수 있다.
format method와 이름으로 된 parameter들
format method는 이름으로 된 parameter들과 교환 field들도 인식이 가능하다.
>>> "{food} is the food of {user}".format(food="Ambrosia", user="the gods") 'Ambrosia is the food of the gods' |
위의 예제에서는 교환 field와 format 명령어의 교환 parameter의 이름이 일치하는 값이 선택되어 반환된다.
위치, 이름으로 된 parameter들을 동시에 사용할 수도 있으며, 이들 parameter들의 속성들과 항목들에도 접근이 가능하다.
>>> "{0} is the food of {user[1]}".format("Ambrosia", user=["men", "the gods", "others"]) 'Ambrosia is the food of the gods' |
위의 예제에서는 첫 parameter는 위치로 되어있으며 두 번째 parameter user[1]는 user라는 이름으로 된 parameter의 두 번째 항목을 참조한다.
형식 지정자(format specifier)
형식 지정자(format specifier)는 기존 style 문자열 형식의 formatting sequence들보다 더 강력하게 제어되도록 결과를 지정할 수 있게 해준다. 형식 지정자(format specifier)는 교환 field가 대체될 때 채워지는 문자, 할당, 기호, 너비, 정밀도, data type을 제어할 수 있게 해준다. 위에서 지적한 바와 같이 형식 지정자(format specifier)의 문법은 독립적인 mini-language이며 여기에서 전체를 다루기에는 너무 복잡하다. 하지만 다음 예제는 유용하게 사용할 수 있는 개념을 알려줄 수 있을 것이다.
>>> "{0:10} is the food of gods".format("Ambrosia") 'Ambrosia is the food of gods' >>> "{0:{1}} is the food of gods".format("Ambrosia", 10) 'Ambrosia is the food of gods' >>> "{food:{width}} is the food of gods".format(food="Ambrosia", width=10) 'Ambrosia is the food of gods' >>> "{0:>10} is the food of gods".format("Ambrosia") ' Ambrosia is the food of gods' >>> "{0:&>10} is the food of gods".format("Ambrosia") '&&Ambrosia is the food of gods' |
:10은 field를 10 칸의 넓이로 지정하고 교환 값의 나머지를 공백으로 채우도록 하는 형식 지정자(format specifier)이다. :{1}은 넓이를 두 번째 parameter로 받는다. :>10은 field를 오른쪽으로 정렬시키고 나머지 공간을 공백으로 채운다. :&>10은 오른쪽으로 정렬시키고 나머지 공간을 공백대신 &로채운다.
%로 문자열 formatting
여기에서는 string modulus 연산자(%)로 문자열을 formatting하는 것을 다루도록 하겠다. 이는Python 값들을 print나 다른 용도로 formatting 된 문자열로 결합하여 사용한다. C 사용자들은 printf종류의 함수들과 매우 유사하다는 것을 알 수 있을 것이다. 문자열 formatting에 %를 사용하는 것은 오래된 문자열 formatting style이다. 여기에서 이 방식을 다루는 것은 이전 version의 Python에서는 이 방식이 표준이었기 때문이다. 이전 version Python code에서는 이 방식으로 작성된 것을 볼 수 있을 것이다. 이 formatting style은 더 이상 사용되지 않고 앞으로는 이 language에서 제거될 예정이므로 새로운 code에서는 사용하지 않아야 할 것이다. 다음은 예제이다.
>>> "%s is the %s of %s" % ("Ambrosia", "food", "the gods") 'Ambrosia is the food of the gods' |
string modulus 연산자(%)는 왼쪽 편은 문자열이고 오른 편은 tuple인 두 part를 가진다. string modulus 연산자(%)는 왼쪽 문자열에서 특수한 formatting sequence들을 탐색하고 오른쪽의formatting sequence들의 값을 순서대로 대체한 신규 문자열을 생성한다. 위의 예제에서 왼쪽의formatting sequence들은 “문자열을 여기에 붙이도록 하는 세 개의 %s instance들만 사용하였다.
오른쪽에 다른 값을 전달하면 다른 문자열을 생성한다.
>>> "%s is the %s of %s" % ("Nectar", "drink", "gods") 'Nectar is the drink of gods' >>> "%s is the %s of the %s" % ("Brussels Sprouts", "food", "foolish") 'Brussels Sprouts is the food of the foolish' |
오른쪽의 tuple 구성원들은 %s를 통해 자동적으로 str을 적용받게 되므로 문자열로 변경해줄 필요는 없다.
>>> x = [1, 2, "three"] >>> "The %s contains: %s" % ("list", x) "The list contains: [1, 2, 'three']" |
formatting sequence들의 사용
모든 formatting sequence들은 중앙의 %에서 왼쪽 편의 문자열 내에 포함된 부분 문자열(substring)들이다. 각 formatting sequence는 % 문자로 시작되며 뒤에 formatting sequence가 무엇으로 대치되면서 어떤 식으로 대치되는 지를 지정하는 하나이상의 문자가 붙는다. 이전에 사용된 %s formatting sequence는 가장 단순한 formatting sequence로서 중앙의 % 오른쪽의 tuple에서 해당되는 문자열을 %s가 위치한 곳으로 대체한다는 것을 나타낸다.
다른 formatting sequence들은 더 복잡할 수도 있다. 다음 예제에서는 field 폭을 6자로 출력하며, 십진수 소숫점을 두 자릿수까지 나타내고, 왼쪽으로 정렬하도록 지정하고 있다. 꺽쇠괄호로 해당 field를 둘러싸서 여분의 공백이 잘 표시되도록 처리하였다.
>>> "Pi is <%-6.2f>" % 3.14159 # formatting sequence: %–6.2f를 사용. 'Pi is <3.14 >' |
formatting sequence 내에서 사용할 수 있는 모든 option들은documentation(http://docs.python.org/2/library/stdtypes.html)을 참조하기 바란다. 몇 가지option들이 존재하지만 사용법이 특별하게 다른 것은 없다. Python에서 대화형 prompt에서 당신이formatting sequence로 표시하려는 방식을 언제나 시험이 가능하다는 것을 기억해두기 바란다.
이름으로 된 parameter들과 formatting sequence들
마지막으로 특정 상황에서 % 연산자로 유용하게 사용할 수 있는 추가 기능을 다루겠다. 여기에서는 아직 설명되지 않았지만 (다른 language들에서는 일반적으로 hashtable이나 연관 배열이라고 부르는) Python 기능 dictionary를 사용하여 설명을 할 것이다.
formatting sequence는 이름으로 대체할 수 있게 지정이 가능하다. 이를 위해서는 각 formatting sequence가 % 다음에 오는 괄호 내에 이름을 지정하면 된다.
"%(pi).2f" # 괄호 내의 이름에 주의 |
추가적으로 중앙의 % 연산자에서 오른쪽의 argument는 단일 값이나 tuple이 아니라 dictionary를 사용하여 이름이 일치하는 key에 해당하는 값이 출력되게 해줘야 한다. 위의 formatting sequence를string modulus 연산자와 같이 사용하여 다음과 같은 code를 작성할 수 있다.
>>> num_dict = {'e': 2.718, 'pi': 3.14159} >>> print("%(pi).2f - %(pi).4f - %(e).2f" % num_dict) 3.14 - 3.1416 - 2.72 |
이 기능을 사용하면 더 이상 문자열 format 내에서 formatting sequence들을 오른쪽의 tuple이나 항목들의 위치가 올바르게 사용되는지 신경쓰지 않아도 되므로, 많은 숫자의 대체를 수행하는 문자열formatting을 사용할 때 특히 유용하다. dict argument 내에서 정의되는 항목들의 순서는 상관이 없으며, template 문자열은 dict의 값들을 한 번 이상 사용할 수 있다. 위의 예제에서는 pi 항목이 여러 번 사용되었다.
print 함수의 option들을 사용하면 단순 text 출력을 충분히 제어할 수 있지만 더 복잡한 상황에서는format method를 사용하는 것이 가장 좋다.
Bytes
bytes object는 string object와 유사하지만 중대한 차이점이 있다. string은 Unicode character들의 수정 불가능한 순서열인데 반해 byte object는 0에서 256의 값을 가진 정수의 순서열이라는 점이다. bytes는 예를 들면 binary data file을 읽는 것과 같은 binary data 처리 시에 사용될 수 있다.
기억해두어야 할 핵심은 bytes object는 문자열처럼 보이지만 정확하게 문자열과 동일하게 사용될 수 없으며 문자열과 합칠 수도 없다는 것이다.
>>> unicode_a_with_acute = '\N{LATIN SMALL LETTER A WITH ACUTE}' >>> unicode_a_with_acute 'á' >>> xb = unicode_a_with_acute.encode() >>> xb b'\xc3\xa1' >>> xb += 'A' Traceback (most recent call last): File "<pyshell#35>", line 1, in <module> xb += 'A' TypeError: can't concat bytes to str >>> xb.decode() 'á' |
위의 예제에서 첫 번째로 string의 encode method를 호출하여 일반 문자열(Unicode)을 bytes로 변경하는 것을 볼 수 있다. bytes object로 encode되면 해당 문자는 2 byte의 binary data로 변경되며 이제까지 문자열을 출력하는 방식과 동일하게 출력이 되지 않는다. 또한 bytes object에 string object를 더하려고 하면 type error를 발생 시킨다. 이는 두 object들이 호환될 수 없는 type들이기 때문이다.마지막으로 bytes object의 decode method를 호출하여 bytes object를 string으로 변경하였다.
대부분 Unicode나 bytes에 대해서 고민할 필요는 전혀 없을 것이다. 하지만 다국어 character set들을 다루려고 한다면 점점 더 일반적인 issue가 될 것이므로 정규 문자열과 bytes의 차이점에 대해서 반드시 이해가 필요할 것이다.
요약
Python의 string type은 text 처리를 위해 강력한 몇 가지 tool들을 제공한다. 이 tool들의 거의 다string object에 덧붙여서 사용되는 method들이다. 또한 re module 내의 tool들의 set보다 더 강력한tool들도 존재한다. 표준 string method들은 검색, 교환, 여분의 문자들을 잘라내기, 변경 등을 수행할 수 있다. 문자열은 수정될 수 없으므로, 문자열을 변경하는 연산은 원본 문자열은 변경되지 않고 복제하여 변경된 결과를 반환한다.