파이썬(문자열, 파일 다루기)

문자열 및 파일을 다뤄보자!


텍스트 데이터 문자열 저장

텍스트 데이터는 문자열(String)로 표현한다.
파이썬에서는 작은따옴표('), 큰따옴표(")로 묶어서 사용한다. 예제 코드는 아래와 같다.

1 my_str = 'Welcome!'
2 ur_str = "You're welcome."
3
4 print(my_str)
5 print(ur_str)

인코딩과 디코딩

문자열 데이터가 메모리에 저장될 때 컴퓨터 내부에서 0과 1로 변환되어 다뤄지게 된다. 이런 이진 데이터의 최소 단위는 비트(bit)이고, 비트가 8개 모이면 바이트(byte)가 된다. 이때 메모리에는 바이트로 저장이 된다.

개념 정리

  • 바이트(byte) : 컴퓨터의 기본 저장 단위
    • 1바이트(1byte)는 8비트(8bit)이다.
    • 1바이트에는 2의 8승 즉, 256개의 고유한 값을 저장할 수 있다.
  • 인코딩 (encoding) : 문자열을 바이트로 변환하는 과정
  • 디코딩 (decoding) : 바이트를 문자열로 변환하는 과정

텍스트 데이터의 처리 과정

텍스트 데이터는 문자열 데이터를 의미한다. 이런 문자열 데이터는 일종의 규약을 통해 인코딩이 진행된다. 이때 사용하는 표준 코드를 유니코드(Unicode)라고 한다.

좀 더 명확하게 공부하고 싶으면 아래의 링크 참조

파이썬에서도 내장함수인 ord()chr()을 이용해 유니코드를 확인할 수 있다.

  • ord() : 해당 문자에 대응하는 유니코드 숫자를 반환
  • chr() : 해당 유니코드 숫자에 대응하는 문자를 반환
1 print(ord('a'))    
2 print(ord('A'))
3 print(chr(97))
4 print(ord('가'))
5 print(chr(0xAC00)) 
/-------------------/
# 출력
97
65
a
44032

파이썬은 유니코드 규약에 따른다. 이로 인해 외부 데이터 및 데이터베이스로부터 데이터를 읽거나 보낼 때 인코딩 혹은 디코딩 과정을 거쳐야하고, 기본 설정되어있는 유니코드는 UTF-8이다.


문자열 다루기

1 s = 'I don't like Python!'
----------------------------
  File "/tmp/ipykernel_16/4145611764.py", line 1
    s = 'I don't like Python!'
               ^
SyntaxError: invalid syntax

위와 같이 문자열 내부에 작은따옴표(')나 큰따옴표("), 줄 바꿈등의 특수문자를 표현할 때 단순히 묶게되면 위의 코드와 같이 오류가 발생한다. 이런 오류를 방지할 수 있게 해주는 것이 바로 이스케이프 문자이다. 이스케이프 문자는 \[원하는 문자] 형태로 사용한다. 예제 코드는 아래와 같다.

1 #- 줄 바꿈
2 print("사회적\n거리두기")
3 print('--------------------------')
4
5 #- 탭(tab)
6 print("사회적\t거리두기")
7 print('--------------------------')
8
9 #- 작은따옴표 포함
10print('오늘부터 \'사회적 거리두기\'')
/-------------------------------------/
# 출력
사회적
거리두기
--------------------------
사회적	거리두기
--------------------------
오늘부터 '사회적 거리두기'

반대로 이스케이프 문자를 무시하고 싶을 때는 원시 문자열(raw string)을 사용한다. 문자열의 시작하는 따옴표 앞에 r을 붙여 사용이 가능하다.

1 print('Please don\'t touch it')
2 print(r'Please don\'t touch it')
/---------------------------------/
# 출력
Please don't touch it
Please don\'t touch it

내가 원하는 키워드로 시작하거 끝나는 데이터를 출력하고 싶을 때는 startswith('원하는 키워드'), endswith('원하는 키워드')를 사용할 수 있다.

1 EmployeeID = ['OB94382', 'OW34723', 'OB32308', 'OB83461', 'OB74830', 'OW37402', 'OW11235', 'OB82345'] 
Production_Employee = [P for P in EmployeeID if P.startswith('OB')]   # 'OB'로 시작하는 직원 ID를 다 찾는다.
Production_Employee
/------------------------------------------------------------------------------------------------------/
# 출력
['OB94382', 'OB32308', 'OB83461', 'OB74830', 'OB82345']
import os
image_dir_path = os.getenv("HOME") + "/data/pictures"   
# 각자의 사진이 보관된 디렉토리를 고르기
photo = os.listdir(image_dir_path )
png = [png for png in photo if png.endswith('.png')]
print(png)
/---------------------------------------------------/
# 출력
['image5.png', 'image2.png', 'image12.png', 'image3.png']

문자열에서 공백을 만들거나 없애는 방법이 있다.
우선 공백 생성에는 아래와 같은 방법이 있다.

  • 스페이스(space) : 한 칸 띄어쓰기
  • 탭(tab) \t : 네 칸 띄어쓰기. 때에 따라 두 칸 띄어쓰기로 표기되기도 한다.
  • 줄 바꿈(new line) : 줄 바꿈
  • 라인 피드 (line feed, 개행) \n : 줄 바꿈을 엄밀히 말하면 라인 피드라고 한다.
  • 캐리지 리턴 (carriage return, 복귀) \r : 커서를 맨 앞으로 이동시키는 것, 즉 커서를 원위치로 복귀(return)한다는 뜻이다. 커서를 맨 앞으로 보내고,\r 뒤에 문자가 있으면 그 문자를 출력한다.
1 print("사회적 거리두기")
2 print('--------------------------')
3 print("사회적\t거리두기")
4 print('--------------------------')
5 print("사회적\n거리두기")
6 print('--------------------------')
7 print("사회적\r거리두기")
/-----------------------------------/
# 출력
사회적 거리두기
--------------------------
사회적	거리두기
--------------------------
사회적
거리두기
--------------------------
거리두기

공백 제거에는 strip() 메서드를 사용해 처리한다.

1 txt = "      Strip white spaces.      "
2 print('[{}]'.format(txt))
3 print('--------------------------')
4
5 #- 양쪽 공백 제거 : strip()
6 print('[{}]'.format(txt.strip()))
7 print('--------------------------')
8
9 #- 왼쪽 공백 제거 : lstrip()
10 print('[{}]'.format(txt.lstrip()))
11 print('--------------------------')

12 #- 오른쪽 공백 제거 : rstrip()
13 print('[{}]'.format(txt.rstrip()))
/------------------------------------/
# 출력
[      Strip white spaces.      ]
--------------------------
[Strip white spaces.]
--------------------------
[Strip white spaces.      ]
--------------------------
[      Strip white spaces.]

대소문자 변환도 가능하다.

  • upper() : 모든 문자를 대문자로 변환
  • lower() : 모든 문자를 소문자로 변환
  • capitalize() : 첫 글자만 대문자로 변환
1 #- 모든 문자를 대문자로 변환 : upper()
2 txt = "I fell into AIFFEL"
3 txt.upper()
4
5 #- 모든 문자를 소문자로 변환 : lower()
6 txt.lower()
7
8 #- 첫 글자만 대문자로 변환 : capitalize()
9 txt.capitalize()
/--------------------------------------/
# 출력
'I FELL INTO AIFFEL'
'i fell into aiffel'
'I fell into aiffel'

문자열의 구성에 따라 boolean값을 반환할 수 있다.

  • isupper() : 문자열이 모두 대문자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • islower() : 문자열이 모두 소문자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • istitle() : 문자열의 첫 글자만 대문자로 되어 있으면 True, 그렇지 않으면 False를 반환
  • isalpha() : 문자열이 모두 알파벳 문자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • isalnum() : 문자열이 모두 알파벳 문자와 숫자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • isdecimal() : 문자열이 모두 숫자로만 되어 있으면 True, 그렇지 않으면 False를 반환
1 print("aiffel".isupper())
2 print("aiffel".islower())
3 print("PYTHON".istitle())
4 print("python101".isalpha())
5 print("python101".isalnum())
6 print("101".isdecimal())
/----------------------------/
# 출력
False
True
False
False
True
True

시퀀스 객체를 하나의 문자열로 합칠 수 있다. 반대로 문자열을 시퀀스 객체로 나눌 수 있다.

  • join() : tuple, list, string 등 반복 가능한(iterable) 객체를 받는 메서드, 각각의 원소를 모아 하나의 문자열로 합쳐 준다.
  • split() : 하나의 문자열을 구분자를 기준으로 나누어 준다.
1 stages = ['fundamentals', 'exploration', 'goingdeeper']
2 ",".join(stages)
3 'fundamentals,exploration,goingdeeper'.split(',')
/-------------------------------------------------------/
# 출력
'fundamentals,exploration,goingdeeper'
['fundamentals', 'exploration', 'goingdeeper']

문자열 내 문자열을 수정할 수 있다.

  • replace() : replace(s1, s2) 형태로 문자열 내 문자열 s1을 s2로 바꾼다.
1 sent = 'I can do it!'
2 sent.replace('I', 'You')
/------------------------/
# 출력
'You can do it!'

정규 표현식

거의 모든 운영 체제가 어떤 특정한 단어를 검색할 때 자주 사용하는 단어 검색 기능 Ctrl+F을 지원하고 있다. 이러한 기능이 바로 정규 표현식(regular expression, regex)에 근거해 만들어진 기능이다.

1 import re
2 sent = 'I can do it!'
3 pattern = re.sub("I", "You", sent)
4 pattern
/----------------------------------/
# 출력
'You can do it!'

정규 표현식은 특정 규칙을 가진 문자열의 집합을 표현하는 형식 언어로, 찾고자 하는 문자열 패턴을 정의하고 기존 문자열과 일치하는지를 비교하여 문자열을 검색하거나 다른 문자열로 치환하는 데 사용된다.

정규 표현식을 사용하기 위해서는 re모듈을 import해서 사용한다. 정규 표현식의 사용법은 크게 2단계로 나눠진다.

1) 찾고자 하는 문자열의 패턴을 정의한다.
2) 정의된 패턴과 매칭되는 경우를 찾아 다양한 처리를 한다.

