python/기초

(파이썬 기본문법) 파일 입출력

sillon 2022. 9. 26. 11:20
728x90
반응형

print 기능

>>> print("life" "is" "too short")

lifeistoo short

>>> print("life"+"is"+"too short")  #큰따옴표로 둘러싸인 문자열은 +연산과 동일

lifeistoo short

>>> print("life","is","too short")   #문자열 띄어쓰기는 콤마로 한다

 

life is too short

>>> for i in range(10):

print(i,end=',')   #개행없이 end에다가 끝문자를 지정 할 수 있다

 

 

 

0,1,2,3,4,5,6,7,8,9,

 

 

파일 열기 -> 파일 읽기/쓰기 -> 파일 닫기

 

#write.py

file = open('text.txt','w')

file.write('hello')

file.close()

 

실행결과:

text.txt 파일이 write.py 파일과 같은 위치에 생성되고 열어보면 hello가 적혀있음

 

#read.py

file = open('text.txt','r')

str = file.read()    #파일의 내용 전체를 문자열로 리턴한다

print(str)



file.close()

 

실행결과: 

hello

 

with~as

: open() 함수와 함께 with~as문을 사용하면 명시적으로 close() 함수를 호출하지 않아도 파일이 항상 닫힘.

 

with open(파일이름) as 파일객체:

    #코드블록

    #이곳에서 읽거나 쓰기를 한 후

    #그냥 코드를 빠져나가면 됨 close() 필요X

ex)

with open('text.txt','r') as file:

    str = file.read()

    print(str)

 

with

:컨텍스트 매니저(Context Manager)를 제공하는 함수여야 with문과 함께 사용할 수 있다

 컨텍스트 매니저는 __enter__(), __exit__() 메소드를 구현하고 있는 객체이다.

 with문은 컨텍스트 매니저를 획득한 후 코드블록의 실행을 시작할 때 컨텍스트 매니저의 __enter__() 메소드를 호출하고,

 코드블록이 끝날 때 __exit__()를 호출한다. 

 

ex) 

class open2(object):

    def __init__(self, path):
        print('initialized')
        self.file = open(path)

    def __enter__(self):
        print('entered')
        return self.file

    def __exit__(self, ext, exv, trb):

        print('exit')
        self.file.close()      #자원 해제 코드를 이 메소드에 구현해놓으면 자원을 해제하는 명시적인 코드가 없어도 안전하게 처리 가능
        return True

with open2('text.txt') as file:
    s = file.read()
    print(s)

실행결과:

initialized

entered

hello

exit

 

@contextmanager 데코레이터

:데코레이터는 __call__() 메소드를 구현하는 클래스이다. @contextmanger 데코레이터는 콜 메소드는 물론이고

 컨텍스트 매니저 규약을 준수하는데 필요한 __enter__(), __exit__() 메소드를 모두 갖고 있다.

 따라서 함수 하나를 만들고 이 데코레이터를 수식하면 컨텍스트 매니져의 구현이 마무리 된다

 

from contextlib import contextmanager   #모듈로부터 contextmanger를 반입

 

@contextmanger  #데코레이터로 함수 수식

def 함수명():

    #자원 획득
    try:
        #yield 자원   yield문을 통해 자원 반환: with문의 코드 블록이 시작될때 실행
    finally:
       #자원 해제    with문의 코드블록이 종료될 때 실행

 

@contextmanger로 수식되는 함수의 구조

1. try~finally 블록을 갖고 있음

2. try문에서는 yield문을 통해 자원을 반환하고 이때 yield문은 자신의 매개변수로 넘겨진 자원을 반환한 뒤

  임시적으로 현재 함수의 실행을 정지시킨다. with문의 코드블록 실행이 끝날 때 다시 실행

3. finally 블록에서 획득한 자원을 해제한다

 

즉 try 블록이 __enter__() 메소드의 역할을, finally블록이 __exit__()메소드의 역할을 수행한다

 

ex)

from contextlib import contextmanager

@contextmanager

def open3(path):
    print("opening file...")
    file = open(path)

    try:
        print("yielding file...")
        yield file

    finally:
        print("closing file...")
        file.close()

with open3("text.txt") as file:
    s = file.read()
    print(s)
 
실행결과: 
opening file...
yielding file...
hello
closing file...
 
open()
:open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open함수는 8개의 매개변수를 받아들이며, 하나의 필수 매개변수와 일곱 개의 선택적 매개변수로 이뤄져 있다
open()함수의 반환값은 파일 객체이다.
 
