ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 08. 문자열 관리
    Python 2021. 1. 20. 16:36

    8.1 문자열 관리

    문자열 -> 순서가 있다.

    순서가 의미 있는 집합을 볼 것이다.

    • 첨자

    파이썬은 문자열을 기본 타입으로 지원하고, 문자열 조작 명령을 제공한다. 문자열은 메모리상에 개별 문자들이 일렬로 쭉 늘어선 형태로 저장된다.

    문자열을 구성하는 개별 문자를 읽을 때는 [] 괄호와 문자의 위치인 첨자(인덱스)를 적는다.

    첨자는 앞에서 셀 수도 있고 뒤에서 셀 수도 있다.

    앞에서 셀 때

    0 1 2 3 4 5
    p y t h o n
    -6 -5 -4 -3 -2 -1

    뒤에서 셀 때

    앞에서 셀 때는 0부터 시작하여 첫 문자가 [0]번이며 뒤로 갈 수록 1씩 증가한다.

    뒤에서 셀 때는 음수를 사용하려 제일 마지막 문자가 [-1]번이며 앞으로 갈 수록 1씩 감소한다.

    첨자는 반드시 문자열의 길이 범위 안이어야 한다. 문자열의 길이가 6인데 s[7]이나 s[-9]를 읽으면 범위에서 벗어났다는 에러 메시지가 출력된다.

    문자열도 넓게 컬렉션의 일종이기 때문에 for 문 사용이 가능하다.

    s = "python"
    for c in s:
        print(c, end = ",")

    문자를 순회하는 대신 문자열의 길이만큼 첨자로 순회하며 각 위치의 문자를 읽을 수도 있다.

    for i in range(len(s)):
        print(s[i], end = ",")

    파이썬의 문자열은 변경 불가능한(immutable) 자료형이기 때문에 한번 초기화하면 바꿀 수 없다.

    []로는 개별문자를 읽을 수 있지만 다른 문자를 대입하거나 삽입, 삭제할 수는 없다.

    s = "python"
    s[2] = "k" # error, 문자열 변경 불가능

    한번 초기화한 문자열을 변경할 수 없도록 한 이유는 편집을 금지하면 메모리를 훨씬 더 절약하고 속도도 빨라지기 때문이다.

    물론 가끔 문자열을 바꾸어야 하는 경우도 있는데 이럴 경우는 변경된 문자열을 새로 만드는 방식을 사용한다.

    • 슬라이스

    [] 괄호에 첨자를 하나만 적으면 해당 위치의 문자를 읽지만, 범위를 지정하면 부분 문자열을 추출한다. [] 괄호 안에 시작, 끝, 증가값을 지정하는데 range 함수와 구조가 같다.

    [begin:end:step]

    시작 위치는 포함되지만 끝 위치는 포함되지 않아 끝 위치 직전의 문자까지만 추출된다.

    step은 건너뛸 문자 수를 지정하는데 생략 시 1을 적용하여 모든 문자를 차례대로 읽는다.

    step을 -1을 지정하면 문자열을 거꾸로 읽기도 한다.

    시작 위치의 디폴트 값은 0이며, 끝 위치를 생략하면 문자열의 끝까지 추출한다.

    슬라이스의 리턴값은 새로운 문자열이다.

    s = "python"
    print(s[2:5])
    print(s[3:])
    print(s[:4])
    print(s[2:-2])
    print(s[:])  # 전체 출력
    print(s[::]) # 전체 출력
    출력 내용
    tho
    hon
    pyth
    th
    python
    python

    추출된 문자열은 원본과는 독립적인 새 문자열이다. 즉 원본은 건들이지 않는다.

    yoil = "월화수목금토일"
    print(yoil[::2])
    print(yoil[::-1])
    출력 내용
    월수금일
    일토금목수화월

    만일, 회문 판별을 하려면

    s[::-1] == s 를 사용하면 된다.

    8.2 문자열 메서드

    • 검색(시퀀스는 모두 지원하는 기능이다.)

    문자열은 수치형에 비해 복잡하고 길이도 가변적이기 때문에 연산자로 조작하기는 어렵다. 그래서 문자열을 관리하는 함수가 제공된다.

    문자열.find(str) # str 문자열을 찾아 인덱스를 반환, 없으면 -1 반환
    문자열.rfind(str) # 뒤부터 str 문자열을 찾아 인덱스 반환, 없으면 -1 반환
    문자열.index(str) # find()와 동일, 없으면 예외 발생
    문자열.count(str) # str 문자열이 몇 번 등장하는지 횟수 리턴
    s = "python programming"
    
    print(len(s))
    print(s.find('o'))
    print(s.rfind('o'))
    print(s.index('r'))
    print(s.count('s'))
    출력 화면
    18
    4
    9
    8
    0

    len 함수는 문자열의 길이를 조사하는데 인수로 문자열을 전달하면 문자의 개수를 리턴한다. 내장 함수이기 때문에 문자열뿐만 아니라 다른 컬렉션에도 사용 가능하다.

    문자열's 를 의미하는 .을 활용해 객체의 함수에 접근한다.

    메서드는 클래스에 소속된 함수이며 객체에 대해 특화된 작업을 수행한다.

    객체.메서드() 식으로 대상 객체가 메서드를 호출한다.

    내장함수와 객체 소속의 메서드는 호출 방식이 다르다.

    len(s)  # 내장 함수
    s.find('o')  # 메서드

    find와 rfind 메서드는 인수로 지정한 문자 혹은 부분 문자열의 위치를 조사하는 일은 똑같지만, rfind는 뒤에서 검색을 시작한다. 똑같은 문자를 찾아도 앞에서부터 찾을 때와 뒤에서부터 찾을 때의 검색 결과는 다르다. 만일 문자열 내에 찾고자 하는 문자가 하나밖에 없다면 큰 문제가 없지만 여러 개가 있을 경우 방향에 따라 찾는 위치가 다르다.

    ex) python programming -> find('o')는 4 리턴, rfind('o')는 9 리턴(공백도 한 공간 차지)

    index 메서드도 문자를 찾는다는 면에서 기능은 같지만 해당 문자가 없을 경우 예외를 발생시키므로 반드시 예외 처리 구문으로 감싸야 한다.

    • 조사

    특정 문자가 어디에 있는지는 관심이 없고 단순히 있는지 없는지만 알고 싶을 때는 in 구문을 사용한다. 함수가 아닌 키워드이기 때문에 "단어 in 문자열" 형식으로 사용한다.

    포함되어 있으면 True 리턴, 아니면 False 리턴한다.

    반대로 not in은 포함되어 있지 않은지 조사한다.

    단어 in 문자열 # 문자열에 단어가 포함되어 있는가
    단어 not in 문자열 # 문자열에 단어가 포함되어 있지 않는가
    s = "python programming"
    print('a' in s)
    print('z' in s)
    print('pro' in s)
    print('x' not in s)
    출력 결과
    True
    False
    True
    True

    startswith 메서드는 특정 문자열로 시작되는지 조사한다. 문자열 전체나 중간이 아닌 앞부분의 일부만 비교하는 점에서 find나 index와 다르다. 반대로 endswith 메서드는 특정 문자열로 끝나는지 뒷부분만 비교한다.

    문자열.startswith(str) # str로 시작하는 문자열인지
    문자열.endswith(str) # str로 끝나는 문자열인지
    name = "김한결"
    if name.startswith("김"):
        print("김씨입니다.")
    if name.startswith("한"):
        print("한씨입니다.")
    
    file = "picture.jpg"
    if file.endswith(".jpg"):
        print("그림 파일입니다.")

    확장자를 조사할 때는 주로 슬라이싱 혹은 endswith 메서드를 사용한다.

    함수 설명
    isalpha 모든 문자가 알파벳인지 조사한다.
    islower 모든 문자가 소문자인지 조사한다.
    isupper 모든 문자가 대문자인지 조사한다.
    isspace 모든 문자가 공백인지 조사한다.
    isalnum 모든 문자가 알파벳 또는 숫자인지 조사한다.
    isdecimal 모든 문자가 숫자인지 조사한다.
    isdigit 모든 문자가 숫자인지 조사한다.
    isnumeric 모든 문자가 숫자인지 조사한다.
    isidentifier 명칭으로 쓸 수 있는 문자로만 구성되어 있는지 조사한다.
    isprintable 인쇄 가능한 문자로만 구성되어 있는지 조사한다.

    isdigit()은 거듭제곱, 숫자 특수문자까지 True라고 허용하는 듯

    isnumeric() 또한 분수, 숫자의 특수문자까지 True라고 허용한다.

    isdecimal()은 int로 변환 가능한 문자만 True라고 허용한다. 즉, 특수문자, 거듭제곱으로 표현된 문자열 수는 False를 리턴한다.

    즉, int로 변환 가능한 문자열을 판별하기 위해선 isdecimal()을 이용하고, 그렇지 않은 특수문자, 루트 같은 경우

    의 문자열 또한 숫자로 이루어진 문자열이라고 판별하고 싶으면 isnumeric(), isdecimal()을 혼용해서 쓰면 될 것 같다.

    • 변경

    문자열불변객체이다. 즉, 부분 수정 불가하다. 다만 통째로 바꾸는 일은 가능하다.

    함수 설명
    lower 영문자를 전부 소문자로 바꾼다.
    upper 영문자를 전부 대문자로 바꾼다.
    swapcase 대소문자를 반대로 뒤집는다.
    capitalize 문장의 첫 글자만 대문자로 바꾼다.
    title 모든 단어의 처음을 대문자로 바꾼다.
    strip("제거원하는문자") 양쪽의 공백을 모두 제거한다, 제거 문자 있으면 제거 문자 제거
    lstrip("제거원하는문자") 왼쪽의 공백만 제거한다, 제거 문자 있으면 제거 문자 제거
    rstrip("제거원하는문자") 오른쪽의 공백만 제거한다, 제거 문자 있으면 제거 문자 제거
    s = "Good morning. my love KIM"
    
    print(s.lower()) # 전부 소문자로
    print(s.upper()) # 전부 대문자로
    print(s) # 메서드를 활용해서 문자열을 바꾸어도 리턴값을 원본을 바꿔서 리턴하는 것이 아니라 바뀐 새로운 값을 리턴하기 때문에 원본 문자열은 그대로이다.
    
    print(s.swapcase()) # 소문자 -> 대문자, 대문자 -> 소문자
    print(s.capitalize()) # 문장의 첫 글자만 대문자로
    print(s.title()) # 단어의 첫 글자만 대문자로 바꾼다. 단어의 첫 글자가 아니면 소문자로
    출력 화면
    good morning. my love kim
    GOOD MORNING. MY LOVE KIM
    Good morning. my love KIM
    gOOD MORNING. MY LOVE kim
    Good morning. my love kim
    Good Morning. My Love Kim

    만일 s의 모든 문자를 아예 바꾸고 싶다면 대입문을 쓰면 된다.

    s = s.lower()

    다른 문자열 변경 메서드도 문자열 자체를 건드리지 않고 변경된 새로운 문자열을 리턴한다는 점을 유의해야 한다.

    사용자로부터 문자열을 입력받거나 다른 문자열에서 분리해내면 앞뒤로 불필요한 공백이 들어가는 경우가 빈번하다. 의미 없는 공백을 제거하기 위해선 strip, rstrip, lstrip 메서드를 사용한다.

    s = "  angel  "
    print(s + "님")
    print(s.lstrip() + "님")
    print(s.rstrip() + "님")
    print(s.strip() + "님")
    출력 내용
      angel  님
    angel  님
      angel님
    angel님
    • 분할

    split 메서드는 구분자를 기준으로 문자열을 분할한다. 하나의 문자열이 여러 개의 부분 문자열로 쪼개져 리스트에 저장된다.

    ex) "a,b,c,10,20".split(',') -> [a, b, c, 10, 20]

    문자열.split(구분자) # 구분자의 디폴트 값은 공백이다.
    문자열.splitlines() # 개행 문자, 파일 구분자, 그룹 구분자 등을 기준으로 문자열 잘라 리스트로 만든다.
    결합문자열.join(문자열) # 지정한 문자열을 element 사이에 끼워 결합한다.
    s = "짜장 짬뽕 탕수육"
    print(s.split())
    
    s2 = "서울->대전->대구->부산"
    city = s2.split("->")
    print(city)
    for c in city:
        print(c, "찍고", end = " ")
    출력 내용
    ['짜장', '짬뽕', '탕수육']
    ['서울', '대전', '대구', '부산']
    서울 찍고 대전 찍고 대구 찍고 부산 찍고

    splitlines 메서드는 개행 문자나, 파일 구분자, 그룹 구분자 등을 기준으로 문자열을 잘라 리스트로 만든다. 주로 개행 코드를 기준으로 한 행씩 잘라내며 긴 문자를 각 행별로 쪼개 관리할 때 편하다.

    """...................\n

    .........................\n -> ["...............", "...................", "..................."]

    .................."""

    traveler = """강나루 건너서 \n구름에 달 가듯이 
    길은 외줄기 \n술 익는 마을마다
    타는 저녁놀 \n구름에 달 가듯이 가는 나그네"""
    poet = traveler.splitlines()
    for line in poet:
        print(line)
    출력 내용
    강나루 건너서 
    구름에 달 가듯이
    길은 외줄기
    술 익는 마을마다
    타는 저녁놀
    구름에 달 가듯이 가는 나그네

    개행 에스케이프 문자인 '\n' 또한 개행으로 인지해 나누어 리스트에 넣는다.

    join 메서드는 문자열의 각 문자 사이에 다른 문자열을 끼워넣는다.

    ",".join([10, 20, 30])

    [10, 20, 30] -> "10, 20, 30"

    s = "0_0"
    print(s.join("대한민국"))
    출력 내용
    대0_0한0_0민0_0국
    s2 = "서울->대전->대구->부산"
    city = s2.split("->")
    print(" 찍고 ".join(city))
    출력 내용
    서울 찍고 대전 찍고 대구 찍고 부산

    split 메서드로 문자열을 분할하고 join 메서드로 다시 합치되 중간중간에 " 찍고 " 문자열을 삽입했다.

    두 메서드로 문자열을 쪼개고 합치면 각 부분 문자열에 대한 개별적인 처리가 가능하고 구분자를 원하는 다른 것으로 쉽게 교체 가능하다.

    • 대체

    replace 메서드는 특정 문자열을 찾아 다른 문자열로 바꾼다. 첫 번째 인수로 검색할 문자열을 지정하고 두 번째 인수로 바꿀 문자열을 지정한다. 문자열 전체를 탐색해 원본 문자열을 전부 찾아 다른 문자열로 바꾼다.

    문자열.replace(기본문자열, 대체문자열 [, 바꿀 개수]) 
    문자열.center(폭숫자)
    문자열.ljust(폭숫자)
    문자열.rjust(폭숫자)
    s = "독도는 일본땅이다. 대마도는 일본땅이다."
    print(s)
    print(s.replace("일본", "한국"))
    출력 내용
    독도는 일본땅이다. 대마도는 일본땅이다.
    독도는 한국땅이다. 대마도는 한국땅이다.

    필요하다면 세 번째 인수로 바꿀 개수를 지정하여 앞쪽의 일부만 교체할 수도 있다.

    s = "독도는 일본땅이다. 대마도는 일본땅이다."
    print(s)
    print(s.replace("일본", "한국", 1))
    출력 내용
    독도는 일본땅이다. 대마도는 일본땅이다.
    독도는 한국땅이다. 대마도는 일본땅이다.

    center 메서드는 중앙 정렬, ljust 메서드는 왼쪽 정렬, rjust 메서드는 오른쪽 정렬한다.

    인수로 정렬할 폭을 지정한다.

    message = "안녕하세요."
    print(message.ljust(30))
    print(message.rjust(30))
    print(message.center(30))
    출력 내용
    안녕하세요.
                             안녕하세요.
                안녕하세요.

    ljust는 문자열을 왼쪽에 정렬하고, 문자폭은 30이 된다.

    rjust는 폭을 30 확보한 뒤 글자를 오른쪽에 배치한다.

    center는 공백을 양쪽에 균등하게 배분해 글자를 중앙에 배치한다. 좌우에 12개의 공백이 들어가고 문자열이 가운데 온다.("안녕하세요."가 6공백을 차지한다고 생각.)

    traveler = """강나루 건너서 \n\n구름에 달 가듯이 
    길은 외줄기 \n\n술 익는 마을마다
    타는 저녁놀 \n\n구름에 달 가듯이 가는 나그네"""
    poet = traveler.splitlines()
    for line in poet:
        print(line.center(30))
    출력 내용
               강나루 건너서
    
              구름에 달 가듯이
               길은 외줄기
    
              술 익는 마을마다
               타는 저녁놀
    
           구름에 달 가듯이 가는 나그네

    이외에도 encode, translate 등의 인수가 있다.

    8.3 포맷팅

    • 포맷팅

    문자열 안에 여러 가지 세부 정보를 포함하여 출력할 때는 + 연산자로 연결하나, 여러변 + 연산자를 사용하는 점과, 문자열이 아닌 값을 연결하기 전에 str 함수로 일일이 문자열을 변환해야 하는 점이 불편하다.

    print("궁금하면 ", price, "원")

    같이 콤마로 출력할 내용을 나열해도 되지만, 이 방법은 정보 사이에 공백이 들어가고 출력만 할 뿐 하나의 문자열을 만들어 내는 것은 아니다.

    포맷팅은 문자열 사이사이에 다른 정보를 삽입하여 조립하는 기법이다.

    문자열에 다음과 같은 표식을 넣고 뒤에 대응되는 변수를 밝히면 표식 자리에 정보가 삽입된다.

    표식 설명
    %d 정수
    %f 실수
    %s 문자열
    %c 문자 하나
    %h 16진수
    %o 8진수
    %% % 문자
    price = 500
    print("궁금하면 %d원" %price)
    출력 내용
    궁금하면 500원

    %d는 정수가 들어올 것이고 변환시킬 것이라는 내용을 알려주고, %price는 실제로 넣을 값을 의미한다.

    만일 서식이 두 개 이상이면 괄호로 묶어 나열한다. 괄호로 묶은 값의 집한은 튜플이다.

    month = 8
    day = 15
    anni = "광복절"
    print("%d월 %d일 %s이다" %(month, day, anni))
    출력 내용
    8월 15일 광복절이다

    포맷팅 기능이 없다면

    print(str(month) + "월" + str(day) + "일은" + anni + "이다")

    이런식으로 변환하고 나열해야 한다.

    서식은 이 자리에 어떤 정보가 들어간다는 것 뿐만 아니라 값을 어떻게 출력할 것인가도 지정한다.

    %와 기호 사이에 전체 폭과 정렬 방식, 유효 자리수 등의 상세한 지시 사항을 적어 넣는다.

    %[-]폭[.유효자리수]서식  # 자리를 잡아라
    value = 123
    print("###%d###" %value)
    print("###%5d###" %value)
    print("###%10d###" %value)
    print("###%1d###" %value)
    출력 내용
    ###123###
    ###  123###
    ###       123###
    ###123###

    %5d는 5만큼의 자리를 확보하고 오른쪽에 출력, %10d는 10 자리를 확보한다.

    price = [30, 13500, 2000]
    for p in price:
        print("가격:%d원" %p)
    for p in price:
        print("가격:%7d원" %p)
    for p in price:
        print("가격:%-7d원" %p)
    출력 내용
    가격:30원
    가격:13500원
    가격:2000원
    가격:     30원
    가격:  13500원
    가격:   2000원
    가격:30     원
    가격:13500  원
    가격:2000   원

    %-7d는 왼쪽부터 출력한다.

    pie = 3.14159265
    print("%10f" %pie) # %-10f로 하면 왼쪽 출력
    print("%10.8f" %pie)
    print("%10.5f" %pie)
    print("%10.2f" %pie)
    출력 내용
      3.141593
    3.14159265
       3.14159
          3.14

    실수는 폭 지정 외에 . 기호와 함께 유표 자리수를 밝혀 소수점 이하 얼마까지 표시할 것인지 정밀도를 지정한다.

    별다른 지정 없으면 소수점 이하 6자리까지 반올림해 표시한다.

    %10.2f는 10자리수를 확보하고, 오른쪽에 문자열을 출력하되 소수점 이하 2자리까지 출력한다.

    • 선형 포맷팅

    파이썬 2.6부터 새로운 문자열 포맷팅 방법을 지원한다.

    "{[:포맷문자열]}".format(값...)
    "{인덱스[:포맷문자열]}".format(값....)
    "{변수명[:포맷문자열]}".format(값....)
    name = "한결"
    age = 23
    height = 165.4
    print("이름:{}, 나이:{}, 키:{}".format(name, age, height))
    출력 내용
    이름:한결, 나이:23, 키:165.4

    위는 print("이름:{0}, 나이:{1}, 키:{2}".format(name, age, height))와 같다.

    {} 괄호의 순서와 변수의 순서가 같다면 번호를 매길 필요는 없다. 번호를 매기면 순서를 바꿔도 상관없다.

    name = "한결"
    age = 23
    height = 165.4
    print("이름:{0:s}, 나이:{1:d}, 키:{2:f}".format(name, age, height))
    출력 내용
    이름:한결, 나이:23, 키:165.400000
    name = "한결"
    age = 23
    height = 165.4
    print("이름:{0:10s}, 나이:{1:5d}, 키:{2:8.2f}".format(name, age, height))
    이름:한결        , 나이:   23, 키:  165.40
    name = "한결"
    age = 23
    height = 165.4
    print("이름:{0:^10s}, 나이:{1:>5d}, 키:{2:<8.2f}".format(name, age, height))
    print("이름:{0:$^10s}, 나이:{1:>05d}, 키:{2:!<8.2f}".format(name, age, height))
    이름:    한결    , 나이:   23, 키:165.40  
    이름:$$$$한결$$$$, 나이:00023, 키:165.40!!

    정렬을 지정할 때는 < 왼쪽, > 오른쪽, ^ 중앙 정렬 기호를 폭 앞에 붙인다.

    정렬 문자 이전에 채움 문자를 지정하면 공백 대신 채움 문자를 출력한다.

    print(f"이름:{name}, 나이:{age}, 키:{height:.2f}")

    하지만 이 포맷팅을 사용할 것이다.

    'Python' 카테고리의 다른 글

    10. 사전과 집합  (0) 2021.01.20
    09. 리스트와 튜플  (0) 2021.01.20
    07. 함수  (0) 2021.01.20
    06. 반복문  (0) 2021.01.20
    05. 조건문  (0) 2021.01.20

    댓글

Designed by Tistory.