오늘도 강의가 쉽지 않았습니다.
처음에는 파이썬 버전별 변동사항을 보면서 어떻게 변해왔는지 역사이야기 듣는 것처럼
재밌게 잘 들었습니다.
파일입출력도 파일에 여러가지 써보기도 하고, 데이터 추출해서 뿌려보기도 하고, 눈에 보이는게
있으니까 재밌었습니다.
심지어 비동기와 코루틴 배울때도 하나도 모르지만 "그래 순차적으로 하지 않고, 동시에 여러 작업 하는거야"
여러 쓰레드로 처리 하는건가 보다. 나름 개념이라도 이해하고 넘어갔습니다.
그런데 정규표현식은? 보다보니 알긴 알겠는데 이걸 데이터 보고 원하는대로 하려면 정말 어렵겠더라고요.
과제도 쉽게 내주셔서 이걸 나중에 잘 쓸 수 있을까? 라는 걱정부터 들더군요.
심난한 강의였습니다.
파일입출력
# 파일은 열었으면 꼭 닫으셔야 합니다.
# 닫지 않으면 메모리가 살아있습니다!
f = open('python.txt', 'w') # 파일모드 : r, w, a, r+,
f.write('hello world')
f.close()
f.close()
를 무조건 해줘야 합니다. 닫지 않으면 메모리가 살아있습니다.
그래서 따로 처리해주지 않더라도 사용할 수 있는 코드를 추천해주셨습니다.
사람 생각하는건 비슷한 구석이 있죠? 내가 불편하면 남도 불편하다.. 누군가는 불편했으니 만들었겠지요^^
with open('test.txt', 'w') as f:
f.write('hello world!')
db_data = [
{
"_id": "7f5440ab-7e2f-4a42-Be13-89987fa65b32",
"index": "1",
"name": "등윤상",
"email": "user-qe89s09@ut.io",
"phone": "010-3542-7711",
"country": "키리바시",
"address": "방학로 74-9",
"job": "일러스트레이터"
},
{
"_id": "b4f7ade0-38bb-4e6f-B94a-18af1426eb30",
"index": "2",
"name": "노동규",
"email": "user-nyr5mqw@ornare.com",
"phone": "010-9743-6320",
"country": "베냉",
"address": "남부순환로 54-6",
"job": "애완동물미용사"
},
{
"_id": "5d788255-8d5e-4f03-B446-dbac6a147e37",
"index": "3",
"name": "경진아",
"email": "user-n7pey4v@Etiam.biz",
"phone": "010-4049-8718",
"country": "아르메니아",
"address": "강남대로 87-5",
"job": "플로리스트"
},
{
"_id": "083efce4-17ed-4e9f-Ad7c-f9aa0ac6cb79",
"index": "4",
"name": "복소유",
"email": "user-ohaz2is@nibh.biz",
"phone": "010-3400-5945",
"country": "페루",
"address": "양산로 78-1",
"job": "응급구조사"
},
{
"_id": "d54ce0fa-c025-4af6-Cd9a-b9d10aa8be5a",
"index": "5",
"name": "화예빈",
"email": "user-mnkpdlp@vel.net",
"phone": "010-3120-8720",
"country": "허드 및 맥도널드 제도",
"address": "한남대로 6-2",
"job": "전문의사"
}
]
제너레이터로 만들어낸 가짜데이터입니다. 이걸로 여러가지 형태로 데이터 다루는 강의가 진행되었습니다.
# 미션 1
# 각각의 회원 이름_info.txt로 각각 정보 정리
# list(map(lambda x: x['name'], db_data)) # 이름을 list로 저장
for i, name in enumerate(map(lambda x: x['name'], db_data)):
with open(f'{name}_info.txt', 'w') as f:
f.write(str(db_data[i]))
db_data 에서 이름을 뽑아내서 for문 돌려서 파일이름을 '홍길동_info.txt' 형태로 만들고, 그 안에 개인정보가
들어가는 파일을 사람수 대로 만들어 냈습니다.
# 미션 2
# 엑셀로 각각 정보 정리
with open('data.csv', 'w') as f:
s = '아이디,인덱스,이름,이메일,휴대폰,나라,주소,직업\n'
for i in db_data:
s += f'{i["_id"]},{i["index"]},{i["name"]},{i["email"]},{i["phone"]},{i["country"]},{i["address"]},{i["job"]}\n'
f.write(s)
csv 파일로 데이터 만들었습니다. 셀 상단에 '아이디,인덱스,이름,이메일,휴대폰,나라,주소,직업' 써주고,
하단에 db_data 돌면서 해당하는 인덱스에 값을 넣어줍니다. i["_id"]
형태로 해당 객체의 값들을 문자열에 넣어줍니다.
csv 파일이라 ,
가 아주 중요합니다.
# 워크북(엑셀파일 1개입니다.)
# 워크시트(sheet이라고 아래 여러개의 sheet을 만들 수 있습니다.)
# 셀(네모 칸 하나)
!pip3 install xlsxwriter
import xlsxwriter
workbook = xlsxwriter.Workbook('hello.xlsx')
worksheet = workbook.add_worksheet('test')
철수 = [10, 20, 30]
영희 = [40, 50, 60]
worksheet.write('A1', '국')
worksheet.write('B1', '영')
worksheet.write('C1', '수')
worksheet.write('A2', 철수[0])
worksheet.write('B2', 철수[1])
worksheet.write('C2', 철수[2])
worksheet.write('A3', 영희[0])
worksheet.write('B3', 영희[1])
worksheet.write('C3', 영희[2])
for i in zip('ABC', 철수, 영희):
worksheet.write(f'{i[0]}4', i[1] + i[2])
# D1에 평균 텍스트 삽입
worksheet.write('D1', '평균')
# 수식 삽입
worksheet.write('D2', '=SUM(A2:C2)/3')
worksheet.write('D3', '=SUM(A3:C3)/3')
workbook.close()
실제 엑셀파일 다루는 것도 했습니다. xlsxwriter 처럼 엑셀파일 다루는 모듈을 알려주셨고,
request 나 bs4 처럼 기본 모듈은 아닌데 사용이 많은 것들을 알려주셨습니다.
파이썬의 다양한 라이브러리들은 장점이라고 생각합니다.
# 크롤링
# 웹크롤링 -> 엑셀
# 한글파일 크롤링 -> 엑셀
# PDF 크롤링 -> 엑셀
# 파일 형식 변환
# CSV => xlsx
# JSON => xlsx
# 메크로
# 메크로 작동 로직 => 엑셀
크롤링도 다양한 타입을 엑셀로 정리할 수 있다고 하셨습니다.
# 미션 3
# html로 table로 정리하기
s = '''
<style>
table {
border-collapse: collapse;
width: 100%;
border: 1px solid black;
}
tr, td, th {
border: 1px solid black;
}
</style>
<table>
<tr>
<th>아이디</th>
<th>인덱스</th>
<th>이름</th>
<th>이메일</th>
<th>휴대폰</th>
<th>나라</th>
<th>주소</th>
<th>직업</th>
</tr>
'''
for item in db_data:
s += f'''
<tr>
<td>{item["_id"]}</td>
<td>{item["index"]}</td>
<td>{item["name"]}</td>
<td>{item["email"]}</td>
<td>{item["phone"]}</td>
<td>{item["country"]}</td>
<td>{item["address"]}</td>
<td>{item["job"]}</td>
</tr>
'''
s += '</table>'
with open('test.html', 'w') as f:
f.write(s)
csv 파일에 정리하듯이 비슷하게 정리했습니다.
# 첫번째 방법 - 한줄씩
f = open('test.html', 'r')
while True:
line = f.readline()
if not line:
break
print(line, end='')
f.close()
# 두번째 방법 - 통채로
f = open('test.html', 'r')
data = f.read()
print(data)
f.close()
방금 저장한 test.html 파일을 읽어오는 코드입니다.
비트연산
실무에서 사용하는 경우는 거의 없다고 하지만 알고리즘 테스트에서 CS지식 테스트 하는 용도로 간혹 출제된다고 합니다.
진법은 수학 기초상식이니까요.
# 10진법
# 1542 == 1 * (10**3) + 5 * (10**2) + 4 * (10**1) + 2 * (10**0)
# 0123456789 10
# 16진법
# 1542 == 1 * (16**3) + 5 * (16**2) + 4 * (16**1) + 2 * (16**0)
# 0123456789abcdef 10
# 8진법
# 1542 == 1 * (8**3) + 5 * (8**2) + 4 * (8**1) + 2 * (8**0)
# 01234567 10
# 진법 변환 팁
# 10진 == 2진
# 1 == 1
# 2 == 10
# 4 == 100
# 8 == 1000
# 16 == 10000
# 9는? 8 + 1 => 1000 + 1 => 1001
# 15는? 16 - 1 => 10000 - 1 => 1111
진법에 대해 간단한 설명과 2진법 전환팁도 알려주셨습니다. 이렇게 생각하고 변환하니 잘되더군요.^^
# 3 & 9
# 0011
# 1001
# ----
# 0001 => 1
# 3 | 9
# 0011
# 1001
# ----
# 1011 => 11
and 연산, or 연산입니다.
# 매개변수 값
# n 5
# arr1 [9, 20, 28, 18, 11]
# arr2 [30, 1, 21, 17, 28]
# 출력 ["#####","# # #", "### #", "# ##", "#####"]
# 9 | 30
# bin(9 | 30)
# bin(9 | 30)[2:] # 앞에 0x 처럼 숫자를 표현하는 비트가 있어서 잘라줍니다.
# bin(9 | 30)[2:].replace('1', '#').replace('0', ' ')
arr1 = [9, 20, 28, 18, 11]
arr2 = [30, 1, 21, 17, 28]
for i, j in zip(arr1, arr2):
print(bin(i | j)[2:].replace('1', '#').replace('0', ' '))
bin(31) # 2진법 변환 '0b11111'
oct(31) # 8진법 변환 '0o37'
hex(31) # 16진법 변환 '0x1f'
int('0b11111', 2) # 2진수를 10진수로 변환
int('11111', 2) # 2진수를 10진수로 변환
진법 변환하는 방법입니다.
f-string 용법
# print에 들어가는 문자열 만들기
name = '홍길동'
age = 10
'제 이름은 %s이고 제 나이는 %d입니다.'%(name, age) # % 용법
'제 이름은 {}이고 제 나이는 {}입니다'.format(name, age) # format 용법(이 문법도 아직 많이 사용합니다.)
f'제 이름은 {name}이고 제 나이는 {age}입니다' # f-string 용법(권고합니다.)
문자열 만드는 방법은 여러가지가 있지만 f-string용법이 권고됩니다.
one_length = 5.343123123
print(f'{one_length} 입니다.')
print(f'{one_length:.1f} 입니다.')
print('{0:.2f}'.format(one_length))
print('{:.2f}'.format(one_length))
자주 사용하는 format용법 과 f-string용법의 숫자 표현입니다.
# 복습은 가운데 정렬만 외워주세요.
age = 100
print('당신의 나이는 [{:^15}]세'.format(22))
print(f'당신의 나이는 [{age:^15}]세')
print(f'당신의 나이는 [{age:<15}]세')
print(f'당신의 나이는 [{age:>15}]세')
print(f'당신의 나이는 [{age:=^15}]세')
print(f'당신의 나이는 [{age:#^15}]세')
# 이런식으로 하셔도 되니 부담되시면 잠시 우선순위는 뒤로 밀어두셔도 괜찮습니다.
print(f'###########{age}###########')
log 출력할 때 가운데 정렬은 많이 사용합니다. 출력 결과 재밌습니다.
log에서 많이 봤던것 같네요.^^
# 중괄호 3개는 1년에 5번정도 만나는 것 같습니다.
# 잘 모르시면 이걸 해결하는 것이 참 힘듭니다.
age = 10
print(f'My set is {{1, 2, 3}}.') # 출력: My set is {1, 2, 3}.
print(f'My set is {{age*2}}.') # 출력: My set is {age*2}.
print(f'My set is {{{age*2}}}.') # 출력: My set is {20}.
중괄호를 표현하고 싶다면 위와 같이 사용해야 합니다. 같은 것도 문자열로만 표현하는게 다르고,
계산하고, {}까지 표현하는게 다릅니다. 실수할만한 부분을 미리 말씀해주십니다.^^
나중에 코드 짜다가 강사님 얼굴이 딱 떠오르고 그때 들었었네 라고 생각할거 같습니다.
리스트컴프리헨션
# 1번
l = []
for i in range(2, 10):
for j in range(1, 10):
l.append(f'{i} X {j} = {i*j}')
l
# 2번
[f'{i} X {j} = {i*j}' for i in range(2, 10) for j in range(1, 10)] # 리스트 컴프리헨션 속도가 for 빠릅니다!
1번을 2번으로도 표현할 수 있습니다. 속도는 더 빠르다고 하네요.
메인코드를 앞으로 빼고, for문이 뒤로 갑니다. 가독성때문에 중첩for문은 사용하지 않는게 좋다고 하는데
2번까지는 괜찮을 거 같습니다. 배웠지만 다시 한번 나와서 정리해봤습니다.
정규표현식
# https://regexr.com/5nvc2
# 1번 일반 문자열 패턴
# hello world
# Hello
# ^hello
# hello$
# ^hello$
# .
# .....
# h[eao]llo
# .[eao]llo
# ^h[eao]llo$
# h[a-zA-Z0-9]llo
# h[a-zA-Z]llo
# 010-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]
# 010.[0-9][0-9][0-9][0-9].[0-9][0-9][0-9][0-9]
# 010[- .][0-9][0-9][0-9][0-9][- .][0-9][0-9][0-9][0-9]
# [^a-zA-Z0-9]: 나머지 문자열을 찾음 # ^는 대괄호 안에서는 '부정'
# _* : 앞에 있는 문자가 0개 ~ N개
# _+ : 앞에 있는 문자가 1개 ~ N개
# _? : 앞에 있는 문자가 0개 ~ 1개
# 010[- .]?[0-9][0-9][0-9][0-9][- .]?[0-9][0-9][0-9][0-9]
# {3} : 3개
# {3,} : 3개 이상
# {1,3} : 1개 ~ 3개
# 010[- .]?[0-9]{4}[- .]?[0-9]{4}
# _* : 앞에 있는 문자가 0개 ~ N개 ({0,})
# _+ : 앞에 있는 문자가 1개 ~ N개({1,})
# _? : 앞에 있는 문자가 0개 ~ 1개 ({0,1})
# /[0-9]{3}[-.* ][0-9]{4}[-.* ][0-9]{4}/gm
# 전화번호
# /[0-9a-zA-Z]{2,3}[-.* ][0-9]{3,4}[-.* ][0-9]{4}/gm
# /[0-9]{2,3}[-.* ][0-9]{3,4}[-.* ][0-9]{4}/gm
# 이메일
# /[0-9a-zA-Z]+@[0-9a-zA-Z]+.[a-zA-Z]+/gm
# ^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$
# .....@----....
# /\w/gm : 워드 # 많이 사용
# /\w{5} /gm : 5개의 글자와 스페이스 하나
# /\W/gm : not 워드 # 많이 사용
# /\d/gm : 숫자 # 많이 사용
# /\D/gm : not 숫자
# /\s/gm : 스페이스
# /\S/gm : not 스페이스
# ^\d{3}[- .]?\d{3,4}[- .]?\d{4}$
# /\[.*]/gm : 대괄호([]) 안에 감싸여진 문자열
# /\(.*\)/gm : 소괄호 안에 감싸여진 문자열
# /\\.*\//gm : 이미 사용되고 있는 특수문자로 감싸여진 문자열
# /-.*-/gm : 이스케이프 문자를 사용할 필요가 없는 경우
# /\^\^/gm : 이스케이프 문자가 필요한 경우
# /:\)/gm : 이스케이프 문자가 필요한 경우
# \[.*]
# \[.*\], \[.*\]
핵심입니다. 일단 정규표현식에 대한 이해도가 높아야 할 것 같습니다.
# 사용되는 패턴
# 1
p = re.compile(r'([0-9]|10)([SDT])([\*\#]?)')
p.findall('1S2D*3T')
# 2
re.findall(r'([0-9]|10)([SDT])([\*\#]?)', '1S2D*3T')
# 자주 사용하는 메서드
# sub() : 매치된 부분을 치환 (str에 replace와 같은 역활) # 가장 많이 사용.
# findall() : 매치된 부분 모두 리스트 반환
이게 사실 더 중요하죠. 정규표현식으로 원하는 형태로 걸러내더라도 사실 치환하거나 데이터로 만들어서
처리해야할 일이 더 많지 않을까 싶습니다.
# 문제: 아래 문자열에서 모든 문자를 제거하고 숫자만 모두 더해주세요.
s = 'hello1928ljwlie123slie21e1j3'
import re
def solution(my_string):
return re.sub(r"[a-zA-Z]", "", my_string) # 이렇게 숫자만 문자열로 만들 수 있습니다.
solution(s) # 결과 : '19281232113'
이렇게 문자열에서 숫자만 뽑아내는게 첫번째 해야할 일입니다.
s = 'hello1928ljwlie123slie21e1j3'
import re
def solution(my_string):
return sum(map(int, list(re.sub(r"[a-zA-Z]", "", my_string))))
solution(s) # 결과 : 33
map() 을 이용해서 문자열리스트를 int형으로 바꾸고, 바로 더해줍니다.
list(re.findall(r'[0-9]', 'hello1928ljwlie123slie21e1j3'))
문자열에서 숫자만 골라서 리스트로 반환합니다.
결과는 ['1', '9', '2', '8', '1', '2', '3', '2', '1', '1', '3']
입니다.
그런데 중간에 1928 이라는 숫자와 123, 21 같은 숫자는 분리하지 않았으면 좋겠는데 어떻게 해야 할까요?
list(re.findall(r'[0-9]+', 'hello1928ljwlie123slie21e1j3'))
+
하나로 결과는 ['1928', '123', '21', '1', '3']
가 나옵니다.+
는 문자가 1개에서 n개라는 말입니다.
import re
my_string = "aAb1B2cC34oOp"
re.sub('[^1-9]', '', my_string) # 결과 : '1234'
[^1-9]
^는 부정의 의미입니다. 숫자가 아닌것을 찾아서 '' 빈문자열로 대체하라는 뜻입니다.
import re
my_string = "aAb1B2cC34oOp"
re.sub('\d', '', my_string) # 결과 : aAbBcCoOp
'\d'
는 숫자를 찾으라는 의미입니다. 숫자를 찾아서 '' 빈문자열로 대체하라는 뜻입니다.
결과는 문자만 남겠네요.
예제는 많지만 여기까지 강의시간에 했던 내용을 정리해봅니다.
정규표현식은 많이 하면 할수록 이해도가 높아질 것 같습니다.
문자열을 다루다보니 반복문이나 조건문을 통해서 파싱하려고 하는 것보다 슬라이싱과 정규표현식을
이용하면 충분히 원하는만큼 문자열을 다룰 수 있지 않을까 합니다.
오늘도.. 내일도.. 화이팅!! 입니다^^
'오름캠프(23.12.28 ~ 24.4.18)' 카테고리의 다른 글
오름캠프 학습일지(2024/01/22) - 5주차 1일 (0) | 2024.01.22 |
---|---|
오름캠프 학습일지(2024/01/19) - 4주차 5일 (0) | 2024.01.19 |
오름캠프 학습일지(2024/01/17) - 4주차 3일 (0) | 2024.01.17 |
오름캠프 학습일지(2024/01/16) - 4주차 2일 (0) | 2024.01.16 |
오름캠프 학습일지(2024/01/15) - 4주차 1일 (1) | 2024.01.15 |