220914 Day14

유예지·2022년 9월 14일

(5) 탭을 4개의 공백으로 바꾸기

-문서 파일을 읽어서 그 문서 파일 안에 있는 탭(tab)을 공백(space) 4개로 바꾼다
-필요한 기능은? 문서 파일 읽어들이기, 문자열 변경하기
-입력받는 값은? 탭을 포함한 문서 파일
-출력하는 값은? 탭이 공백으로 수정된 문서 파일

① 'a.txt' 문서 작성

Life is too short
You need python

② 코드 작성

>>> import sys

    src = sys.argv[1]
    dst = sys.argv[2]

    with open(src, 'r') as f:
        data = f.read()

    data_new = data.replace("\t", " "*4)

    with open(dst, 'w') as f:
        f.write(data_new)

③ 명령 프롬프터 실행 후, 'b.txt' 파일 열어 확인하기

C:\Users\admin>python tabto4.py a.txt b.txt

(6) 하위 디렉터리 검색하기

-특정 디렉터리에서 시작해서 하위 모든 파일 중 파이썬 파일('*.py')파일만 출력해 주는 프로그램 만들기

① search 함수를 만들고, 시작 디렉터리에 있는 파일을 검색할 수 있도록

-os.listdir() : 해당 디렉터리에 있는 파일들의 리스트를 구할 수 있다
-os.path.join() : 디렉터리와 파일 이름을 이어준다 -> 디렉터리를 포함한 전체 경로를 쉽게 구할 수 있다

>>> import os
>>> os.listdir()
>>> os.listdir("./Downloads")

>>> dirname = "Downloads"
	filenames = os.listdir(dirname)

>>> def search(dirname):
        filenames = os.listdir(dirname)
        for filename in filenames:
            full_filename = os.path.join(dirname, filename)
            print(full_filename)
            
>>> search("Downloads")

② "Downloads" 디렉터리에 있는 파일들 중 '.py' 파일만 출력하도록

-os.path.splitext() : 파일 이름에서 확장자만 추출 -> 파일 이름을 확장자를 기준으로 두 부분으로 나누어 준다
-os.path.splitext(full_filename)[-1] : 해당 파일의 확장자 이름

>>> import os

>>> os.path.splitext("Downloads\ChromeSetup.exe")
('Downloads\\ChromeSetup', '.exe')

>>> def search(dirname):
        filenames = os.listdir(dirname)
        for filename in filenames:
            full_filename = os.path.join(dirname, filename)
            ext = os.path.splitext(full_filename)[-3]
            if ext == '.py':
            	print(full_filename)

>>> search("Downloads")

③ "Downloads" 디렉터리 바로 밑에 있는 파일 뿐만 아니라 그 하위 디렉터리(sub directory)까지도 검색이 가능하도록

>>> import os

>>> def search(dirname):
		try:
            filenames = os.listdir(dirname)
            for filename in filenames:
                full_filename = os.path.join(dirname, filename)
                if os.path.isdir(full_filename):  
                	search(full_filename)
                else:
                    ext = os.path.splitext(full_filename)[-3]
                    if ext = '.py':
                        print(full_filename)
        except PermissionError:
        	pass

>>> search("Downloads")

-os.path.isdir(full_filename) : full_filename 이 디렉터리 인가, 파일인가?
-search(full_filename) : 디렉터리일 경우 해당 경로를 입력받아 '다시 search 함수 호출 (재귀호출)' -> 해당 디렉터리의 하위 파일을 다시 검색하기 시작하므로 결국 모든 파일들을 검색할 수 있게 된다

-'try ... except PermissionError' 로 함수 전체를 감싼 이유는 os.listdir 를 수행할 때 권한이 없는 디렉터리에 접근하더라도 프로그램이 오류로 종료되지 않고 계속 수행되도록 하기 위함이다

* 하위 디렉터리 검색을 쉽게 해주는 os.walk

-시작 디렉터리부터 시작하여 그 하위 모든 디렉터리를 차례대로 방문하게 해준다
-os.walk 를 사용하면 위의 코드를 보다 간편하게 만들 수 있다

>>> import os

>>> for (path, dir, files) in os.walk("Downloads"):
		for filename in files:
        	ext = os.path.splitext(filename)[-1]
            if ext == '.py':
            	print("%s/%s" % (path, filename))

14. 정규 표현식 (Regular Expression)

-복잡한 문자열을 처리할 때 사용하는 기법

(1) 메타 문자

① [ ] : 문자 클래스 (character class)