이 중 1)의 과정을 컴파일(compile)이라고 한다.

1 #1단계 :  "the"라는 패턴을 컴파일한 후 패턴 객체를 리턴 
2 pattern = re.compile("the")    
3
4 # 2단계 : 컴파일된 패턴 객체를 활용하여 다른 텍스트에서 검색을 수행
5 pattern.findall('of the people, for the people, by the people')
/----------------------------------------------------------------/
# 출력
['the', 'the', 'the']

정규 표현식의 메서드

  • search() : 일치하는 패턴 찾기 (일치 패턴이 있으면 MatchObject를 반환)
  • match() : search()와 비슷하지만, 처음부터 패턴이 검색 대상과 일치해야 한다.
  • findall() : 일치하는 모든 패턴 찾기 (모든 일치 패턴을 리스트에 담아서 반환)
  • split() : 패턴으로 나누기
  • sub() : 일치하는 패턴으로 대체하기

match(), search() 등이 리턴하는 MatchObject의 메서드

  • group() : 실제 결과에 해당하는 문자열을 반환

패턴

  • [ ] : 문자
  • - : 범위
  • . : 하나의 문자
  • ? : 0회 또는 1회 반복
  • * : 0회 이상 반복
  • + : 1회 이상 반복
  • {m, n} : m ~ n
  • \d : 숫자, [0-9]와 동일
  • \D : 비 숫자, [^0-9]와 동일
  • \w : 알파벳 문자 + 숫자 + _, [a-zA-Z0-9_]와 동일
  • \W : 비 알파벳 문자 + 비숫자, [^a-zA-Z0-9_]와 동일
  • \s : 공백 문자, [ \t\n\r\f\v]와 동일
  • \S : 비 공백 문자, [^ \t\n\r\f\v]와 동일
  • \b : 단어 경계
  • \B : 비 단어 경계
  • \t : 가로 탭(tab)
  • \v : 세로 탭(vertical tab)
  • \f : 폼 피드
  • \n : 라인 피드(개행문자)
  • \r : 캐리지 리턴(원시 문자열)