-file: 보통 실제 파일의 경로를 나타내는 문자열을 넘기지만, 이미 생성해 놓은 파일 객체의 파일 기술자를 넘겨 사용하기도 한다
      (파일 기술자는 운영체제에서 파일에 대해 애플리케이션에게 할당한 번호값을 의미)
 
-mode: 읽기/쓰기, 텍스트모드/바이너리모드 결정
        r=읽기용으로열기(기본), w=쓰기용으로열기(존재시 덮어씀), x=배타적생성모드로 열기(파일이 존재하면 IOError 예외)
       a=쓰기용으로 열기(이미 존재하는 경우 기존 내용에 덧붙임), b=바이너리모드, t=텍스트모드(기본값), +=읽기/쓰기용으로 파일 읽기
       open('test.txt'.'rt') ==  open('test.txt'.'r') ==  open('test.txt'.'t') ==  open('test.txt')
       조합 가능. '바이너리 모드로 쓰기' == 'wb'
 
-buffering: 버퍼링 정책을 지정. 0 입력하면 파일 입출력시 버퍼링을 수행X(단,바이너리에서만 사용가능)
            1을 입력하면 개행문자를 만날때까지 버퍼링하는 line 버퍼링을 수행(텍스트모드에서만 사용가능)
            임의의 값으로 버퍼의 크기를 지정하고 싶을땐 1보다 큰수를 입력한다 (비워둘시 시스템 지정 기본값 이용)
 
-encoding: 텍스트 모드에서만 사용 가능
 
-errors: encoding 변수와 관련이 있으며(텍스트모드에서만 사용가능) 인코딩과 디코딩을 수행할 때 발생하는 에러가
         어떻게 처리될지를 지정하는 문자열.
 strict=인코딩 에러 발생시 ValueError 예외를 일으키며 None과 같은 효과 , ignoe=에러 무시
 replace=기형적인 데이터가 있는곳에 대체 기호( ex: '?') 를 삽입
 surrogateescape= U+DC80~U+DCFF 사이에 있는 유니코드 사용자 자유 영역(PUA)의 잘못된 바이트를 코드 포인트로 나타냄
 xmlcharrefreplace=파일에 기록하려는 텍스트 안에 지정된 인코딩에서 지원되지 않는 문자를 &#NNN; 꼴의 XML 문자참조로
                     바꿔 기록한다 파일을 쓸때만 적용
 backslashreplace=파일에 텍스트를 기록할때만 사용하며 현재 인코딩에서 지원되지 않는 문자를 이스케이프 시퀸스로 바꿔 기록
 
-newline: 파일을 읽고 쓸때 줄바꿈을 어떻게 처리할지 나타냄. 역시 텍스트모드에서만 사용하며 None, '', \n, \r, \r\n 중 하나 입력
 파일을 읽을 때: newline이 None이면 \n, \r, \r\n를 모두 개행문자로 간주하며 이들을 \n으로 변환하여 읽기 메소드(read,readline)
                  에게 반환한다. '' 으로 설정되어 있으면 None 설정과 동일하지만 개행 문자의 변환을 수행하지 않는다(그대로 읽음)
                  이 외에 \n, \r, \r\n 중 하나를 입력하면 입력한 문자만을 개행 문자로 간주한다.
 파일을 쓸 때: None이면 \n, \r, \r\n 등 어떤 개행문자를 파일에 쓰려고 해도 시스템 기본 개행 문자로 변환되어 기록된다
               '' 또는 \n를 지정하는 경우엔 어떤 변환도 수행하지 않는다. \r, \r\n 등으로 지정하는 경우에는 모든 개행문자가
               지정한 개행문자로 변환되어 기록된다
 
-closefd :첫번째 매개변수 file에 파일의 경로가 아닌 파일 기술자가 입력됐을때만 사용한다 file변수에 파일 기술자를,
          closefd에 False를 입력하면 파일이 닫혀도 파일 기술자를 계속 열어둔 상태로 유지한다
 
-opener: 파일을 여는 함수를 직접 구현하고 싶을때 이용한다. opener에 구현한 함수 또는 호출가능 객체(__call__()메소드 구현한 객체)
          를 넘기면 된다. 이때 opener에 넘기는 함수/호출 가능 객체는 반드시 파일 기술자를 반환해야 한다
 
텍스트 파일 읽기/쓰기
:관련 메소드 소개
 
ex)
lines = ["we'll find a way we always have - Interstellar\n",
         "I'll find you and I'll kill you - Taken\n",
         "I'll be back - Terminator 2\n"]

with open('movie_quotes.txt', 'w') as file:
    for line in lines:
        file.write(line)
 
실행결과:
>type movie_quotes.txt      //cmd에서 type 명령어로 출력
 
