ㅅㅇ

python 프로그래밍 : 텍스트 파일 입출력 본문

AI_STUDY/Python

python 프로그래밍 : 텍스트 파일 입출력

SO__OS 2022. 5. 19. 21:23

_플레이데이터 빅데이터캠프 공부 내용

 

- 텍스트(입출력할 데이터 종류) 파일(입출력할 대상) 입출력 을 뜻한다.

1. 입출력 개요

경로란?

파일, 디렉토리 위치 -> 자원의 위치. 프로그램이 사용할 자원의 위치

 

경로 표현 방법

ROOT 디렉토리 : 시작 디렉토리

- 윈도우 -> C: , d:

- 리눅스, 유닉스 ->  /

 

(1) 절대경로

- 시작 디렉토리부터 자원(파일, 디렉토리)의 위치를 표현한다.

    - 시작 디렉토리 : root 디렉토리

    - ex. 파일 경로 c:\class\p1.pdf    디렉토리 경로  c:\class\lib  #윈도우

- 자원의 전체 경로를 경로를 표현하는 방식

- 장점 : 같은 데이터라면 항상 경로 고정 (오류 확률 낮다.)    - 단점 : 길다

 

(2) 상대경로

- 현재 디렉토리(위치)로부터 찾아가는 경로 표현방식

       - 시작  :  현재 디렉토리

- 장점 : 짧다       - 단점 : 같은 데이터라도 현 위치에 따라 경로가 다르다 (오류 확률 높다.)

- 구문

      : 현재 디렉토리

    ..    : 상위 디렉토리

    윈도우즈  \   리눅스 유닉스 / : 경로 구분자   ->  하위 계층으로 가라.

 

- ex.   현재 디렉토리 c:\class 

     .\p1.pdf  => 현재(.)디렉토리의 하위 계층(\)으로 가라. 

보통 .\은 생략한다. p1.pdf

 

- ex.   현재 디렉토리 c:\class 

     .\01_PYTHON\a.txt  => 현 디렉토리의 하위 계층에서 파이썬 폴더의 하위계층에서 해당 파일을.   보통 .\은 생략한다.  보통 .\은 생략한다. 01_PYTHON\a.txt 

 

- ex.   현재 디렉토리 c:\lntel\n  => 현 디렉토리에서 나와서 다른 디렉토리로 가야 한다. 

     .\..\..\class\01_PYTHON\a.txt 

보통 .\은 생략한다. ..\..\class\01_PYTHON\a.txt 

 

** 경로구분자 만날 때 마다 그 해당 디렉토리에 들어간다고 생각하자. ** 계층구조 이해하기(이미지로 생각)

 

입출력이란?

- 프로그램외부 자원(파일...)에 데이터를 쓰거나(output 출력) 읽는(input 입력) 작업을 입출력(IO)라고 한다.

- 외부자원

: 데이터를 제공하는 것 : 파일, Database, 원격지 컴퓨터(네트워크로 연결된) 등

: 어떤 자원이냐에 따라 입출력 방식이 달라지지만 입출력 코딩 패턴(아래), 개념 자체는 동일하다.

 

코딩 패턴 : 1 파일 열기(연결)  ->  2 데이터를 파일에 쓰기/읽기  -> 3 파일 닫기(열결 끊기)

          file = open()   ->      file.write()  / file.read()      ->    file.close()

 

- 파일열기, 데이터를 읽기쓰기는 예외상황이 있기 때문에  ->  try에 넣는다. 

- 파일 닫기(연결 끊기)는 무조건 일어나야 하는 작업   ->  finally에 넣는다.

 

- 현재 내 프로그램을 위해 외부자원에서 내가 필요한 데이터 입력받고(read), 내부에 저장할 데이터를 출력(write)한다.

  ex. 내가 만든 코드를 디렉토리 자원에 저장한다. -> Output / 하드디스크에 있는 받은 코드 파일을 읽어온다. -> Input / 작업 한다.(여기다가 쓴다고 출력이 아님. 저장을 해야.) /하드디스크에 수정한 파일을 저장한다. -> Ouput )

 