-문자 클래스로 만들어진 정규식은 '[] 사이의 문자들과 매치' 라는 의미를 갖는다


  • [abc] : a 또는 b 또는 c 중 '한 개'의 문자와 매치
    [a-c] : [abc]와 동일
    [a-z] : 모든 소문자 알파벳 ([A-Z], [a-zA-Z] ..)
    [0-5] : [012345] ([0-9] ..)

  • '^' 표시
    [ ] 안에서 사용 -> 'not(반대)'의 의미
    [ ] 밖에서 사용 -> '시작'의 의미

  • 자주 사용하는 문자 클래스
    \d : '숫자'와 매치, [0-9]와 동일한 표현
    \D : '숫자'가 아닌 것과 매치, [^0-9]와 동일한 표현
    \s : whitespace 문자(공백을 표현하는 문자-tab, space)와 매치, [ \t\n\r\f\v] 와 동일한 표현(맨 앞의 빈칸은 공백(space)를 의미), " " 로 표현할 수도 있음
    \S : whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v] 와 동일한 표현
    \w : alphanumeric(문자+숫자)와 매치, [a-zA-Z0-9_]와 동일한 표현
    \W : alphanumeric(문자+숫자)가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현

② Dot(.)

-줄바꿈 문자 \n을 제외한 모든 문자와 매치됨

  • a.b : 'a + 모든 문자 (1개) + b'
    a와 b 사이에 줄바꿈 문자를 제외한 어떤 문자가 들어가도 모두 매치
    '.'의 개수에 따라 매치할 수 있는 문자의 수도 달라진다

  • a[.]b : 'a + . + b'
    [.] : 문자 '.' 그대로를 의미

③ * : 0번 이상 반복

-ca*t : * 문자 바로 앞에 있는 a가 0번 이상 반복되면 매치
-* 의 바로 앞에 있는 문자 a가 0부터 무한대로 반복될 수 있다는 의미
ct : 'a'가 0번 반복 -> 매치
cat : 'a'가 0번 이상(1번) 반복 -> 매치
caaat : 'a'가 0번 이상(3번) 반복 -> 매치

④ + : 1번 이상 반복

-ca+t : + 문자 바로 앞에 있는 a가 1번 이상 반복되면 매치
ct : 'a'가 0번 반복 -> 매치X
cat : 'a'가 1번 이상 반복 -> 매치
caaat : 'a'가 1번 이상(3번) 반복 -> 매치

⑤ {m,n}, ?

  • {m}
    ca{2}t : a가 2번 반복되면 매치

  • {m, n}
    ca{2, 5}t : a가 2번~5번 반복되면 매치 (2번 이상, 5번 이하)

  • ?
    '.?' : {0, 1}과 동일
    ab?c : b가 0~1번 반복되면 매치
    문자가 있거나 없거나 둘다 매치되는 경우

(2) 문자열 바꾸기

참고 사이트 : https://regexr.com/ , https://regex101.com/
sub 메서드를 사용하면 정규식과 매치되는 부분을 다른 문자로 쉽게 바꿀 수 있다

  • re.sub(패턴, 바꿀 문장, 무엇에 대하여)

① blue -> AAA 바꾸기

>>> sentence = "blue socks and red shoes"

>>> import re

#re.sub(패턴, 바꿀 문장, 무엇에 대하여)
>>> re.sub("blue", "AAA", sentence)
'AAA socks and red shoes'

>>> result = re.sub("blue", "AAA", sentence)
    result = re.sub("red", "BBB", result)
    result
'AAA socks and BBB shoes'

>>> re.sub("blue|red", "AAA", sentence)   #| : 'or'의 의미
'AAA socks and BBB shoes'

>>> re.sub("b.{3}", "AAA", sentence)
'AAA socks and red shoes'
#"b.{3}" : b 뒤에 어떤 문자든 3번 반복되는 것
#'.' -> 모든 문자, {3} -> {} 앞의 문자 3번 반복

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
	for string in li:
    	print(string, end = ",  ")

