06-4 ~ 07-1 (p.278 ~ p.292)

 

어제부터 시작한 6장을 모두 끝내고 7장의 '정규표현식'을 배우기 시작했는데, 필자의 말로는 정규표현식은 현직자들도 잘 모를 수 있는 고급 주제라서 가벼운 마음으로 읽어달라고 했다. 하지만 책에도 소개되어 있고 이해하면 매우 유용하게 사용할 수 있다고 하니 어렵더라도 최대한 이해하려고 노력해봐야겠다!

 

유용한데 안배우는건 못참지 ㅋㅋ

 


 

06-4 간단한 메모장 만들기

 

문제 : 원하는 메모를 파일에 저장하고 추가 및 조회가 가능한 메모장을 만들어라.

 

필요한 기능은? : 메모 추가하기, 메모 조회하기
입력받는 값은? : 메모 내용, 프로그램 실행 옵션

출력하는 값은? : memo.txt

1. 우선 다음과 같이 입력으로 받은 옵션과 메모를 출력하는 코드를 작성한다.

# C:/doit/memo.py
import sys

option = sys.argv[1] # sys.argv : 프로그램을 실행할 때 입력된 값을 읽어 들임
                     # sys.argv[0]은 프로그램 이름인 memo.py이므로 [1]부터 시작
memo = sys.argv[2]

print(option)
print(memo)

 

2. memo.py를 작성했다면 다음 명령을 수행해 본다.

# cmd
C:\doit>python memo.py -a "Life is too short"
-a
Life is too short

 

3. 이제 입력으로 받은 메모를 파일에 쓰도록 코드를 변경해 본다.

# C:/doit/memo.py
import sys

option = sys.argv[1]

if option == '-a': # 옵션이 -a인 경우에만 memo 값을 읽고 memo.txt 파일에 그 값을 씀
    memo = sys.argv[2]
    f = open('memo.txt', 'a')
    f.write(memo)
    f.write('\n')
    f.close()

 

4. 이제 다음과 같은 명령을 수행해 본다.

# cmd
C:\doit>python memo.py -a "Life is too short"
C:\doit>python memo.py -a "You need python"

# 결과
C:\doit>type memo.txt
Life is too short
You need python

 

5. 이번에는 작성한 메모를 출력하는 부분을 만들 차례이다.

# C:/doit/memo.py
import sys

option = sys.argv[1]

if option == '-a':
    memo = sys.argv[2]
    f = open('memo.txt', 'a')
    f.write(memo)
    f.write('\n')
    f.close()
elif option == '-v': # -v : 메모 출력 옵션
    f = open('memo.txt')
    memo = f.read()
    f.close()
    print(memo)
# 결과
C:\doit>python memo.py -v
Life is too short
You need python

 

06-5 탭을 4개의 공백으로 바꾸기

 

문제 : 문서 파일을 읽어서 그 문서 파일 안에 있는 탭(tab)을 공백(space) 4개로 바꾸어 주는 스크립트를 작성해라.

 

필요한 기능은? : 문서 파일 읽어 들이기, 문자열 변경하기
입력받는 값은? : 탭을 포함한 문서 파일

출력하는 값은? : 탭이 공백으로 수정된 문서 파일
# 다음과 같은 형식으로 프로그램 수행
python tabto4.py src dst
# src : 탭을 포함하고 있는 원본 파일 이름
# dst : 파일 안의 탭을 공백 4개로 변환한 결과를 저장할 파일 이름

1. 우선 다음과 같이 tabto4.py 파일을 작성한다.

# C:/doit/tabto4.py
# sys.argv를 사용하여 입력값을 확인하도록 한 코드
import sys

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

print(src)
print(dst)

 

2. 다음과 같이 수행했을 입력값이 정상적으로 출력되는지 확인해 본다.

# cmd
C:\doit>python tabto4.py a.txt b.txt
a.txt
b.txt

 

3. 테스트를 위한 원본 파일(탭을 포함하는 파일)인 a.txt를 다음과 같이 작성한다. 각 단어는 탭(\t) 문자로 분리되도록 입력해야 한다.

Life    is         too         short
You    need    python

 

4. 테스트를 위한 원본 파일(탭을 포함하는 파일)인 a.txt를 다음과 같이 작성한다. 각 단어는 탭(\t) 문자로 분리되도록 입력해야 한다.

# C:/doit/tabto4.py
import sys

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

f = open(src)
tab_content = f.read()
f.close()

space_content = tab_content.replace("\t", " "*4) # replace 함수를 사용해서 탭을 4개의 공백으로 변경
print(space_content)

 

5. tabto4.py를 위와 같이 변경한 후 다음과 같은 명령을 수행해 본다.

# cmd
C:\doit>python tabto4.py a.txt b.txt
Life  is  too  short
You   need   python
# 탭 문자가 공백 4개로 변경되어 출력

 

6. 이제 변경된 내용을 b.txt. 파일에 저장할 수 있도록 다음과 같이 프로그램을 변경해 본다.

# C:/doit/tabto4.py
import sys

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

f = open(src)
tab_content = f.read()
f.close()
space_content = tab_content.replace("\t", " "*4)

f = open(dst, 'w') # 탭이 공백으로 변경된 space_content를 출력 파일인 dst에 쓰도록 함
f.write(space_content)
f.close()

 

