문자열 조작하기(re 함수)

문자열 조작하기는 카카오 코테에서 level1,2에 단골 출제입니다.

알고리즘이나 자료구조가 어렵지 않고, 문자열 조작이 필요한 문제가 많이 나오는 것 같습니다.

카카오 문제는 문장이 길기 때문에 개인적으로 문제 이해 시간이 좀 걸려서, 경험상 level1, 2는 30분 안에는 풀어야 하는 것 같습니다.

re 내장함수 : 문자열에서 숫자만 추출

import re
re.sub(pattern, repl, string)  # string에서 pattern과 일치하는 문자를 repl로 교체
re.findall(pattern, string)    # string에서 pattern에 해당하는 내용을 리스트로 리턴
re.search(pattern, string)      # string에서 pattern 추출
re.split(pattern,string)

(1) 모든 숫자들을 문자열로

string = 'aaa1234, ^&*2233pp'
num = re.sub(r'[^0-9]','',string)
print(num)  # 12342233 <class 'str'>

(2) 연속된 숫자를 리스트로

string = 'aaa1234, ^&*2233pp'
num = re.findall(r'\d+', string)
print(num)  # ['1234', '2233']

string = 'the theme is other'
pattern = r'\bthe\b'
match = re.findall(pattern, string)
print(match)  # 출력: ['the']

(3) 패턴을 리스트로

arr = "1S2D*3T"
pattern = '(\d+)([SDT])([*#]?)'
re.findall(pattern, arr) # [('1', 'S', ''), ('2', 'D', '*'), ('3', 'T', '')]

(4) 패턴을 기준으로 잘라 리스트로

s = "img12.png"
parts = re.split('(\d+)', s)        # ['img', '12', '.png']

parts = re.split(r'\d+',s)          # ['img', '.png']

정규표현식을 사용해서 split 하면 해당 부분을 포함해서 리스트로 return 하고,

raw string으로 만들어주는 r 을 사용하면 해당 부분을 빼고 리스트로 return 한다.

패턴 설명 예제  
^ 이 패턴으로 시작해야 함 ^abc : abc로 시작해야 함 (abcd, abc12 등)  
$ 이 패턴으로 종료되어야 함 xyz$ : xyz로 종료되어야 함 (123xyz, strxyz 등)  
[문자들] 문자들 중에 하나이어야 함. 가능한 문자들의 집합을 정의함. [Pp]ython : “Python” 혹은 “python”  
[^문자들] [문자들]의 반대로 피해야할 문자들의 집합을 정의함. [^aeiou] : 소문자 모음이 아닌 문자들  
| 두 패턴 중 하나이어야 함 (OR 기능) a | b : a 또는 b 이어야 함  
? 앞 패턴이 없거나 하나이어야 함 (Optional 패턴을 정의할 때 사용) \d? : 숫자가 하나 있거나 없어야 함  
+ 앞 패턴이 하나 이상이어야 함 \d+ : 숫자가 하나 이상이어야 함  
* 앞 패턴이 0개 이상이어야 함 \d* : 숫자가 없거나 하나 이상이어야 함  
패턴{n} 앞 패턴이 n번 반복해서 나타나는 경우 \d{3} : 숫자가 3개 있어야 함  
패턴{n, m} 앞 패턴이 최소 n번, 최대 m 번 반복해서 나타나는 경우 (n 또는 m 은 생략 가능) \d{3,5} : 숫자가 3개, 4개 혹은 5개 있어야 함  
\d 숫자 0 ~ 9 \d\d\d : 0 ~ 9 범위의 숫자가 3개를 의미 (123, 000 등)  
\w 문자를 의미 \w\w\w : 문자가 3개를 의미 (xyz, ABC 등)  
\s 화이트 스페이스를 의미하는데, [\t\n\r\f] 와 동일 \s\s : 화이트 스페이스 문자 2개 의미 (\r\n, \t\t 등)  
. 뉴라인(\n) 을 제외한 모든 문자를 의미 .{3} : 문자 3개 (F15, 0x0 등)

(4) 문장 속에서 숫자 추출

string = "Request 12345 Finished."
pattern = "Request (\d+) Finished."
group = re.search(pattern, string)
print(group[1])  # 12345 <class 'str'>

공백 제거

(1) 모든 공백 제거

string.replace(' ','')
re.sub(r'\s','',string)

(2) 양쪽 공백 제거

s.strip()

(3) 왼쪽 혹은 오른쪽 공백 제거

s.lstrip()
s.rstrip()

문자열 리스트 비교하기

set의 &(교집합), | (합집합), -(차집합)을 통해 구할 수 있습니다.


문자열의 내장함수 이용

문자열의 숫자, 알파벳을 판별할 때 쓰는 함수입니다.

(1) isalpha()

알파벳으로만 구성되었는지 판별 (case insensitive)

s = 'daD+'
d = 'da '
s.isalpha()   # False
d.isalpha()   # False

(2) isdecimal(), isdigit(), isnumeric()

  • isdecimal ⊆ isdigit ⊆ isnumeric

  • isdecimal( ): 어떤 문자열이 int형으로 변환이 가능하면 True를 반환

  • isdigit( ): 어떤 문자열이 숫자의 형태면 True를 반환 (예: 3², ², ..)

  • isnumeric( ): 숫자값 표현에 해당하는 문자열이면 True를 반환 (예: 3², ², ½, …)

