[F-05] 텍스트 다루기

인코딩과 디코딩

  • 인코딩(encoding): 문자열을 바이트로 변환하는 과정
  • 디코딩(decoding): 바이트를 문자열로 변환하는 과정

유니코드(Unicode): 전 세계 문자를 모두 표시할 수 있는 표준코드

파이썬 내장함수 ord()와 chr()

  • ord(): 해당 문자에 대응하는 유니코드 숫자를 반환합니다.
  • chr(): 해당 유니코드 숫자에 대응하는 문자를 반환합니다.
#- 파이썬 2 -#
#- string, unicode string으로 구분됩니다.
str1 = b'hello'
str2 = 'hello'
str3 = u'hello'
print(type(str1), type(str2), type(str3))
#결과 : <type 'str'>, <type 'str'>, <type 'unicode'>


#- 파이썬 3-#
#- bytes와 string으로 구분됩니다.
str1 = b'hello'
str2 = 'hello'
str3 = u'hello'
print(type(str1), type(str2), type(str3))
# 결과 : <class 'bytes'>,  <class 'str'>,  <class 'str'>
  • 파이썬 2에서는 인코딩한 후에도 아스키(ascii) → 유니코드(unicode) 변환 등의 작업을 거쳐야 했던 반면에,
  • 파이썬 3부터는 문자열이 무조건 유니코드로 인코딩되므로 해당 텍스트가 인코딩되어 있는지 혹은 디코딩되어 있는지만 고려하면 된다는 것이 포인트입니다.

문자열 다루기

startswith

EmployeeID = ['OB94382', 'OW34723', 'OB32308', 'OB83461', 
                                  'OB74830', 'OW37402', 'OW11235', 'OB82345'] 
# 'OB'로 시작하는 직원 ID를 다 찾아봅니다
Production_Employee = [P for P in EmployeeID if P.startswith('OB')]
Production_Employee

endswith

>>> import os
>>> photo = os.listdir('/Users/gseung/Documents/photo')
>>> png = [png for png in photo if png.endswith('.png')]
>>> print(png)
metaflow.png
Pytorch.png
smartphone.png

공백 문자 처리: trimming

여러 가지 공백 문자

  • 스페이스(space) : 한 칸 띄어쓰기
  • 탭(tab) \t : 네 칸 띄어쓰기. 때에 따라 두 칸 띄어쓰기로 표기되기도 합니다.
  • 줄 바꿈(new line) : 줄 바꿈
  • 라인 피드 (line feed) \n : 줄 바꿈을 엄밀히 말하면 라인 피드라고 합니다.
  • 복귀 (carriage return) \r : 커서를 맨 앞으로 이동시키는 것, 즉 커서를 원위치로 복귀(return)한다는 뜻입니다. 커서를 맨 앞으로 보내고, 그 뒤에 문자가 있으면 그 문자를 입력시킵니다.

공백 문자 제거하기
strip(): 양쪽 공백 제거
lstrip(): 왼쪽 공백 제거
rsrip(): 오른쪽 공백 제거

대소문자 관련

  • upper() : 모든 문자를 대문자로 변환합니다.
  • lower() : 모든 문자를 소문자로 변환합니다.
  • capitalize() : 첫 글자만 대문자로 변환합니다.

isX
문자열의 구성에 따라 boolean 값을 반환

  • isupper() : 문자열이 모두 대문자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • islower() : 문자열이 모두 소문자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • istitle(): 문자열의 첫 글자만 대문자로 되어 있으면 True, 그렇지 않으면 False를 반환
  • isalpha(): 문자열이 모두 알파벳 문자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • isalnum(): 문자열이 모두 알파벳 문자와 숫자로만 되어 있으면 True, 그렇지 않으면 False를 반환
  • isdecimal(): 문자열이 모두 숫자로만 되어 있으면 True, 그렇지 않으면 False를 반환

replace()

  • replace() : replace(s1, s2) 형태로 문자열 내 문자열 s1을 s2로 바꿉니다.
sent = 'I can do it!'
sent.replace('I', 'You')