7. 프로그램을 실행하기 위해 다음 명령을 수행한다.

# cmd
C:\doit>python tabto4.py a.txt b.txt
# 결과 : C:\doit 디렉터리에 b.txt 파일 생성

 

06-6 하위 디렉터리 검색하기

 

문제 : 특정 디렉터리부터 시작해서 그 하위 모든 파일 중 파이썬 파일(*.py)만 출력해 주는 프로그램을 만들어라.

 

1. 다음과 같이 sub_dir_search.py 파일을 작성해 본다.

# C:/doit/sub_dir_search.py

def search(dirname):
    print(dirname)
    
search("C:/")
# search 함수를 만들고 시작 디렉터리를 입력받도록 코드 작성

 

2. 이제 이 디렉터리에 있는 파일을 검색할 수 있도록 소스를 변경해 본다.

# C:/doit/sub_dir_search.py
import os

def search(dirname):
    filenames = os.listdir(dirname) # os.listdir : 디렉터리에 있는 파일들의 리스트 구해줌
    for filename in filenames:
        full_filname = os.path.join(dirname, filename) # 디렉터리를 포함한 전체 경로를 구해주는 함수
        print(full_filename)
        
search("C:/")

# 결과 : C:/ 디렉터리에 있는 파일 목록 출력

 

3. 이제 C:/ 디렉터리에 있는 파일들 중 확장자가 .py인 파일만을 출력하도록 코드를 변경해 본다.

# C:/doit/sub_dir_search.py
import os

def search(dirname):
    filenames = os.listdir(dirname)
    for filename in filenames:
        full_filname = os.path.join(dirname, filename)
        ext = os.path.splitext(full_filname)[-1] # 파일 이름에서 확장자만 추출하기 위해 함수 사용
                                                 # 파일 이름을 확장자를 기준으로 두 부분으로 나눔
        if ext == '.py':
            print(full_filname)
        
search("C:/")

 

4. 하지만 우리가 원하는 것은 C:/디렉터리 바로 밑에 있는 파일뿐만 아니라 그 하위 디렉터리(sub directory)를 포함한 모든 파이썬 파일을 검색하는 것이다. 하위 디렉터리도 검색이 가능하도록 다음과 같이 코드를 변경해야 한다.

# C:/doit/sub_dir_search.py
import os

def search(dirname):
    try: # os.listdir를 수행할 때 권한이 없는 디렉터리에 접근하더라도 프로그램이 오류로 종료되지 않고 그냥 수행토록 하기 위해 사용
        filenames = os.listdir(dirname)
        for filename in filenames:
            full_filname = os.path.join(dirname, filename)
            if os.path.isdir(full_filname): # full_filename이 디렉터리인지 파일인지 구별하기 위해 함수 사용
                search(full_filname) # 디렉터리일 경우 해당 경로를 입력받아 다시 search 함수 호출(재귀 호출)
            else:
                ext = os.path.splitext(full_filname)[-1]
                if ext == '.py':
                    print(full_filname)
    except PermissionError:
        pass
        
search("C:/")

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

import os

for (path, dir, files) in os.walk("C:/"):
    for filename in files:
        ext = os.path.splitext(filename)[-1]
        if ext == '.py':
            print("%s/%s" % (path, filename))
# os.walk : 시작 디렉터리부터 시작하여 그 하위 모든 디렉터리를 차례대로 방문하게 해주는 함수

 

 

7장 : 정규 표현식

 

7장에서는 정규 표현식을 배운다.

 

07-1 정규 표현식 살펴보기

 

정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용한다. 정규 표현식을 배우는 것은 파이썬을 배우는 것과는 또 다른 영역이다.

 

정규 표현식은 왜 필요한가?

주민등록번호를 포함하고 있는 텍스트가 있다. 이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해 보자.

이 문제를 정규식을 모른다면 다음과 같은 순서로 프로그램을 작성해야 할 것이다.

1. 전체 텍스트를 공백 문자로 나눈다(split).
2. 나뉜 단어가 주민등록번호 형식인지 조사한다.
3. 단어가 주민등록번호 형식이라면 뒷자리를 *로 변환한다.
4. 나뉜 단어를 다시 조립한다.

이를 구현한 코드는 다음과 같다.

data = """
park 800905-1049118
kim  700905-1059119
"""

result = []
for line in data.split("\n"):
    word_result = []
    for word in line.split(" "):
        if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
            word = word[:6] + "-" + "*******"
        word_result.append(word)
    result.append(" ".join(word_result))
print("\n".join(result))

# 결과
park 800905-*******
kin 700905-*******

하지만 정규식을 사용하면 다음처럼 훨씬 간편하고 직관적으로 코드를 작성할 수 있다.

import re # 정규 표현식을 사용하기 위한 모듈

data = """
park 800905-1049118
kim  799905-1059119
"""

pat = re.compile("(\d{6})[-]\d{7}")
print(pat.sub("\g<1>-*******", data))

# 결과
park 800905-*******
kin 700905-*******

* 정규 표현식을 사용하면 간단한 예제에서도 코드가 상당히 간결해진다. 만약 찾으려는 문자열 또는 바꾸어야 할 문자열의 규칙이 매우 복잡하다면 정규식의 효용은 더 커지게 된다.

 

+ Recent posts