we'll find a way we always have - Interstellar
I'll find you and I'll kill you - Taken
I'll be back - Terminator 2
 
writelines() :문자열을 담은 리스트를 파일에 쓰는 메소드
(문자열을 요소로 가지는 순서열 객체를 매개변수로 입력받아 해당 순서열 객체의 내용을 모두 파일에 기록한다)
lines = ["we'll find a way we always have - Interstellar\n",
         "I'll find you and I'll kill you - Taken\n",
         "I'll be back - Terminator 2\n"]

with open('movie_quotes.txt', 'w') as file:
    file.writelines(lines)
 
readline(), readlines()
readline(): 파일로부터 텍스트를 한 줄씩만 읽어온다. (기준은 개행 문자이며 newline을 건드리지 않았다면 \n,\r,\r\n모두 간주)  
readlines(): 파일로부터 문자열 리스트를 읽어들인다. 리스트의 각 요소는 개행 문자로 구분한 한 줄의 텍스트.    
 
ex)
with open('movie_quotes.txt', 'r') as file:
    line = file.readline()

    while line != '':   # readlne는 파일의 끝에 도달하면 ''를 반환하며 실제로 빈줄을 읽어 들인 경우엔 개행 문자를 반환한다
        print(line, end='')
        line = file.readline()
결과:
we'll find a way we always have - Interstellar
I'll find you and I'll kill you - Taken
I'll be back - Terminator 2
 
ex)
with open('movie_quotes.txt', 'r') as file:
    lines = file.readlines()
    line = ''
    for line in lines:
        print(line, end='')
결과: readline()과 동일
 
UTF-8 
:코드포인트(유니코드에서 문자에 부여되는 번호)의 크기에 따라 1바이트~4바이트까지 가변폭으로 인코딩(부호화)하므로
 1바이트로 표현 가능한 U+0000(십진수0)부터 U+007F(십진수 127)까지는 ASCII와 완벽하게 호환된다.
 
ex)
lines = ['안녕하세요?\n',
         'こんにちは\n',
         'Hello.\n']

with open('greetings_utf8.txt', 'w', encoding='utf-8') as file:
    for line in lines:
        file.write(line)
결과:
greetings_utf8.txt
안녕하세요?
こんにちは
Hello.
 
ex)
with open('greetings_utf8.txt', 'r', encoding='utf-8') as file:
    lines = file.readlines()
    line = ''
    for line in lines:
        print(line, end='')
 
결과:
안녕하세요?
こんにちは
Hello.
 
바이너리 파일
: '123' 문자열의 경우 텍스트 파일에 바로 저장하지만 123을 그대로 텍스트 파일에 저장할 수 없다 둘의 저장 구조가 다르기 때문이다
struct 모듈의 도움 없이는 바이너리 파일을 제대로 다루기가 까다롭다 struct 모듈은 일반 데이터 형식과 bytes 형식 사이의 변환을
수행하는 함수들을 담고 있다
 (파이썬의 데이터 타입을 C언어 형식의 이진 바이트열로 변경하거나 그 반대로 변경할때 이용)
 
ex)
>>> import struct
>>> packed = struct.pack('i', 123)  # 이 함수는 매개변수 i에 따라 4바이트 크기의 bytes 객체 packed를 준비하고 두번째 매개변수를
>>> for b in packed:                 # bytes에 복사해 넣는다
print(b)                         # bytes 객체 packed의 각 바이트에 있는 내용을 출력한다


123
0
0
0
>>> unpacked = struct.unpack('i',packed)
>>> unpacked
(123,)
>>> type(unpacked)    #unpacke() 함수는 튜플 형식을 반환한다
<class 'tuple'>
 
pack() :일반 데이터를 bytes 형식으로 변환
unpack(): bytes형식을 튜플 형식으로 변환
 
>>> struct.pack("i",2)
b'\x02\x00\x00\x00'      #10진수 2를 4바이트 크기의 int 타입으로 출력
>>> struct.pack("2i",1,2)
b'\x01\x00\x00\x00\x02\x00\x00\x00'   #10진수 1 과 2를 int타입으로 각각 출력
>>> struct.pack("il",1,2)
b'\x01\x00\x00\x00\x02\x00\x00\x00'   # int와 long타입으로 각기 다른 타입으로 혼용 가능
>>> struct.pack("<il",1,2)
b'\x01\x00\x00\x00\x02\x00\x00\x00' #리틀 엔디안(오른쪽->왼쪽순)
>>> struct.pack(">il",1,2)
b'\x00\x00\x00\x01\x00\x00\x00\x02'  #빅 엔디안(왼쪽->오른쪽)
>>> struct.pack("!il",1,2)
b'\x00\x00\x00\x01\x00\x00\x00\x02'   # 네트워크 방식(빅엔디안)
#그냥 "il" 의 경우는 사용하는 운영체제 따라 표현 방식이 정해진다 위에 방식들은 강제로 지정방법.
>>> u = struct.pack("il",1,2)
>>> struct.unpack("il",u)   # 튜플 데이터로 변환
(1, 2)
 