불변(immutable)의 문자열

  • 가변객체(mutable object)
    • 객체를 생성한 후 객체의 값을 수정할 수 있습니다.
    • 변수는 값이 수정된 같은 객체를 가리키게 됩니다.
    • e.g. list, set, dict
  • 불변객체(immutable object)
    • 객체를 생성한 후 객체의 값을 수정할 수 없습니다.
    • 변수는 해당 값을 가진 다른 객체를 가리키게 됩니다.
    • e.g. int, float, complex, bool, string, tuple, frozen set
  • Python 개념 정리 - 객체란 ( mutable vs immutable )
  • [Python 변수] mutable과 immutable의 차이

정규 표현식

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

정규 표현식을 사용하면 startswith(), endswith(), replace()등과 같은 문자열 메소드를 매번 사용하는 것보다 간단하게 구현 가능

import re
정규 표현식의 사용법

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

#1단계 :  "the"라는 패턴을 컴파일한 후 패턴 객체를 리턴합니다. 
pattern = re.compile("the")    

# 2단계 : 컴파일된 패턴 객체를 활용하여 다른 텍스트에서 검색을 수행합니다.
pattern.findall('of the people, for the people, by the people')

아래와 같이 명시적 re.compile() 호출 없이도 가능
re.findall('the', 'of the people, for the people, by the people')

메소드

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

아래는 search(), match() 등이 리턴하는 MatchObject가 가진 메소드입니다.

  • group(): 실제 결과에 해당하는 문자열을 반환합니다.
src = "My name is..."
regex = re.match("My", src)
print(regex)
if regex:
    print(regex.group())
else:
    print("No!")

패턴: 특수문자, 메타문자

  • [ ]: 문자
  • -: 범위
  • .: 하나의 문자
  • ?: 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: 캐리지 리턴(원시 문자열)

예제

#- 연도(숫자)
text = """
The first season of America Premiere League  was played in 1993. 
The second season was played in 1995 in South Africa. 
Last season was played in 2019 and won by Chennai Super Kings (CSK).
CSK won the title in 2000 and 2002 as well.
Mumbai Indians (MI) has also won the title 3 times in 2013, 2015 and 2017.
"""
pattern = re.compile("[1-2]\d\d\d")
pattern.findall(text)
#- 전화번호(숫자, 기호)
phonenumber = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
phone = phonenumber.search('This is my phone number 010-111-1111')
if phone:
  print(phone.group())
print('------')
phone = phonenumber.match ('This is my phone number 010-111-1111')
if phone:
  print(phone.group())

search()와 달리 match()를 사용한 경우, 패턴과 검색대상이 처음부터 일치하지 않으므로 아무 것도 출력되지 않는 것을 볼 수 있습니다.

#- 이메일(알파벳, 숫자, 기호)
text = "My e-mail adress is [email protected], and [email protected]"
pattern = re.compile("[0-9a-zA-Z]+@[0-9a-z]+\.[0-9a-z]+")
pattern.findall(text)

구현 순서

  • import re 를 통해 정규식 모듈을 가져옵니다.
  • re.compile() 함수로 Regex 객체를 만듭니다.
  • 검색할 문자열을 Regex 객체의 search(), findall() 메소드로 전달합니다.

파일과 디렉토리

파일

write

f = open("hello.txt","w") 
#- open(파일명, 파일모드)
#- 파일을 열고 파일 객체를 반환합니다. 
for i in range(10):
    f.write("안녕")
    #- write() 메소드로 '안녕'을 10번 씁니다.
f.close()
#- 작업이 끝나면 close() 메소드로 닫아줍니다. *필수!

print("완료!")

read

with open("hello.txt", "r") as f:
  print(f.read())

파일 관련 메소드

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

디렉토리

루트 디렉토리(root directory): 최상위 폴더

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

리눅스 파일 시스템 영상

모듈과 패키지

파이썬에서 지원하는 디렉토리 관련 표준 라이브러리

  • sys
  • os
  • glob
#- 현재 실행되고 있는 파이썬 실행 파일의 디렉토리를 반환합니다.
import sys
sys.executable
#- 임포트할 때 불러 오는 모듈들이 위치한 경로입니다.
sys.path