#- 연도(숫자)
1 text = """
2 The first season of America Premiere League  was played in 1993. 
3 The second season was played in 1995 in South Africa. 
4 Last season was played in 2019 and won by Chennai Super Kings (CSK).
5 CSK won the title in 2000 and 2002 as well.
6 Mumbai Indians (MI) has also won the title 3 times in 2013, 2015 and 2017.
7 """
8 pattern = re.compile("[1-2]\d\d\d")
9 pattern.findall(text)
/--------------------------------------------------------------------------/
# 출력
['1993', '1995', '2019', '2000', '2002', '2013', '2015', '2017']
1 #- 전화번호(숫자, 기호)
2 phonenumber = re.compile(r'\d{3}-\d{3}-\d{4}')
3 phone = phonenumber.search('This is my phone number 010-111-1111')
4 if phone:
5	print(phone.group())
6 print('------')
7 phone = phonenumber.match ('This is my phone number 010-111-1111')
8 if phone:
9	print(phone.group())
/------------------------------------------------------------------/
# 출력
010-111-1111
------
1 #- 이메일(알파벳, 숫자, 기호)
2 text = "My e-mail adress is [email protected], and [email protected]"
3 pattern = re.compile("[0-9a-zA-Z]+@[0-9a-z]+\.[0-9a-z]+")
4 pattern.findall(text)
/--------------------------------------------------------------------------/
# 출력
['[email protected]', '[email protected]']

