python - 파일 읽기 / 쓰기
읽기 모드 : r, 쓰기 모드(기존파일 삭제) : w, 추가 모드(파일 생성 또는 추가) : a
read write add
점 2개 ( .. ) : 상대 경로
점 1개( . ) : 절대 경로
파이썬에서 파읽을 읽고 쓰는 방법에 대해 알아본다.
여기서 상대경로라 함은 현재 위치한 파일에서 상위파일로 넘어간 후 상위파일에서 찾는 것을 말하고
절대경로는 해당 위치의 파일에서 근처 파일을 찾는 것을 말한다.
이건 뭐 상식이니 대충 정리하고 넘어간다.
파일 읽기
파일 읽기를 할 때는 open() 함수를 사용한다. open 함수는 파이썬 내장 함수 builtins 모듈에 포함된 함수이다.
import builtins
print(dir(builtins))
이렇게 print 해보면
'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'
이렇게 많은 빌트인 함수들이 있다.
이 안에 open 함수가 들어있는 것을 볼 수 있다.
open 함수 사용법은 아래와 같다.
f(변수 f로 인스턴스 할당) = open('./열기원하는해당파일위치로 가서 파일이름 적고/','r')
ex_
f = open('./resource/review.txt'.'r') # r은 read를 의미 w 적고 할당하면 write 기능을 수행함.
'r' 을 적지않으면 오류가 발생한다. 이후, 아래 코드를 통해 불러온다.
content = f.read()
print(content)
print(dir(f)) # 오픈 함수 안에 많은 메소드가 있다.
f 에 할당된 open 함수에는 많은 메소드가 있다. r w a 가 바뀐다고 아래 항목이 바뀌지는 않는다.
'_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines
정리하면 파이썬 (인터프리트 프로그램 (해석 실행 프로그램)) 안에 많은 내장 함수가 있고, 내장 함수 중에 open() 함수가 있고 그 안에 위와 같은 메소드 들이 있다. (메소드는 함수 안에 정의된 동적인 속성들이다.)
위에서 변수 f 로 인스턴스 할당이라는 것은 파이썬의 특징 중 하나인 모든 것을 객체로 보기 때문이다.
파이썬은 함수를 일급객체로 보는데 이 뜻은 프로그래밍 설계에서 매개변수로 넘길 수 있고 함수가 반환할 수 있으며
변수에 할당이 가능한 객체를 가리키는 용어이다.
따라서 함수도 객체이므로 인스턴스 할당이라는 말이 가능하다.
open 함수(객체)를 변수 f 에 인스턴스화 할당 - 자바와 같은 언어와 다른 점이다.
파이썬에서는 클래스가 나와야만 인스턴스라는 말을 쓸 수 있는 줄 알면 안된다.
※ 아무튼 왜 인스턴스 할당이라는 건지 궁금했는데 해결됐다.
위에서 open 함수 중 read() 메소드를 사용 후에는 반드시 close() 메소드를 사용해서 리소스를 반환해야한다.
컴퓨터에서 사용할 수 있는 리소스는 제한적이다. 따라서 사용한(acquired) 리소스는 종료해주는 것(released)이 중요
그렇지 않다면 프로그램이 종료된 이후에도 사용한 리소스가 계속 열려있는 resource leak현상이 발생한다.
f = open('./resource/review.txt','r') # open 함수에서 f로 인스턴스 할당
content = f.read()
print(content)
print(dir(f)) # 오픈 함수 안에 많은 메소드가 있다.
# 반드시 close 리소스 반환
f.close()
파이썬의 파일 핸들러는 내부적으로 ContextManager 프로토콜을 따르도록 설계되어 있다.
따라서 다음과 같이 with 구문을 통해서 액세스할 수 있다.
with open('./resource/review.txt','r') as f:
c = f.read()
print(c)
print(list(c))
print(iter(c))
with 구문에 관한 설명은 아래 접힌 글을 참조한다.
이 코드는 open() ~ FileHandle.close()에 이르는 구간이 with 구문에 의한 블록으로 감싸지고 명시적으로 열었던 파일 핸들을 닫는 코드를 작성하지 않고 있다. 이는 open() 함수를 통해서 생성되는 파일 핸들이 컨텍스트 매니저 프로토콜을 따르고 있기 때문이다. 컨텍스트 매니저 객체들은 with 문과 함께 사용되었을 때 다음과 같은 동작을 처리할 수 있다.
- 객체가 생성된 후 with 블럭에 진입하면서 미리 지정된 특정한 작업을 수행한다.
- with 블럭을 떠나는 시점에 미리 지정된 특정한 작업을 수행한다.
보통의 경우에는 시작 지점에는 특정한 동작을 수행하는 일이 별로 없을 것이다. 파일 객체의 경우에 with 블럭을 떠나는 시점에 자신을 닫는 동작을 수행하도록 미리 정해져 있고, 따라서 with 문 내에서 예외가 발생해서 실행이 중단되거나, 함수 내에서 리턴하는 동작을 만나더라도 파일 객체는 닫기를 수행하는 기회를 보장받게 된다.
또한 with 문을 쓸 때의 장점은 위의 예에서 보듯이 with 블럭 자체가 중첩이 가능하다는 점이다. 따라서 어느 한 지점에서 문제가 발생하여 모든 블럭을 빠져나와야 하는 상황에서도 가장 하위의 블럭에서부터 파일을 순차적으로 닫고 안전하게 리소스를 회수할 수 있게 된다. 또한 제어 로직에서의 전후관계를 따로 추적하지 않아도 되어 그만큼 코드가 간결해지는 장점도 있다.
컨텍스트 매니저
컨텍스트 매니저는 with 구문에 쓰일 수 있는 객체의 타입이며, (파이썬의 문법 상으로는 명시적으로 지원하지는 않지만) context manager2 프로토콜을 준수한다. 컨텍스트 매니저는 다음 두 개의 메소드를 정의하고 있는 것으로 간주한다.
- __enter__(self) : with 문에 진입하는 시점에 자동으로 호출된다.
- __exit__(self, type, value, traceback) : with 문이 끝나기 직전에 자동으로 호출된다.
__exit__() 메소드가 받는 세 개의 인자는 해당 객체와 연관된 컨텍스트 내에서 예외가 발생되었을 때, 해당 예외에 관한 정보이다. 예외없이 with 구문이 종료되었다면 이 세 인자는 모두 None이 될 것이다.
with ~ as 구문은 close() 메소드 사용을 못하게 되는 참사를 막는데 사용된다. for 과 같이 with 구문도 일종의 블록이다.
아무튼 파일이 읽히고 나서 다 사용된 뒤에는 자동으로 닫아준다는 것이다.
with open('./resource/review.txt','r') as f:
c = f.read()
print(c)
print(list(c))
print(iter(c))
위에서 list로 c를 변환해주면
'a', 'l', ' ', 'f', 'o', 'r', 'c', 'e', 's', ' ', 'f', 'r', 'o', 'm', ' ', 'w', 'i', 't', 'h', 'i', 'n', ' ', 't', 'h', 'e', 'i', 'r', ' ', 'v', 'e', 'r', 'y', ' ', 'o', 'w', 'n', ' ', 'u', 'n', 'i', 't', 's', '.
이런식으로 output 출력 된다.
iter(c) - Iterator 는 next() 메소드로 데이터를 순차적으로 호출 가능한 object 이다. 만약 next() 로 다음 데이터를 불러 올수 없을 경우 (가장 마지막 데이터인 경우) StopIteration exception을 발생시킨다.
print(iter(c)) 를 하면 for 문을 사용할 수 있다는 뜻이다.
with open('./resource/review.txt','r') as f:
for b in f:
print(b.strip())
위와 같이 f가 iterator 기 때문에 for 문을 사용하여 출력이 가능하고 한라인 단위로 출력하는 것이 가능하다.
strip 함수의 경우 문자열 양쪽 공백과 줄바꿈을 지워준다.
with open('./resource/review.txt','r') as f:
content = f.read()
print(">",content)
content = f.read() # 내용 없음
print(">",content)
위와 같은 경우는 한번 읽고 나면 '커서'의 위치가 끝에 있기 때문에 한번 더 읽으려고 하면 내용이 없다고 출력된다.
with open('./resource/review.txt','r') as f:
line = f.readline()
# print(line)
while line:
line = f.readline()
print(line, end='#####')
readline 메소드는 한줄 한줄 읽어온다. while 문으로 조건 line이 false 즉 커서의 위치가 끝에 닿으면 while 문을 종료한다. (iterator 이기 때문이다)
with open('./resource/review.txt','r') as f:
contents = f.readlines() # list 형태로 가져옴
print(contents)
for d in contents :
print(d, end = ' ******* ')
readlines() 메소드는 엔터를 기준으로 줄바꿈까지 한줄씩 리스트형태로 묶어서 가져온다.
따라서 이번에는 for in 구문을 사용하여 list 형태인 contents 에서 인덱싱된 데이터를 차례대로 가져와서
프린트 해주고 끝나면 끝에 **** 을 달아 줄 수 있다.
score = []
with open('./resource/score.txt', 'r') as f:
for line in f:
score.append(int(line))
print(score)
print('Average : {:6.3}'.format(sum(score)/len(score)))
score 빈 배열을 할당하고, append 함수를 사용한다.
append를 사전에서 검색해 보면 "덧붙이다, 첨부하다"라는 뜻이 있다. 이 뜻을 안다면 다음 예가 바로 이해될 것이다. append(x)는 리스트의 맨 마지막에 x를 추가하는 함수이다.
score.txt 파일에서는 무조건 str 형태로 가져오기 때문에 int 정수 형태로 변환 후 넣어준다.
그럼 배열이 완성된다.
이후 평균값을 구해준다. { :6.3 }(6자리고 소숫점 셋째자리까지 나와라는 뜻) 슬라이싱은 추후 추가로 공부할 예정이다.
.format 함수도 추가로 공부할 예정이다.
sum 과 len 으로 배열을 합치고 배열내 인덱스 갯수만큼 나누어준다.
파일 쓰기
with open('./resource/text1.txt','w') as f :
f.write('Niceman!\n')
print(dir())
write 모드로 'w' 바꿔준 뒤
text1.txt 파일을 만들어서 그안에 Niceman! 이라는 문자열을 넣어준다.
with open('./resource/text1.txt','a') as f :
f.write('GoodMan!\n')
add 모드로 'a' 해준 뒤 GoodMan 이라고 쓰면 뒤에 붙어서 문자열이 text1.txt 파일에 들어가게 된다.
from random import randint
with open('./resource/text2.txt','w') as f :
for cnt in range(6) :
f.write(str(randint(1,50)))
f.write('\n')
random 이라는 파일로부터 randint 함수를 가져온다. (파이썬에서 미리 만들어놓은 필트인? 패키지다.)
1에서 50까지 랜덤한 정수를 적고 6번 반복 할때마다 줄바꿈해서 write 한다.
with open('./resource/text3.txt','w') as f :
list =['Kim\n','Park\n','Cho\n']
f.writelines(list)
writelines : 리스트 -> 파일로 저장한다.
with open('./resource/text4.txt','w') as f :
print('test contents1!', file=f)
print('test contents2!', file=f)
위의 경우 print() 함수를 통해 직접 적은 test contents1! test contents1! 들을 file= 을 이용하여 파일에다가 직접 적을 수 있다. (콘솔엔 찍히지 않는다)