개념

  • 모듈(module): 파이썬으로 만든 코드가 들어간 파일 .py
  • 패키지(package): 기능적으로 동일하거나 동일한 결과를 만드는 모듈들의 집합 또는 폴더. 종종 라이브러리라고도 불림
  • 라이브러리(library): 모듈과 패키지의 집합. 패키지보다 포괄적인 개념이나 패키지와 혼용되어 사용되기도 함.
  • PIP(Package Installer for Python): 패키지 관리자로 파이썬을 설치하면 기본으로 설치됨
  • PyPA(Python Packaging Authority): 파이선 패키지를 관리하고 유지하는 그룹
  • PyPI(The Python Package Index): 파이썬 패키지들의 저장소

함수

  • sys.path : 현재 폴더와 파이썬 모듈들이 저장된는 위치를 리스트 형태로 반환
  • sys.path.append(): 자신이 만든 모듈의 경로를 append 함수를 이용해서 추가함. 그 후 추가한 디렉토리에 있는 파이썬 모듈을 불러와 사용할 수 있다.
  • os.chdir(): 디렉토리 위치 변경
  • os.getcwd(): 현재 자신의 디렉터리 위치를 반환
  • os.mkdir(): 디렉토리 생성
  • os.rmdir(): 디렉토리 삭제 (단, 디렉토리가 비어 있을 경우)
  • glob.glob(): 해당 경로 안의 디렉토리나 파일들을 리스트 형태로 반환
  • os.path.join(): 경로(path)를 병합하여 새 경로 생성
  • os.listdir(): 디렉토리 안의 파일 및 서브 디렉토리를 리스트 형태로 반환
  • os.path.exists(): 파일 혹은 디렉토리의 경로 존재 여부 확인
  • os.path.isfile(): 파일 경로의 존재 여부 확인
  • os.path.isdir(): 디렉토리 경로의 존재 여부 확인
  • os.path.getsize(): 파일의 크기 확인

여러가지 파일 포맷 다루기

CSV 파일

CSV

CSV는 Comma Seperated Value의 약자로, 쉼표로 구분된 파일을 의미

각각의 칼럼(column)을 쉼표(,)로 구분합니다.
아래 데이터는 빌보드 차트 1위~5위까지의 곡들입니다. 각 키(key)는 순위를, 값(value)은 곡명, 가수, 그리고 발매일에 대한 정보를 가지고 있습니다.

billboardchart = {
  				 1 : ["Tho Box","Roddy Ricch","2019-12-19"],
                 2 : ["Don't Start Now", "Dua Lipa", "2019-11-01"],
                 3 : ["Life Is Good", "Future Featuring Drake", "2020-02-10"],
                 4 : ["Blinding", "The Weeknd", "2019-11-29"],
                 5 : ["Circles", "Post Malone","2019-08-30"]}

with open("billboardchart.csv","w") as f:
    for i in billboardchart.values():
        data = ",".join(i)
        f.write(data+"\n")

print("완료")

각 칼럼이 무엇을 의미하는지 추가하기 위해 첫 번째 줄에 title, singer, released date를 헤더로 추가합니다.

import csv

header = ["title", "singer", "released date"]

with open("billboardchart.csv","r") as inputfile:
    with open("billboardchart_out.csv","w", newline='\n') as outputfile:
        fi = csv.reader(inputfile, delimiter=',')
        fo = csv.writer(outputfile, delimiter=',')
        fo.writerow(header)
        for row in fi:
            fo.writerow(row)

print("완료")

CSV 파일과 Pandas

판다스(pandas)의 DataFrame은 to_csv 메소드를 지원합니다.
데이터를 판다스를 활용해 csv 파일로 저장합니다.

#- 1. 데이터를 준비합니다.
fields = ["title", "singer", "released date"]
rows = [ ["Tho Box","Roddy Ricch","2019-12-19"],
               ["Don't Start Now", "Dua Lipa", "2019-11-01"],
               ["Life Is Good", "Future Featuring Drake", "2020-02-10"],
               ["Blinding", "The Weeknd", "2019-11-29"],
               ["Circles", "Post Malone","2019-08-30"]]
        
#- 2. 판다스를 이용해 데이터를 csv 파일로 저장합니다.
import pandas as pd
df=pd.DataFrame(rows, columns=fields)
df.to_csv('pandas.csv',index=False)