struct.pack(fmt, v1, v2, ...)
struct.unpack(fmt, buffer)
위 함수들의 첫번째 매개 변수는 이진 데이터의 구조를 나타내는 형식 문자열(Format String)이다.
 
형식문자열
=2f12si
= :바이트순서(Byte Order)
2f12si :데이터 구조 및 형식,크기
 
※바이트순서: CPU, 플랫폼마다 바이트를 정렬하는 순서가 다르며 x86 계열의 CPU는 하위 바이트부터 메모리에 배열하는
                리틀 엔디안 방식을 채용한다 (0x12345678 78 56 34 12) 반대로 ARM계열들은 빅 엔디안을 따른다
 
바이트순서를 지정하는 형식 문자
@ :시스템 바이트 순서를 따르며 크기와 바이트 정렬 또한 시스템을 따른다
= : 시스템 바이트 순서를 따르며 크기는 표준을 따르고 바이트 정렬은 하지 않는다
< :리틀 엔디안 순서이며 크기는 표준을 따르고 바이트 정렬은 하지 않는다
> :빅 엔디안 순서이며 크기는 표준을, 바이트 정렬은 하지 않는다
! :네트워크 바이트 순서(빅 엔디안과 동일) 이며 똑같이 크기는 표준을 따르고 바이트 정렬은 수행하지 않는다
 
바이트 순서 형식 문자를 지정하지 않은 경우에는 pack/unpack 함수는 @를 따른다. 앞서 사용한 문자열 'i'의 경우는 @가 생략
되었다고 보면 된다.
 
※바이트 정렬 :시스템에 따라 4 혹은 8바이트 등으로 묶음 처리를 하려는 경향이 있는데 예를 들어 1바이트, 4바이트의 필드로
                 구성된 자료구조가 있다면 컴퓨터가 1바이트 3바이트를 채워 4바이트를 만들어서 8바이트 크기로 다룰수 있다
                 다행히 pack()/unpack() 함수는 @가 들어오지 않는다면 바이트 정렬을 수행하지 않도록 구현되어 있다
 
2f12si :4바이트짜리 부동 소수형 2개, 크기가 12인 bytes형, 4바이트짜리 부호있는 정수형 1개로 이뤄진 자료 구조를 나타냄
f:float(4b) , s:string(char[]), i:int(4b)
 
 
ex) 부동소수형 변환
>>> import struct
>>> packed = struct.pack('f', 123.456)
>>> unpacked = struct.unpack('f', packed)
>>> unpacked
(123.45600128173828,)   #튜플이라 한개 요소만 있을 경우 뒤에 콤마 포함
>>> unpacked[0]
123.45600128173828
 
ex) 문자 변환 지시
>>> packed = struct.pack('12s', '대한민국'.encode())  # str.encode/ str.decode함수는 매개변수를 생략하면 utf-8을 지정
>>> unpacked = struct.unpack('12s', packed)  #문자열을 제대로 담을수 있을 정도로 충분한 크기 지정
>>> unpacked[0].decode()
'대한민국'
>>> type(unpacked[0])
<class 'bytes'>
>>> type(unpacked)
<class 'tuple'>
ex) 여러개의 데이터 변환 (2개의 8바이트 부동 소수형과 2개의 부호가 있는 정수형 데이터를 변환)
>>> import struct 
>>> packed = struct.pack('2d2i', *(123.456, 987.765, 123, 456)) #*연산자가 튜플, 리스트의 요소를 하나씩 분리해서 매개변수로 입력
>>> unpacked = struct.unpack('2d2i',packed)
>>> unpacked
(123.456, 987.765, 123, 456)
 
 
 
ex) 튜플의 리스트를 이진 파일에 기록하고 다시 읽어 들임
#binary_write.py
import struct

struct_fmt = '=16s2fi' # char[16], float[2], int
city_info = [
    #CITY,                Latitude,      Longitude,    Population
    ('서울'.encode(encoding='utf-8'), 37.566535, 126.977969, 9820000),
    ('뉴욕'.encode(encoding='utf-8'), 40.712784, -74.005941, 8400000),
    ('파리'.encode(encoding='utf-8'), 48.856614, 2.352222, 2210000),
    ('런던'.encode(encoding='utf-8'), 51.507351, -0.127758, 8300000)
    ]