#1개의 숫자를 찾아서 A로 바꿔라
>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[0-9]", "A", string), end = ",  ")   #[0-9] : 모든 숫자
,  A,  AA,  AAA,  AAAA,  abc,  Aa,  #숫자 하나하나를 찾아서 A로 바꾸었다

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[0-9]{4}", "A", string), end = ",  ")
,  1,  12,  123,  A,  abc,  1a,  
#하나하나의 숫자가 4번 반복, 즉 숫자 4개가 있는 것이 A로 바뀜

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[0-9]+", "A", string), end = ",  ")
,  A,  A,  A,  A,  abc,  Aa,  
#하나하나의 숫자가 1번 이상 반복, 즉 숫자가 1개 이상 있는 것이 A로 바뀜

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[0-9]?", "A", string), end = ",  ")
A,  AA,  AAA,  AAAA,  AAAAA,  AaAbAcA,  AAaA,          
#'?' : 1번 이하(0~1번) 반복 -> 숫자가 없는 자리까지 A로 바뀌었다
#숫자 앞에는 보이지 않는 공간이 있어 그 자리도 A로 바뀌었고,
#숫자 뒤의 공간은 바로 사라져버리지만, 문자 뒤에는 보이지 않는 공간이 남아있어 그 자리도 A로 바뀌었다

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[0-9]*", "A", string), end = ",  ")
A,  AA,  AA,  AA,  AA,  AaAbAcA,  AAaA,

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[^0-9]", "A", string), end = ",  ")
,  1,  12,  123,  1234,  AAA,  1A,  
#숫자를 제외한 문자만 A로 바뀌었다

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("^[0-9]", "A", string), end = ",  ")
,  A,  A2,  A23,  A234,  abc,  Aa,          
#[]바깥에 '^' -> '시작'이 숫자면 A로 바꿔라

>>> li = ["", "1", "12", "123", "1234", "abc", "1a"]
    for string in li:
        print(re.sub("[0-9]$", "A", string), end = ",  ")
,  A,  1A,  12A,  123A,  abc,  1a,          
#[]바깥에 '$' -> 숫자 1개로 끝나는 것만 A로 바꿔라

  • a로 시작하고 o로 끝나는 문자에서 a와 o사이에 2개의 문자가 있는 경우 : a..o, [a]..[o]
>>> li = ["a12o", "abco", "aBo", "a123o", "kbs", "123kbs", "KBS", "KB3", "abcKBS123"]
	for string in li:
   		print(re.sub("a..o", "#", string), end = ",  ")
#,  #,  aBo,  a123o,  kbs,  123kbs,  KBS,  KB3,  abcKBS123,

>>> for string in li:
        print(re.sub("a.{2}o", "#", string), end = ",  ")

>>> for string in li:
        print(re.sub("^a.{2}o$", "#", string), end = ",  ")
  • 알파벳 대문자로 시작하고 숫자로 끝나는 경우 : ^[A-Z].+[0-9]$
>>> li = ["a12o", "abco", "aBo", "a123o", "kbs", "123kbs", "KBS", "KB3", "abcKBS123"]
    for string in li:
        print(re.sub("^[A-Z].+[0-9]$", "#", string), end = ",  ")
a12o,  abco,  aBo,  a123o,  kbs,  123kbs,  KBS,  #,  abcKBS123,  

>>> for string in li:
    	print(re.sub("^[A-Z]\S[0-9]$", "#", string), end = ",  ")
        
>>> for string in li:
    	print(re.sub("^[a-z].+[0-9]$", "#", string), end = ",  ")        
a12o,  abco,  aBo,  a123o,  kbs,  123kbs,  KBS,  KB3,  #,  
  • '123'을 찾아서 바꾸기
>>> li = ["a12o", "abco", "aBo", "a123o", "kbs", "123kbs", "KBS", "KB3", "abcKBS123"]
    for string in li:
        print(re.sub("123+", "#", string), end = ",  ")
a12o,  abco,  aBo,  a#o,  kbs,  #kbs,  KBS,  KB3,  abcKBS#,          
#[123]은 '1 또는 2 또는 3' 을 의미

>>> for string in li:
        print(re.sub("123", "#", string), end = ",  ")
  • 문자들 중간에 있는 '123' 을 찾아서 바꾸기
>>> li = ["a12o", "abco", "aBo", "a123o", "kbs", "123kbs", "KBS", "KB3", "abcKBS123"]
    for string in li:
        print(re.sub(".+123.+", "#", string), end = ",  ")
a12o,  abco,  aBo,  #,  kbs,  123kbs,  KBS,  KB3,  abcKBS123,  

>>> for string in li:
        print(re.sub("\S+123\S+", "#", string), end = ",  ")
        
>>> for string in li:
        print(re.sub("\D+123\D+", "#", string), end = ",  ")        
  • 주민번호 뒷자리 바꾸기
>>> string = "park 800905-1049118 Kim 700905-1059119"
    print(re.sub("\d{7}", "*******", string))
#모든 숫자가 7번 반복, 즉 7개가 있는 부분은 주민번호의 앞부분이 아니라 뒷부분에만 해당됨  
>>> print(re.sub("-\d{7}", "-*******", string))

>>> print(re.sub(r'1.{6}','*******',string))

0개의 댓글