#- 3. 동일한 내용을 csv.writer를 이용해 수행해 봅니다.
import csv 
filename = "test.csv"
with open(filename, 'w+', newline='\n') as csv_file: 
    csv_writer = csv.writer(csv_file) 
    csv_writer.writerow(fields) 
    csv_writer.writerows(rows)

print("완료")

반대로, csv 파일을 DataFrame으로 변환시키면 데이터 분석 등 사용자가 편집하기에 용이

df = pd.read_csv('pandas.csv')
df.head()

XML 파일

XML

XML은 Extensible Markup Language의 약자로, 다목적 마크업 언어입니다.
인터넷 웹상에서 문서 즉, 내용을 교환할 때 마크업 언어를 이용합니다. (HTML)
간단히 말하자면, 태그(< >)로 구분된 언어, API에서 데이터를 요청하고 저장할 때 XML이나 JSON 형식을 이용해 데이터를 교환

<Person>
    <Name>이펠</Name>
    <Age>28</Age>
    <Place>강남</Place>
</Person>

#- 꺾쇠 괄호 안에 태그 이름을 정의하고, 태그 사이에 데이터를 기록하는 형식입니다.
#- 참고로, 태그 이름은 사용자가 마음대로 지정할 수 있습니다.

아래는 세계보건기구(WHO)에서 제공하는 데이터 API 정보의 XML파일 일부

<?xml version="1.0" encoding="utf-8"?>
#- XML의 버전과 인코딩을 명시하는 태그입니다. *필수!
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
          xmlns:o="urn:schemas-microsoft-com:office"
          xmlns:x="urn:schemas-microsoft-com:excel"
          xmlns:ss="urn:schemas-microsoft-com:spreadsheet"
          xmlns:html="http://www.w3.org/TR/REC-html40">
   <DocumentProperties/>
   <Styles>
      <Style ss:ID="Hyperlink" ss:Name="Hyperlink">
         <Font ss:Color="#0000FF" ss:Underline="Single"/>
      </Style>
      <Style ss:ID="header">
         <Alignment ss:Vertical="Bottom" ss:WrapText="1"/>
         <NumberFormat ss:Format="@"/>
      </Style>
   </Styles>
   <Worksheet ss:Name="notice">
      <Table>
         <Row>
            <Cell>
               <Data ss:Type="String">Notice</Data>
            </Cell>
         </Row>
         <Row/>
         <Row>
            <Cell>
               <Data ss:Type="String">Date generated</Data>
            </Cell>
         </Row>

상위 태그 안에 하위 태그가 속해 있을 수 있는데 이런 관계를 부모-자식 관계라고 합니다.
위에서 <Table>태그는 <RoW>, <Cell>, <Data> 태그를 자식 태그로 가지고 있습니다.

기본적으로 <태크> 내용 </태그> 형태로 구성되어 있지만 태그에 속성(attribute)값이 포함될 수도 있습니다.

XML 파일 만들기

ElementTree
파이썬 표준 라이브러리인 ElementTree는 XML 관련 기능을 다음과 같이 제공합니다.

  • Element(): 태그 생성
  • SubElement(): 자식 태그 생성
  • tag: 태그 이름
  • text: 텍스트 내용 생성
  • attrib: 속성 생성

dump()
생성된 XML 요소 구조를 시스템(sys.stdout)에 사용합니다. 출력 형식은 일반 XML 파일로 기록됩니다.

  • write(): XML 파일로 저장
  • 리스트(list)와 유사한 메소드를 제공
    • append(), insert(), remove(), pop()

아래는 person XML을 만드는 코드입니다.

import xml.etree.ElementTree as ET

person = ET.Element("Person")
name = ET.Element("name")
name.text = "이펠"
person.append(name)

age = ET.Element("age")
age.text = "28"
person.append(age)

ET.SubElement(person, 'place').text = '강남'

ET.dump(person)

다음과 같은 XML 파일이 만들어 집니다.

<Person>
<name>이펠</name>
<age>28</age>
<place>강남</place>
</Person>

속성값은 attrib란 메소드를, name 태그명은 tag 메소드를 이용해 변경할 수 있습니다.