- 컴퓨터는 기계어 0과1  2진수만을 이해한다. 우리 작성한 name ="홍길동" 또한 기계어로 저장된다.

이때 데이터를 출력된다는 것은 바이트단위로 외부자원으로 흘러 가는 것. 프로그램과 외부자원이 통이 연결되고 그 통으로 데이터가 흘러가고 바이트 단위로   

 

- Stream

: 데이터 입출력 흐름. 프로그램과 외부자원 사이 데이터가 흐르는.

입력(자원에서 프로그램으로 데이터가 흐르는) : InputStream

출력(프로그램에서 자원으로 데이터가 흐르는) : OutStream  

 

입출흐름, 출력흐름 하는 것들이 있는데, 그것 하나만 가지고 이 흐름이 중복되거나, 왔다갔다 같이 할 수 없다.

한쪽으로만 흐르는 개념이다. 입력흐름이면 입력만, 출력흐름이면 출력만 흐름 하나가지고 둘 다 할 수 없다.

 

 

** 현재 경로 - workdirectory 경로 조회 방법

import os # os 모듈을 import

cwd = os.getcwd()   # current work directory
print(cwd)  # 상대경로의 현재위치가 된다. ( . )

 

2. TEXT 파일 입출력

- 문자열(string)을 입/출력 하는 것.
- 메모장으로 볼 수 있으면 text

- 모드 : 목적+데이터형식(t)   => rt, wt

 

인코딩이란?

: 원본(내 데이터)를 일관된 형식으로 외부자원에 맞춰 변환하는 것.

==> 유니코드를 기반으로 인코딩 방식(실질적으로 처리하는 방식)의 다양한 종류가 있다. 

- open 함수에서 encoding="" 인코딩방식을 다르게 넣을 수 있다. 
    - None일 경우 os 기본 인코딩 방식따름.   윈도우 "ANSI"   쥬피터 인코딩 "utf-8" 
    - 근데 utf-8로 출력하면 입력받을 때도 utf-8로 인코딩 방식을 맞춰서 해줘야 함.

       남의 데이터를 입력받을 때도 해당 데이터의 방식을 알아내어 읽어줘야 한다.

- 우리가 "A"을 작성한다고 해서 그대로 저장되는 게 아님. 기계어 0과 1로 저장되어야 한다. 숫자 10진수는 2진수로 바꾸는 방법 우리가 안다. 그렇다면, 문자열을 이진수로 어떻게 바꾸냐? 약속한 표를 만들었다.
    - ASC 코드 : 8bit로 정의한 코드 2\**8=256가 정의되어 있다.영문 알파벳을 사용하는 대표적인 문자 인코딩
        - 근데 영어말고도 많은 언어를 표현할 수 있어야 한다. 그래서 Unicode를 만듦
    - Unicode : 전 세계의 모든 문자를 일관되게 표현하고 다룰 수 있도록.  16bit

디코딩이란?

코드화된 내용을 원래의 정보로 변환하는 디코딩 작업

 

** UnicodeDecodeError : 잘못된 (저장된 인코딩방식과 다른) encoding 방식으로 읽을 경우 발생. 해당 파일의 인코딩방식으로 입력받아야 한다.   ex)  encoding=""

 

<출력하기 Write>

1)  연결하기 _ open() 함수 사용

: 연결된 파일과 입출력 메소드를 제공하는 객체(Stream)를 리턴

 

- open(file, mode, encoding) 상황에 따라 함수 주요 매개변수 넣어주기.

  file : 연결할 경로

  mode : 열기 모드 : 목적과 데이터종류를 조합해 나타낸다.

  encoding : 해당 인코딩 표시. (None일 경우 OS 기본 encoding 방식을 따른다.)

 