with open('cities.dat', 'wb') as file:
    for city in city_info:
        file.write(struct.pack(struct_fmt, *city))   # *연산자가 순서열의 요소를 하나씩 분리해서 매개변수로 입력해준다
결과: cities.dat 파일 생성
#binary_read.py
import struct

struct_fmt = '=16s2fi' # char[16], float[2], int
struct_len = struct.calcsize(struct_fmt)   # 16+(4*3) = 28

cities = []
with open('cities.dat', 'rb') as file:
    while True:
        buffer = file.read(struct_len)
        if not buffer: break   #파일의  끝에 도달하면 while 루프 탈출
        city = struct.unpack(struct_fmt, buffer)
        cities.append(city)

    for city in cities:
        name = city[0].decode(encoding='utf-8').replace('\x00', '') #pack()하는 과정에서 문자를 할당하고 남은 공간에 채워진,
        print('City:{0}, Lat/Long:{1}/{2}, Population:{3}'.format(     # \x00를 디코딩한 후 빈 문자열로 다시 바꿔 넣는다
            name,
            city[1],
            city[2],
            city[3]))
결과:
 
City:서울, Lat/Long:37.56653594970703/126.97796630859375, Population:9820000
City:뉴욕, Lat/Long:40.71278381347656/-74.00594329833984, Population:8400000
City:파리, Lat/Long:48.85661315917969/2.352221965789795, Population:2210000
City:런던, Lat/Long:51.50735092163086/-0.1277579963207245, Population:8300000
>>> 
 
--------------------------------------추가------------------------------------------
 
 
 
 
파일 포인터 :파일에서 현재 어디까지 읽었는지 위치 관리
tell() :현재 파일 포인터의 위치를 알 수 있다. 파일포인터는 1바이트당 1글자를 의미하는데 한글은 글자당 2바이트이다.
seek() :전달된 인수로 파일 포인터를 이동시킴. 즉 파일의 내용을 읽어올 때 파일 포인터에서부터 읽어온다
 
>>> f = open("C:\\Users\\ok\AppData\\Local\\Programs\\Python\\Python35-32\\lern\\test.txt", "r")
>>> f.readline()
'1. 햄버거\n'
>>> f.tell()
11
>>> f.readline()
'2. 돈가스\n'
>>> f.tell()
22
>>> f.readline()
'1. 햄버거\n'
>>> f.tell()
11
>>> f.seek(0)
0
>>> f.readline()
'1. 햄버거\n'
>>> f.tell()
11
 
그림 복사
그림파일등을 복사할 때 바이너리 모드에서 수행한다
>>> img1 = open("hack.png","rb")    #원본을 바이너리, 읽기모드로 연다
>>> img2 = open("hack_2.png","wb")   #복사할 파일을 바이너리, 쓰기 모드로 생성한다
>>> img2.write(img1.read())       #원본 사진의 데이터를 read로 읽어서 복사할 파일에 write시켜준다
432459
>>> img1.close()                     #작업후 꼭 닫아준다
>>> img2.close()
데이터 형식
>>> x = "socket"
>>> type(x)
<class 'str'>     #파이썬 언어에서 문자열 데이터는 유니코드로 이뤄졌다
>>> y = b"socket"    #바이트 데이터는 문자열 앞에 b를 붙여 표현한다
>>> type(y)
<class 'bytes'>   #문자열을 다루는 데이터 타입에는 바이트 데이터라는 타입도 있으며 아스키 코드로 이뤄진 데이터이다
>>> type(x.encode())   #문자열 데이터 -> 바이트 데이터
<class 'bytes'>
>>> type(y.decode("utf-8"))  #바이트 데이터 -> 문자열 데이터
<class 'str'>
>>> z = bytearray(y) #바이트 데이터 타입을 바이트 배열 데이터 타입으로 변경 했다
>>> print(z)
bytearray(b'socket')
>>> type(z)
<class 'bytearray'>   #문자열 데이터 타입이나 바이트 데이터 타입에서는 문자 변경이 불가능하지만 바이트 배열 타입
>>> z[0] = ord("S")  #타입으로 변경하면 문자 변경이 가능해진다. ord는 영문자를 아스키코드로 변환해준다
>>> print(z)
bytearray(b'Socket')
>>> y = bytes(z)   # 바이트 배열 데이터 타입을 바이트 데이터 타입으로 변경할 수 있다
>>> print(y)
b'Socket'
>>> type(y)
<class 'bytes'>

reference

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=jkf941&logNo=220740467538 

728x90
반응형