person.attrib["id"] = "0x0001"
name.tag = "firstname"
ET.dump(person)
<Person id="0x0001">
<firstname>이펠</firstname>
<age>28</age>
<place>강남</place>
</Person>

새롭게 lastname이라는 태그를 firstname 태그 다음으로 삽입하고 속성에 date를 넣어 보겠습니다.

lastname = ET.Element('lastname', date='2020-03-20')
lastname.text = '아'
person.insert(1,lastname)
ET.dump(person)

속성은 태그를 생성하는 함수인 Element 의 인자로 넣어 주어도 됩니다. firstname 태그의 다음으로 인덱스 번호는 1번이 됩니다.
insert() 를 이용해 인덱스 번호와 태그 이름을 삽입합니다.(리스트의 insert() 와 형태가 동일)

<Person id="0x0001">
<firstname>이펠</firstname>
<lastname date='2020-03-20'></lastname>
<age>28</age>
<place>강남</place>
</Person>

삭제는 remove() 혹은 pop() 을 이용합니다. age 태그를 지우고 싶다면 다음과 같이 합니다.
person.remove(age)

XML 파일로 저장합니다.
ET.ElementTree(person).write('person.xml')

XML 파싱하기

파싱(parsing, 구문 분석)이란?
어떤 문자열을 의미있는 토큰(token)으로 분해해, 문법적 의미와 구조를 반영한 파스 트리(parse tree)를 만드는 과정입니다. XML 문서를 파싱하게 되면 특정 태그명이나 속성값 등을 불러올 수 있습니다.

파이썬에서는 크게 2가지 방법이 있습니다.
위에서 살펴본 ElementTree와 추가로 BeautifulSoup 라이브러리가 있습니다.

1) 설치

$ pip install beautifulsoup4
$ pip install xml

2) books.xml 파일의 "title" 태그의 내용만 가져오기

from bs4 import BeautifulSoup
import os

path = os.getenv("HOME") + "/aiffel/ftext/data/books.xml"
with open(path, "r", encoding='utf8') as f:
    booksxml = f.read() 
    #- 파일을 문자열로 읽기
 
soup = BeautifulSoup(booksxml,'lxml') 
#- BeautifulSoup 객체 생성 : lxml parser를 이용해 데이터 분석

for title in soup.find_all('title'): 
#-  태그를 찾는 find_all 함수 이용
    print(title.get_text())

BeautifulSoup의 다른 메소드

JSON 파일

JSON

JSON은 JavaScript Object Notation의 약자로, 웹 언어인 JavaScript의 데이터 객체 표현 방식입니다.
웹 브라우저와 다른 애플리케이션 사이에서 HTTP 요청으로 데이터를 보낼 때 널리 사용하는 표준 파일 포맷중 하나로, XML과 더불어 웹 API나 config 데이터를 전송할 때 많이 쓰입니다.

JSON 데이터 예시

person = {
      "first name" : "Yuna",
      "last name" : "Jung",
      "age" : 33,
      "nationality" : "South Korea",
      "education" : [{"degree":"B.S degree", "university":"Daehan university", "major": "mechanical engineering", "graduated year":2010}]
       } 
  • 파이썬의 dict 타입과 유사한 구조
  • CSV 파일에 비해 좀 더 유연하게 데이터를 표현 가능
  • XML 파일보다 파일을 쉽게 읽고 쓸 수 있음
  • Javascript로 작성된 프로그램에서 쉽게 다룰 수 있음

JSON 파싱

1) JSON 파일 저장
person이라는 dict 객체를 JSON파일로 저장

import json

person = {
      "first name" : "Yuna",
      "last name" : "Jung",
      "age" : 33,
      "nationality" : "South Korea",
      "education" : [{"degree":"B.S degree", "university":"Daehan university", "major": "mechanical engineering", "graduated year":2010}]
       } 

with open("person.json", "w") as f:
    json.dump(person , f)

2) JSON 파일 읽기
파이썬 dict 객체로 쉽게 읽을 수 있음

import json

with open("person.json", "r", encoding="utf-8") as f:
    contents = json.load(f)
    print(contents["first name"])
    print(contents["education"])

좋은 웹페이지 즐겨찾기