-  목적 : 'r '읽기(기본값) 'w' 쓰기  'a' 이어쓰기(기본내용 두고)

-  데이터 종류 : 'b' 바이너리모드  't' 텍스트모드(기본값: 생략가능)

 

- 구문

fw = open(file_path, mode, encoding="utf-8")

   => '파일'과 '지정한 모드'에 맞춰서 연결하고 입출력할 수 있는 기능(메소드)를 제공하는 객체를 반환. 이것을 변수로 받아서 쓰면됨.

 

- 출력 연결의 경우에는 연결할 파일이 없으면 생성. 있으면 있는 파일과 연결 (단 데이터를 지운다.)
- 입력 연결의 경우에는 연결할 파일이 없으면 exception 발생.  => 예외처리해주기. try except finally

 

 

EX. txt의 문자열데이터를 "현재작업경로\a.txt" 파일에 출력(저장)

 

# 출력 데이터
txt = """안녕하세요
반갑습니다.
오늘도 즐거운 하루되세요."""
print(txt)

# 1. a.txt와 연결: 목적-출력, 데이터종류-string(text)

# 상대 경로 #  .\을 생략
file_path = "a.txt"
# 목적데이터종료
mode = "wt"  # 목적: w 쓰기모드  데이터 :t  텍스트 모드  #t 기본값이라 안 써도 되지만 써주자.


fw = open(file_path, mode, encoding="utf-8") 
# 파일과 지정한 모드에 맞춰서 연결하고 입출력할 수 있는 기능(메소드)를 제공하는 객체를 반환. 이것을 변수로 받아서 쓰면됨.

- 만약 절대경로로 표시한다면?

# file_path = r"C:\Users\ABC\a.txt" # 절대경로  
# r붙이면 escape \ 처리 안 하게 # 유닉스 슬러시면 escape 아니라 상관없다.

2) 데이터쓰기 출력  _  객체명.write(데이터)

fw.write(txt)

# 이때 출력값은 글자수이다.

3) 연결닫기 : 예외없이 얼어나야 함.   _  객체명.close()

fw.close()

 

<입력받기 Read>
1) 연결하기(파일열기)  _ open() 함수 사용

fi = open("경로", "rt", encoding="utf-8")

# 목적 : 읽기 - r(기본값),   데이터 : string(text) - t(기본값) 

2) 데이터읽기 입력  _  데이터 받을 변수 = 객체명.Read()

r_txt = fi.read()

- 받아온 데이터를 변수에 저장

3) 연결닫기  _  객체명.close()

fi.close()

i_file_path = "a.txt"  # 상대경로
# 목적 : 읽기 - r(기본값),   데이터 : string(text) - t(기본값) 
i_mode = "rt"

# 연결
fi = open(i_file_path, i_mode, encoding="utf-8")

# 입력 - read() 받아 변수에 저장
r_txt = fi.read()
print(r_txt)

# 3. 연결닫기
fi.close()

 

<연결 닫기 코드 예외처리 해주기 >

- 연결닫기는 무조건 실행해야 하는 코드

 

방법 1) try except finally 로 처리

# 예외처리 - "연결닫기 " finally 처리

try:
    fi = open("a.txt", "rt")
    t = fi.read() # 이 줄에서 오류난다면
    print(t)

except:
    print("처리") # 출력하고
    
finally:
    print("연결닫기") # 이구문 반드시 하고
    fi.close()

 

방법 2)  with block 이거쓰기!

- with block을 빠져나오면 close를 자동으로 처리한다.

- 구문

with 연결함수 as 변수: 

    작업코드
    
- print(fi.closed) # 연결여부를 부울값으로 반환받아 체크할 수 있다. : True-끊는 것, False-연결상태

 

ex)

with open("a.txt", "rt", encoding="utf-8") as fi

 

EX. With block 예시