s = '234'
s.isdigit()     # Ture    

(3) isalnum()

주어진 문자열이 알파벳+숫자로만 구성되었는지 판별

s = 'se12'
s.isalnum()   # True

정렬

1. 숫자로 변환하여 정렬

a = ['2', '01', '1', '10']

a_sorted = sorted(a)            # ['01', '1', '10', '2']

# 문자열을 숫자로 변환하여 정렬
a_sorted = sorted(a, key=int)   # ['1', '01', '2', '10']

2. 2차배열의 기준 열을 기준으로 정렬

# 2차원 배열
arr = [['Apple', 'banana'], ['Cherry', 'apple'], ['Date', 'cherry'], ['banana', 'Date']]

# 각 행의 1열을 소문자로 변환한 후 그 결과를 기준으로 정렬
arr_sorted = sorted(arr, key=lambda x: x[1].lower()) 
# [['Cherry', 'apple'], ['Apple', 'banana'], ['Date', 'cherry'], ['banana', 'Date']]


코테 문제

1. 튜플 (level_2)

  • 문제 설명 : 문제의 input인 s가 표현하는 튜플을 배열에 담아 return

  • 풀이 : re.findall 함수로 연속된 숫자를 list로 받았습니다.

import re
def solution(s):
    dic = {}
    n = 1
    result = []
    
    s = s.split('},')
    for ss in s:
        ss = re.findall(r'\d+',ss)
        dic[len(ss)] = set(ss)
        n = max(n,len(ss))
        if len(ss) == 1:
            result.append(int(ss.pop()))
      
    for i in range(2,n+1):
        intersection = dic[i] - dic[i-1]
        result.append(int(intersection.pop()))
    return result  


2. 다트 게임 (level_1)

  • 문제 상황 : 3번의 다트 게임을 진행하는데, 보너스(S, D, T)와 옵션(*, #)을 고려하여 점수 합계를 출력합니다.

  • 풀이 : re.findall 함수로 pattern을 list로 받았습니다.

import re

def solution(dartResult):
    answer = []
    bonus_dic = {'S':1, 'D':2, 'T':3}
    pattern = '(\d+)([SDT])([*#]?)'
    dart = re.findall(pattern, dartResult)
    for i in range(3):
        answer.append(int(dart[i][0])**bonus_dic[dart[i][1]])
        if dart[i][2] == '*':
            answer[i] = answer[i]*2
            if i != 0:
                answer[i-1] = answer[i-1]*2
        elif dart[i][2] == '#':
            answer[i] = -answer[i]
    
    return sum(answer)
  • 모법 풀이 : option_point 까지 하나의 식으로 고려
import re

def solution(dartResult):
    bonus = {'S' : 1, 'D' : 2, 'T' : 3}
    option = {'' : 1, '*' : 2, '#' : -1}
    p = re.compile('(\d+)([SDT])([*#]?)')
    dart = p.findall(dartResult)
    for i in range(len(dart)):
        if dart[i][2] == '*' and i > 0:
            dart[i-1] *= 2
        dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]

    answer = sum(dart)
    return answer


3. 뉴스 클러스터링 (level_2)

isalpha() 함수와 collections.Count() 함수

  • 문제 상황 : 자카드 유사도 방법을 통해 두 기사 제목의 유사도를 판정하는 문제입니다. 각 기사 제목에서 두글자씩 다중집합의 원소로 생각하고, 두 다중집합간의 교집합 / 합집합 * 65536 값이 answer입니다.

  • 문제 접근 : collections.Count 함수를 통해 기사제목 글자가 중복되는 숫자를 dic로 정리합니다. 두 기사의 dic.keys()값을 합쳐서 교집합은 min(dic1[key], dic2[key])로 하고, 합집합은 두 기사의 리스트 길이를 합치고 교집합개수를 뺀 값으로 합니다.

  • 제한 사항 : 2 <= str1, str2 <= 1000

from collections import Counter
import math
import re

def solution(str1, str2):
    answer = 0
    n = len(str1)
    m = len(str2)
    input1 = []
    input2 = []
    
    input1 = [str1[i:i+2].casefold() for i in range(n-1) if str1[i:i+2].isalpha()]  #if not re.findall('[^a-zA-Z]+', str1[i:i+2])]
    dic1 = dict(Counter(input1))
    
    input2 = [str2[i:i+2].casefold() for i in range(m-1) if str2[i:i+2].isalpha()]
    dic2 = dict(Counter(input2))

    # 예외처리
    if not input1 and not input2:
        return 65536
    
    keys_set = set(list(dic1.keys()) + list(dic2.keys()))
    intersectN = 0
    for key in keys_set:
        if key in dic1 and key in dic2:
            intersectN += min(dic1[key], dic2[key])
    unionN = len(input1) + len(input2) - intersectN
    
    answer = intersectN / unionN
    return math.trunc(answer*65536)


str1 = "e=m*c^2"
str2 = "E=M*C^2"
print(solution(str1, str2))


re 라이브러리 설명 참고

Categories:

Updated:

Leave a comment