파일과 디렉터리

지금까지는 문자열 데이터를 메모리에 저장했다. 이는 데이터의 로드 및 액세스 속도가 빠르다는 장점이 있지만, 전원이 공급이 안되면 데이터가 날라간다는 단점이 있다.

파일

데이터를 날리지 않기 위해 우리는 파일을 만들어 데이터를 저장한다. 파이썬에서 파일을 수정, 저장하기 위해서는 write를 사용하여 작업한다.

1 f = open("hello.txt","w") 
2 #- open(파일명, 파일모드)
3 #- 파일을 열고 파일 객체를 반환 
4 for i in range(10):
5    f.write("안녕")
6    #- write() 메서드로 '안녕'을 10번 쓴다.
7 f.close()
8 #- 작업이 끝나면 close() 메서드로 닫는다. *필수!
9 print("완료!")
/-------------------------------------------/
# 출력
완료!

반대로 저장된 파일을 읽기위해서는 read를 사용한다.

1 with open("hello.txt", "r") as f:
2	print(f.read())
/---------------------------------/
# 출력
안녕안녕안녕안녕안녕안녕안녕안녕안녕안녕
안녕하세요.
반갑습니다.
오랫만입니다.

파일 관련 메서드

  • f.read() : 파일을 읽는다.
  • f.readline() : 파일을 한 줄씩 읽는다.
  • f.readlines() : 파일 안의 모든 줄을 읽어 그 값을 리스트로 반환한다.
  • f.write(str) : 파일에 쓴다. 문자열 타입을 인자로 받는다.
  • f.writelines(str) : 파일에 인자를 한 줄씩 쓴다.
  • f.close() : 파일을 닫는다.
  • f.seek(offset) : 해당 파일의 위치(offset)를 찾아 파일의 커서를 옮긴다. 파일의 처음 위치는 0이다.
  • f.tell() : f.tell(): 현재 커서의 위치를 반환한다.

디렉터리

파일이 저장된 위치를 디렉터리라고 한다. 파일을 정상적으로 읽기 위해서는 어느 디렉터리에 위치하는지가 중요하다.
최상위 디렉터리를 루트 디렉터리(root directory)라고 한다.

  • Window 운영 체제 : C:\
  • Linux 계열 운영 체제 : /

리눅스의 파일 시스템