with open("a.txt", "rt", encoding="utf-8") as fi: 
# fi = open(...)과 동일
# as 뒤에 객체 변수

# 입출력코드
    try:
        r = fi.read()
        print(fi.closed) # 연결여부를 반환 : True-끊는 것, False-연결상태
        print(r)
    except:
        print("예외처리")

print("with block 밖", fi.closed)

# with block을 빠져나오면! close를 자동으로 처리한다.

 

3. 출력 메소드

(1)  fw.wirte("")

- 출력은 여러번에 걸쳐서도 할 수 있다. 
        - 작업하고 출력하고 작업하고 출력하고 가능한 것
        - but, print와 달리 엔터처리 없음. 이어서 나옴. => \n 넣어줘야 함

- 출력가능한 타입은 string = > 다른 타입은 모두 str() 변환해줘야 한다.

with open("b.txt", "wt") as fw:
    
    fw.write("안녕하세요\n")
    fw.write("반갑습니다.\n")
    fw.write("조금만 있으면 점심시간이네요.\n")
    fw.write(30) 
    # 출력가능한 타입은 string!!

 

(2)  리스트 데이터를 출력

 

방법1 ) fw.writelines(txt_list)

: 리스트의 원소들을 한번에 출력

- 원소와 원소 사이가 엔터가 안되기 때문에 각 원소 사이에 \n을 넣어준 것이다.

 

EX. 리스트 출력 _ 메소드 fw.writelines(txt_list) 로 

# EX. 리스트 출력 fw.

txt_list = ["안녕하세요.\n", "지금 12시 16분입니다.\n", str(120)]

with open("c.txt","wt") as fw:
    fw.writelines(txt_list) # 리스트의 원소들을 한번에 출력 # 엔터 안 넣으면 한줄

 

방법2) 리스트 출력을 위 함수메소드 대신에 반복문으로 구현 가능하다.

# EX. 리스트 출력  _ for 문으로

txt_list = ["안녕하세요.\n", "지금 12시 16분입니다.\n", str(120)]

for t in txt_list:
	fw.write(txt_list(i))

 

4. 입력 메소드

(1) fi.read() : 파일 데이터를 한번에 읽는다.

 

EX. 뉴스 news.txt 파일 읽기_fi.read()

news_file_path = 'text_data/news.txt'
with open(news_file_path, "rt", encoding="utf-8") as fi:
    news = fi.read() # 파일의 전체내용을 한번에 읽는다.
    
print(news)

- 파일 내 엔터되있으면 엔터 되있는대로. 그냥 파일의 전체내용을 한번에 읽어 변수에 저장한다.

 

(2)  리스트 데이터를 입력 Read

 

방법1) fi.readlines() 

\n 엔터 기준으로 한 줄, 한줄을 원소로 하는 리스트를 반환.

    - 잘 안 씀.

 

EX. 뉴스 news.txt 파일 읽기 _ 리스트에 넣어 읽기_메소드 fi.readlines()

# EX.뉴스 news.txt 파일 읽기 _ 리스트에 넣어 읽기

with open(news_file_path, "rt", encoding="utf-8") as fi:
    text_list = fi.readlines() # \n 엔터 기준으로 한 줄, 한줄을 원소로 하는 리스트를 반환.

print(text_list) # 리스트 전체 출력
print("--------------")
print(text_list[0])
print(text_list[1])
print(text_list[2])

 

방법2) 리스트 출력을 위 함수메소드 대신에 반복문으로 구현 가능하다.

- 반복문을 이용해 한줄 씩 읽을 수 있다.

- rt모드로 연결한 객체(TextIOWapper)가 iterable

     => 직접 for in 문의 in 자리에 넣을 수 있다.

    - read 메소드를 사용하지 않고 객체 자체를 바로 print 하는 것. 

- read() fi 반복문. 이를 가장 많이 쓴다. 굳이 writelines 쓸 필요 없음.

 

