방송대 컴퓨터과학과/파이썬 프로그래밍 기초

방송대 파이썬 프로그래밍 기초 - 12강. 파일

꼽파 2023. 5. 22. 17:41


  • 1. 파일의 이해

  • 2. 파일의 활용

  • 1. 파일의 이해

    파일의 역할

    파일(file) : 컴퓨터에 의해 처리될 또는 처리된 데이터와 정보가 임시적으로 저장된 상태

    - 일련의 연속된 바이트

    - 프로그램(파이썬 소스코드)에 읽혀 가공, 처리 후 다시 파일에 기록시킴.


    파일의 구성

    연속된 바이트와 파일의 시작, 파일 포인터, 파일의 끝(EoF)으로 표현

    출처 : 12강 강의록


    파일의 종류

    데이터가 저장되는 방식에 따라 텍스트 파일바이너리 파일로 구분됨.

    두 파일을 쉽게 구분하는 방법 : 텍스트 에디터로 열리면 텍스트 파일, 그렇지 않으면 바이너리 파일
    대다수의 파일은 다 바이너리 파일임.

    텍스트 파일 바이너리 파일
    199를 '1', '9', '9'라는 서로 다른 개별 문자로 인식함.
    인코딩 시스템이 아스키코드 값이라면 아스키코드의 1,에 해당되는 바이트, 9에 해당되는 바이트 두 개를 기록함. 
    해당 텍스트 파일에는 1, 9, 9라고 하는 문자가 적혀있다는 방식으로 해석함.
    199를 개별 문자로 인식하는 게 아니라 실제 바이너리 숫자로 저장함.
    199는 0부터 255 또는 -127에서 128까지 1바이트 또는 2바이트로 기록함.
    (훨씬 더 적은 데이터 용량으로 실제 데이터 값을 저장 가능함.)
    - 주로 아스키코드 또는 유니코드 인코딩을 사용함.
    - 일반적으로 텍스트 파일은 바이너리 파일보다 큼.
    - 특정 인코딩을 사용하지 않음.
    - 일반적으로 바이너리 파일은 텍스트 파일보다 용량이 적음.

    파일 함수

    파일의 시작, 파일 포인터, 파일의 끝을 활용하여 데이터 읽기, 쓰기를 위한 함수 및 메소드를 내장

    멤버 설명
    open():file obj 파일과 연결되어 있는 파일 객체 생성
    read() 특정 개수의 문자를 반환
    readline() 한 라인의 문자열을 반환
    readlines(): list 전체 라인의 문자열을 리스트로 반환
    write(s: str): 파일에 문자열을 작성
    close() 파일 닫기 및 파일 객체 삭제
    close()를 하지 않았을 경우 잘못된 연산이 이루어지거나 파일이 깨질 수도 있으니 주의
    # open(): 파일과 연결된 파일 객체 생성
    file = open("example.txt", "r")
    # → "example.txt" 파일을 읽기 모드("r")로 열고, 파일과 연결된 파일 객체 file을 생성
    
    # read(): 특정 개수의 문자를 반환
    data = file.read(10)
    print(data)
    # → 파일 객체 file에서 최대 10개의 문자를 읽고, 읽은 내용을 변수 data에 저장하고 출력
    
    # write(s: str): 파일에 문자열을 작성
    file.write("Hello, World!")
    # → 파일 객체 file에 문자열 "Hello, World!"를 작성
    
    # close(): 파일 닫기 및 파일 객체 삭제
    file.close()
    # → 파일 객체 file을 닫아서 파일과의 연결을 종료
    # 파일을 사용한 후에는 close()를 호출하여 메모리 누수를 방지
    
    # readline() 함수 : 한 라인의 문자열을 반환
    # readlines() 함수 : 전체 라인의 문자열을 리스트로 반환

    파일 객체 생성

    객체는 생성-사용-삭제의 과정을 거치는데, 파일 객체도 마찬가지임.

    가비지 컬렉터(Garbage collector : 파이썬 인터프리터가 참조변수로 쓰이지 않는 객체들을 주기적으로 점검하고 정리하여 프로그램의 메모리 사용량을 최적화하는 기능

    구문형식

    → 물리적인 파일과 연결된 파일 객체를 생성하고 참조변수에 할당

    모드 설명
    'r' 읽기 용도
    'w' 새로운 파일을 쓰기 용도
    'a' 파일의 끝에 데이터를 덧붙이기 용도

     


    파일 이름

    파일의 고유 식별자 역할 및 저장장치 내부에서 파일의 위치를 표현하는 파일경로를 내포

    해당 파일에 접근하기 위한 모든 정보가 다 들어있음.

    파이썬 프로그램이 텍스트 파일과 같은 폴더에 있다면 앞의 경로 생략 가능함.

    리눅스의 파일 시스템 구조로 만들기 : 드라이브 → var → etc → python.py


    파일 읽기

    특정 범위의 데이터를 파일에서 읽고 문자열로 반환

    파이썬에서는 파일을 읽을 때 파일 포인터의 이동이 일어남.
    - 파일 포인터는 현재 파일에서 읽거나 쓸 위치를 가리키는 역할을 함.
    - 파일을 읽을 때마다 파일 포인터는 읽은 데이터의 으로 자동으로 이동됨.

    file = open("example.txt", "r")
    
    data1 = file.read(5)
    print(data1)  # 출력: "Hello"
    
    # 파일 포인터는 "Hello"의 뒤에 위치한 상태
    
    data2 = file.read(5)
    print(data2)  # 출력: "\nWorl"
    
    # 파일 포인터는 "Hello\nWorl"의 뒤에 위치한 상태
    
    data3 = file.read()
    print(data3)  # 출력: "d\nPython"
    
    # 파일 포인터는 파일의 끝에 위치한 상태
    
    file.close()

    \n (new line character) : 한 줄을 밑으로 내리라는 의미

    h_fp = open("Hamlet_by_Shakespeare.txt", "r")
    # 경로가 없음 → 같은 폴더 내에 있음
    # open() 함수 : 파일과 연결된 객체 생성
    # r : 읽기 모드 사용
    # 'h_fp'라고 하는 참조변수로 메소드를 부름.
    
    title = h_fp.read(6)
    # 파일 포인터가 이동하면서 최대 6개의 문자를 읽음.
    # title에는 'Hamlet'이 문자열 형태로 저장됨.
    # read 함수 : 입력 파라미터 만큼의 바이트 수 만큼 파일 포인터로 읽어서 데이터를 반환함.
    
    author = h_fp.readline()
    # realine 함수 : 다음에 있는 개행 포인터(\n)가 나올 때까지 쭉 읽어들임.
    # author에는 'by William Shakespeare'라고 하는 문자열이 들어감.
    
    h_fp.close()
    # 파일 포인터를 닫아서 삭제함.
      read() 함수 readline() 함수
    파라미터 O 파일에서 지정한 개수문자를 읽어서 반환함. 파일에서 지정한 개수 줄의 문자열을 읽어서 반환함.
    파라미터 X 파일 전체 내용을 읽어옴. 파일에서 한 줄의 문자열을 읽어서 반환함.
    (\n이 나올 때까지)

    파일 쓰기

    문자열을 파일 포인터가 위치한 지점에 기록함.

    "w" 모드는 파일을 새로 생성하고, 파일 포인터가 맨 앞으로 위치함.

    동일한 파일 이름을 입력한 경우, 기존 파일이 이미 존재하면 해당 파일의 내용은 모두 삭제되고 새로운 파일이 생성됨.

    이미 존재하는 파일을 덮어쓰게 되므로 파일을 생성하기 전에 기존 파일의 내용을 백업하거나, 파일이 존재하는지 확인하는 등의 조치를 취하는 것이 좋음.

     

    p_fp = open("python.txt", "w")
    # 'python.txt'라는 파일을 쓰기모드(w)로 열기
    # 이미 동일한 이름의 파일이 있다면 해당 파일의 내용은 모두 삭제됨.
    # 파일과 연결된 파일 객체는 p_fp라는 변수에 할당됨.
    
    p_fp.write("KNOU\n")
    # 파일 객체인 p_fp를 통해 "KNOU"라는 문자열을 파일에 작성함.
    # KNOU가 한 줄에 작성됨.
    
    p_fp.write("python programming\n")
    # p_fp를 통해 'python programming'이라는 문자열을 파일에 작성함.
    
    p_fp.close()
    # 파일을 닫고 파일과 연결된 파일 객체를 삭제함(flushing).
    # 특히 쓰고 나서의 close가 더 중요함!
    # 'python.txt' 파일에는 'KNOU'와 'python programmming'이라는 텍스트가 작성되어 있음.

    데이터 추가 (a모드, append 모드)

    파일의 끝에 데이터를 덧붙이는 작업

    - 파일 오픈 후 파일 포인터를 EoF(맨 뒤)로 이동

    - 존재하지 않는 파일을 a모드로 실행하면 write와 동일

    a_fp = open("python.txt", "a")
    # "python.txt" 파일을 이어쓰기 모드("a")로 열기
    # 이어쓰기 모드는 기존 파일의 끝 부분에서부터 작성을 시작함.
    # 파일과 연결된 파일 객체는 a_fp라는 변수에 할당됨.
    
    a_fp.write("\nby CS\n")
    # 파일 객체인 a_fp를 통해 "\nby CS\n"이라는 문자열을 파일에 작성함.
    # \n을 통해 다음 줄에 작성되도록 함.
    # "by CS"가 새로운 줄에 작성됨.
    
    a_fp.close()
    # 파일을 닫고 파일과 연결된 파일 객체를 삭제함. 
    # 파일을 닫으면 해당 파일에 대한 작업을 모두 마치게 됨.

    파일 읽고 쓰고 수정하는 프로그램

    'Khan.txt' 파일을 읽고 처리하는 프로그램을 작성하시오.

    - 모든 내용을 출력하시오.

    - 마지막에 '-칭기스 칸-'을 삽입하시오.

     

    깃헙에서 파일 다운 받기

    https://github.com/jaehwachung/Python-Programming

     

    GitHub - jaehwachung/Python-Programming: 한국방송통신대학교 컴퓨터과학과 파이썬 프로그래밍 기초

    한국방송통신대학교 컴퓨터과학과 파이썬 프로그래밍 기초. Contribute to jaehwachung/Python-Programming development by creating an account on GitHub.

    github.com

     

     

    Colab에서 실습하기 위해 다운로드한 파일을 Colab에 업로드하기

    khan.txt의 위치 : 개인 컴퓨터 바탕화면
    프로그램 코드 실행 위치 : 구글 서버 어딘가
    → Github 사이트에서 다운로드 된 파일을 우리가 여기 있는 코랩에다 올려줘야 됨.

     

    주의할 점은 85GB의 저장공간이 있다고 해서 개인 파일을 업로드하면 안 된다는 것임.

    이 창을 닫으면 구글에서 여기 있는 자원들을 삭제하고 가져가기 때문에 없어짐.

     

     

    Colab에 업로드한 파일의 접근 경로 알아보기

    점 3개 있는 부분 누르고 '경로 복사' 누르기

     

    실행하고 있는 ipynb 파일은 content 폴더 밑에 있음.

    텍스트 파일과 ipynb 파일이 같은 폴더에 있으므로 앞의 경로 생략 가능함.

     

    Khan_fp = open("/content/Khan.txt", "r")
    
    print(Khan_fp.read(10)) #1 0글자
    print(Khan_fp.readline()) # 새로운 개행문자 이전까지
    
    Khan_fp.close()

     

     

    텍스트 한 줄씩 읽고 출력하기

    Khan_fp = open("/content/Khan.txt", "r")
    
    for motto in Khan_fp.readlines() :
    # 한 줄 씩 리스트로 읽으라는 명령
        print(motto)
    
    Khan_fp.close()

    \n으로 한 줄 띄우고, print로 한 줄 띄워지면서 문장과 문장 사이에 새로운 줄이 생겨버림.

    → 문자열에 있는 개행문자를 지우면 됨.

     

     

    str메소드를 사용하여 개행문자를 지우기

    Khan_fp = open("/content/Khan.txt", "r")
    
    for motto in Khan_fp.readlines() :
    # 한 줄 씩 리스트로 읽으라는 명령
        print(motto.strip())
    # strip() 메서드는 문자열의 양쪽 끝에 있는 공백 문자와 개행 문자를 제거함. 
    # 별도의 인자로 개행 문자(\n)를 전달할 필요가 없음.
    
    Khan_fp.close()

     

     

    텍스트 파일 맨 뒤에 새로운 텍스트 삽입

    Khan_fp = open("Khan.txt", "a")
    
    Khan_fp.write("\n")
    Khan_fp.write(format("-칭기스 칸-", ">50s"))
    
    Khan_fp.close()

     

     

    파일을 다운로드하여 수정이 잘 되었는지 확인하기


    2. 파일의 활용

    시퀀스의 개념

    순서화된 값의 집합체를 저장할 수 있는 데이터 타입

    - 단일 식별자로 연속된 저장 공간 접근 수단 제공

    - 개별 원소의 값을 수정, 추가, 삭제 가능

    - 원소(element)의 나열을 저장할 수 있는 타입

    리스트, 세트, 투플, 딕셔너리 (특성에 따라 종류가 구분됨)

    순서화된 번지수가 아니라 영문 단어를 방 번호 대신 사용하고, 그 방 번호에 맞는 값들을 저장할 수 있는 구조를 찾고 싶음.


    딕셔너리의 이해

    · 딕셔너리(dictionary) : 키와 값의 쌍(pair)을 저장하는 시퀀스

    · 키(key) : 딕셔너리 내에서 고유한 식별자 역할, 값에 접근하기 위한 인덱스

    · 값(value) : 키와 연결된 데이터

    · 항목(item) : 딕셔너리에서 키와 값의 쌍

    번지도 사용자가 마음껏 결정할 수 있는 구조

    # 딕셔너리 생성
    student = {
        "name": "John",
        "age": 20,
        "grade": "A"
    }
    
    # 딕셔너리 값 접근
    print(student["name"])  # 출력: John
    print(student["age"])  # 출력: 20
    
    # 딕셔너리 값 변경
    student["grade"] = "B"
    print(student["grade"])  # 출력: B
    
    # 딕셔너리에 새로운 항목 추가
    student["major"] = "Computer Science"
    print(student)  # 출력: {'name': 'John', 'age': 20, 'grade': 'B', 'major': 'Computer Science'}
    
    # 딕셔너리에서 키와 값을 함께 순회
    for key, value in student.items():
        print(key, ":", value)
    # 출력:
    # name : John
    # age : 20
    # grade : B
    # major : Computer Science
    
    # 딕셔너리에서 키의 유무 확인
    if "name" in student:
        print("name 키가 존재합니다.")
    else:
        print("name 키가 존재하지 않습니다.")
    # 출력: name 키가 존재합니다.

    딕셔너리의 생성

    딕셔너리(+시퀀스)도 생성-수정-삭제 등의 Life Cycle이 존재함.

     

    구문형식

    세트, 리스트, 딕셔너리는 키 불가능

     

    빈 딕셔너리 생성 구문형식


    항목의 추가 및 삭제

     

    추가 구문형식

    삭제 구문형식


    딕셔너리 멤버

    멤버 설명
    keys(): tuple 포함된 모든 를 반환
    values(): tuple 포함된 모든 을 반환
    items(): tuple (키, 값) 형태의 투플로 모든 항목을 반환
    clear(): None 모든 항목을 삭제
    get(key): value 키에 해당하는 값을 반환
    pop(key): value 키에 해당하는 값을 반환하고 항목을 삭제
    popitem(): tuple 무작위로 한 (키, 값)을 반환하고 선택된 항목을 삭제

    딕셔너리 순회

    순회(traversal) : 시퀀스에서 각각의 항목을 순서대로 한 번씩만 방문하는 과정

     

    순회형식


    데이터 분석 프로그램

    "Hamlet_by_Shakespeare.txt" 파일에 포함된 단어가 출현한 횟수를 출력하는 프로그램을 작성하시오

    수업시간에 작성한 코드

    h_fp = open("/content/Hamlet_by_Shakespeare.txt", "r")
    # 파일을 읽기 모드(r)로 열고 파일 객체를 생성함.
    
    word_dict = dict()
    # 'word_dict'라는 이름의 빈 딕셔너리를 생성하여 단어의 빈도수를 저장할 것임.
    
    for line in h_fp.readlines():
        # 파일의 각 줄을 읽어들이면서 반복함.
    
        for word in line.strip().split():
            # 현재 줄에서 불필요한 개행문자를 제거(strip)하고 공백을 기준으로 단어를 분할(split)한 것을 반복함.
            
            word = word.strip(".,:?[]\'\":-").lower()
            # 단어에서 불필요한 문장 부호와 기호를 제거하고 모두 소문자로 변환하여 word에 저장함.
            # .strip()의 괄호 안에는 여러 특수 문자가 한꺼번에 들어갈 수 있음.
            # 이스케이프의 사용
            # -- \'는 작은따옴표(') 문자를 특수 문자로 해석
            # -- \"는 큰따옴표(") 문자를 특수 문자로 해석
    
            if word_dict.get(word) is not None:
                # .get() : 딕셔너리에서 특정 키의 값을 가져오는 메서드
                # word_dict라는 딕셔너리에서 word라는 값을 가져와서 존재한다면
    
                count = word_dict[word]
                # 해당 word를 count 변수 안에 저장함.
                
            else:
                count = 0
                # 딕셔너리에 새로운 단어로 등록되는 경우 초기 빈도수를 0으로 설정
    
            word_dict[word] = count + 1
            # 단어의 빈도수를 1 증가시킴.
    
    for key in word_dict:
        print("[" + key + "] " + str(word_dict[key]) + "회")
        # 단어와 해당 단어의 빈도수를 출력함.
    
    h_fp.close()
    # 파일 객체를 닫음.

     

    더 간단하고 깔끔하게 수정한 코드

    # 키 : 단어 / 값 : 단어가 반복된 횟수
    
    with open("/content/Hamlet_by_Shakespeare.txt", "r") as file:
        word_dict = {}
    # with문 : 파일을 열고 사용한 후 자동으로 파일을 닫아줌
    # as file : open() 함수로 열린 파일 객체를 'file'이라는 변수에 할당하는 역할을 함.
    # word_dict라는 딕셔너리를 생성함.
    
    # 반복문1 : 한 줄씩 분리하여 단어로 넣기
        for line in file:
            words = line.strip().split()
    # 파일을 한 줄씩 읽는 과정
    # words 안에 개행문자 제거(strip), 단어 단위로 분리(split)
    
    # 반복문2 : 단어의 문자 제거하고 소문자로 통일
            for word in words:
                word = word.strip(".,:?![]'\":--").lower()
    # 정리된 단어의 모음이 words 안에 있음.
    # words에서 쓸데없는 문자를 다 제거(strip)하고 소문자로 통일(lower)
    # --를 입력하면 -와 --모두 없어짐.
    
                # word가 딕셔너리에 있으면
                # word라는 키에 값 1을 하나 더 증가
                if word in word_dict:
                    word_dict[word] += 1
                
                # 없으면 word라는 키를 추가해서 넣고 값 1을 추가
                else:
                    word_dict[word] = 1
    
        for word, count in word_dict.items():
            print(f"[{word}] {count}회")
    # f-string 형식 사용
    # {word}는 현재 단어
    # {count}는 해당 단어의 빈도수
    # ['키'] '값'회 -> 이런식으로 출력됨.

    반복문을 2개로 나눠서 쓴 이유는 가독성을 높이고 유지보수를 잘하기 위함임.


    데이터 분석 프로그램 개선

    'Hamlet_by_Shakespeare.txt' 파일에 출현 횟수가 100 이상되는 단어와 출력 횟수를 정렬하여 출력하는 프로그램을 작성하시오.

    → 딕셔너리를 정렬할 필요가 있고, 반복횟수(해당 키의 값)이 100회 이상인 조건 걸기

    수업시간에 개선한 코드

    h_fp = open("/content/Hamlet_by_Shakespeare.txt", "r")
    # 파일을 읽기 모드(r)로 열고 파일 객체를 생성함.
    
    word_dict = dict()
    # 'word_dict'라는 이름의 빈 딕셔너리를 생성하여 단어의 빈도수를 저장할 것임.
    
    for line in h_fp.readlines():
        # 파일의 각 줄을 읽어들이면서 반복함.
    
        for word in line.strip().split():
            # 현재 줄에서 불필요한 개행문자를 제거(strip)하고 공백을 기준으로 단어를 분할(split)한 것을 반복함.
            
            word = word.strip(".,:?[]\'\":-").lower()
            # 단어에서 불필요한 문장 부호와 기호를 제거하고 모두 소문자로 변환하여 word에 저장함.
            # .strip()의 괄호 안에는 여러 특수 문자가 한꺼번에 들어갈 수 있음.
            # 이스케이프의 사용
            # -- \'는 작은따옴표(') 문자를 특수 문자로 해석
            # -- \"는 큰따옴표(") 문자를 특수 문자로 해석
    
            if word_dict.get(word) is not None:
                # .get() : 딕셔너리에서 특정 키의 값을 가져오는 메서드
                # word_dict라는 딕셔너리에서 word라는 값을 가져와서 존재한다면
    
                count = word_dict[word]
                # 해당 word를 count 변수 안에 저장함.
                
            else:
                count = 0
                # 딕셔너리에 새로운 단어로 등록되는 경우 초기 빈도수를 0으로 설정
    
            word_dict[word] = count + 1
            # 단어의 빈도수를 1 증가시킴.
    
    # sorted함수는 key값으로만 정렬이 가능함.
    # 그러나 value(빈도) 값으로 정렬해야 됨.
    # 그래서 key와 value를 바꿔주는 새로운 딕셔너리를 만들어야 함.
    word_r_dict = {v:k for (k,v) in word_dict.items()}
    # 리스트에서 가져와서 뒤바꿔서 딕셔너리로 만들라는 말
    # ex. 10회 the
    
    word_dict = {k:v for (v,k) in sorted(word_r_dict.items(), reverse=True)}
    # 정렬된 걸 다시 바꿔서 원상복귀, 내림차순 정렬로 설정함.
    # ex. the 10회, apple 8회
    
    for key in word_dict:
        # key의 value(빈도횟수)이 100이상이면 출력
        if word_dict[key] >= 100:
            print("[" + key + "] " + str(word_dict[key]) + "회")
        # 단어와 해당 단어의 빈도수를 출력함.
    
    h_fp.close()
    # 파일 객체를 닫음.

     

    더 간단하고 깔끔하게 수정한 코드

    with open("/content/Hamlet_by_Shakespeare.txt", "r") as file:
        word_dict = {}
    
        for line in file:
            words = line.strip().split()
    
            for word in words:
                word = word.strip(".,:?![]'\":--").lower()
    
                if word in word_dict:
                    word_dict[word] += 1
                else:
                    word_dict[word] = 1
    
        sorted_word_dict = sorted(word_dict.items(), key=lambda x: x[1], reverse=True)
    
    # lambda x: x[1]는 입력값 x를 받아서 x[1]을 반환하는 함수
    # x : sorted() 함수에서 정렬 기준이 되는 요소인 튜플
    # x[1] : 튜플의 두 번째 요소
    
    # sorted() 함수 안 정렬 기준인 'lambda x: x[1]', 즉 두 번째 요소(값, 빈도수)를 기준으로 정렬함.
    
    # sorted_word_dict는 word_dict의 항목을 빈도수에 따라 내림차순으로 정렬한 리스트
    
        for word, count in sorted_word_dict:
            if count >= 100:
                print(f"[{word}] {count}회")

    728x90