EX. 뉴스 news.txt 파일 읽기 _ 리스트에 넣어 읽기_ for문으로

# EX.뉴스 news.txt 파일 읽기 

# read() fi 반복문. 이를 가장 많이 쓴다.
# 그래서 굳이 writelines 쓸 필요 없음.

with open(news_file_path, "rt", encoding="utf-8") as fi:
    for ln, txt in enumerate(fi,start=1):
        print(f"{ln}.{txt}")
1.아스널 레전드가 극찬한 공격수, ‘SON 파트너’로 부상

2.토트넘 홋스퍼가 손흥민(29), 해리 케인(28) 파트너를 찾았다.


(3) fi.readline()

# 한줄씩만 읽는다. 읽고 그 다음 줄

with open(news_file_path, "rt", encoding="utf-8") as fi:
    print(fi.readline()) #한줄만 읽는다.
    print(fi.readline())
아스널 레전드가 극찬한 공격수, ‘SON 파트너’로 부상

토트넘 홋스퍼가 손흥민(29), 해리 케인(28) 파트너를 찾았다.


5. binary file 입출력

- text 파일을 제외한 나머지. 
- 모드 : 목적+데이터형식(b)   => rb, wb
- 메소드 : write(), read()  # readline(s)는 txt용이라 안씀.

 EX. 이미지 파일 복사하는 코드

=> 두 단계로 진행된다. (1. 읽고 -> 2.쓰기(다른파일명))

 1. 파일 Read

# EX. img.jpg 을 복사
# 1. 파일 읽기
try:
    fi = open('img.jfif', 'rb')
    img = fi.read()
    print(type(img))  # 확인 byte 타입
finally:    
    fi.close()

2.  파일 Write : 다시쓰기-다른파일명

# EX. img.jpg 을 복사
# 2. 파일 쓰기

try:
    fo = open('img3.jfif', "wb")
    fo.write(img)
finally:
    fo.close

 EX. 이미지 파일 복사하는 코드 _ 두 단계를 한 번에 하고 싶을 때 _ 중복 with 문

# img.jpg 을 복사 (읽은 뒤에 다시쓰기-다른파일명)
# 1.2. 단계 한번에 하고 싶을 때 with문 두 개로 

with open('img.jfif','rb') as fi: # Read 객체 fi
    with open('img3.jfif', 'wb') as fo: # Write 객체 fo
        img = fi.read()  # img파일을 읽어와서 img 변수에 저장하고
        fo.write(img) # img변수를 통해 img3 파일에 저장한다.

- 이렇게 read 객체 write 객체 각각 불러와 함께 처리하고 다 하면 with로 파일 닫기

 

6. pickle

파이썬 객체(모든 종류의 값)을 binary로 입출력할 때 사용하는 모듈.
    - 입출력을 위해 모든 데이터를 변환!!해주는 모듈

- import pickle : 사용을 위해 피클 모듈을 import 해줘야 한다.

 

(1) write

pickle.dump(data, fo) #쓰기. 저장. 

 

EX. 리스트 객체를 파일에 저장하기 (write)

# 리스트 객체를 파일에 저장 
import pickle # 피클 모듈을 import

data = ['abc', '가나다', True, 1000, 200, 30.67]
with open('list.pkl', 'wb') as fo:
    pickle.dump(data, fo) #쓰기. 저장.
    
type(data) # 원본데이터를 바꾸는 것은 아니고 입출력할 때 binary로 변환하는 것.

(2) read

read_list = pickle.load(fi)

- 이렇게 피클로 정한 파일도  원래 데이터 타입 list로 읽을 수 있다. 

# 피클로 정한 데이터(리스트)를 읽기

with open('list.pkl','rb') as fi:
    read_list = pickle.load(fi)
    
read_list

 

EX. 위 예제 클래스 객체 함수로 구현하기 

# 클래스 선언 
class item:
    
    def __init__(self, name, price):
        self.name, self.price = name, price
    def __str__(self):
        return f"Type: {type(self)}, name: {self.name}, price:{self.price}"

# 리스트 객체를 파일에 저장 쓰기
import pickle # 피클 모듈을 import

data = item('tv', 250000)

with open('list.pkl', 'wb') as fo:
    pickle.dump(data, fo) #쓰기. 저장. 

# 2 피클로 정한 데이터(리스트)를 읽기
with open('list.pkl','rb') as fi:
    read_list = pickle.load(fi)
print(read_list)
print(type(read_list))

 

## CSV (Comma Separated Value) 파일 형식
- 데이터를 표형태로 텍스트파일에 저장하기 위한 문서형식
하나의 데이터는 한 줄에 작성 (엔터 : 데이터값 구분자)
하나의 데이터를 구성하는 속성값들은 ',' 로 구분

 

 

[TODO 문제 1 _ 간단한 메모장]

사용자로부터 파일명을 입력받는다.(input())
사용자로 부터 파일에 저장할 문장을 입력받아서 파일에 저장한다. ->반복
사용자가 !q 를 입력하면 종료한다. -> 조건
사용자가 저장한 파일을 읽어서 출력한다.

 

- 코드 -

def simple_memo():
    fname = input("파일명은?:")

    with open(fname, "wt") as fw:
        print("내용을 입력하시오.")
        print("----------")
        while True:
            line =input()
            if line == "qi":
                break
            else:
                fw.write(line+"\n")
    with open(fname, "rt") as fi:
        r1 = fi.read()
        print("------------")
        print("<입력받은 값>")
        print(r1)

        
simple_memo()

[TODO 문제 2.1 _ csv 파일을 읽어서 각 열의 값을 배열에 담는다.]

이름,나이,주소  형태의 csv를 읽어
names = []
ages =[]
address =[]
리스트에 넣는다. (라인이 index가 된다.) 

파일 member.csv

name,age,address
김영수,20,서울
박영희,21,서울
오민수,30,부산
최정길,15,인천
이명수,22,광주
이철수,17,대전

- 코드 -

# 풀이 코드
names = []
ages = []
address = []

with open("member.csv", "rt", encoding="utf-8") as fi:
    
    for idx, line_num in enumerate(fi):
        if idx == 0: # 첫번째 줄 제외를 위해
            continue
        info = line_num.split(",") # 구분자로 나눠주기
        
        names.append(info[0])
        ages.append(info[1])
        address.append(info[2].strip()) # \n을 없앨 수 있다.
    
fi.close
print(names) 
print(ages) 
print(address)
['김영수', '박영희', '오민수', '최정길', '이명수', '이철수']
['20', '21', '30', '15', '22', '17']
['서울', '서울', '부산', '인천', '광주', '대전']
# 내가 푼 것

names =[]
ages =[]
address=[]

with open("member.csv", "rt", encoding="utf-8") as fi:
    
    text_list = fi.readlines()
    for line_num in text_list[1:]:
        info = line_num.split(",")
        names.append(info[0])
        ages.append(info[1])
        address.append(info[2].strip())
    
fi.close
print(names) 
print(ages) 
print(address)

- info[2].strip()  :  # \n을 없앨 수 있다.

 

[TODO 문제 2.2 _ name, ages, address => member_ 2.csv 파일에 csv 형식으로 저장하기(zip()) .]

# EX. name, ages, address => member_ 2.csv 파일에 csv 형식으로 저장하기(zip())

with open("member_2.csv","wt", encoding="utf-8") as fi:
    fi.write("name,age,address\n")
    
    #같은 인덱스끼리 튜플로 묶어서 # 튜플대입
    for name, age, addr in zip(names,ages,address): 
        fi.write(f"{name},{age},{addr}\n")

fi.closed

- 튜플 그대로 str 변환해 쓰기하면 괄호()까지 파일에 저장됨. => 튜플대입해주기