5. Files and I/O

5.1. Reading and Writing Text Data

P : 인코딩에 상관없이 텍스트를 읽고 쓰고 싶다.

S : open() 함수를 사용한다. 읽기는 rt, 쓰기는 wt, 덧붙이기는 at이다. 인코딩은 encoding= 인자로 줄 수 있다. 플랫폼간 개행 문자 변환을 억제하려면 newline=”을 주면 된다. 인코딩 에러는 기본적으로는 처리되지 않지만, errors=로 무시하거나 인코딩이 맞지 않는 글자들을 자동적으로 변환하게 할 수 있다.

# Read the entire file as a single string
with open('somefile.txt', 'rt') as f:
    data = f.read()

# Iterate over the lines of the file
with open('somefile.txt', 'rt') as f:
    for line in f:
        # process line
        ...

# Write chunks of text data
with open('somefile.txt', 'wt') as f:
    f.write(text1)
    f.write(text2)
    ...

# Redirected print statement
with open('somefile.txt', 'wt') as f:
    print(line1, file=f)
    print(line2, file=f)
    ...

with open('somefile.txt', 'rt', encoding='latin-1') as f:
     ...

>>> # Newline translation enabled (the default)
>>> f = open('hello.txt', 'rt')
>>> f.read()
'hello world!\n'

>>> # Newline translation disabled
>>> g = open('hello.txt', 'rt', newline='')
>>> g.read()
'hello world!\r\n'
>>>

>>> # Replace bad chars with Unicode U+fffd replacement char
>>> f = open('sample.txt', 'rt', encoding='ascii', errors='replace')
>>> f.read()
'Spicy Jalape?o!'
>>> # Ignore bad chars entirely
>>> g = open('sample.txt', 'rt', encoding='ascii', errors='ignore')
>>> g.read()
'Spicy Jalapeo!'
>>>

5.2. Printing to a File

P : 파일에 출력하고 싶다.

S : print() 함수 인자에 file=을 주면 된다.

with open('somefile.txt', 'wt') as f:
    print('hello world', file=f)

5.3. Printing with a Different Separator or Line Ending

P : 구분자와 개행 문자를 다르게 해서 출력하고 싶다.

S : print() 함수 인자에 sep=, end=을 주면 된다.

>>> print('ACME', 50, 91.5)
ACME 50 91.5
>>> print('ACME', 50, 91.5, sep=',')
ACME,50,91.5
>>> print('ACME', 50, 91.5, sep=',', end='!!\n')
ACME,50,91.5!!
>>>

5.4. Reading and Writing Binary Data

P : 바이너리 데이터를 읽고 쓰고 싶다.

S : open() 함수를 쓰고 읽기는 rb, 쓰기는 wb로 처리한다. 바이너리 파일로부터 텍스트를 읽고 쓰려면 인코딩을 따로 해 줘야 한다.

# Read the entire file as a single byte string
with open('somefile.bin', 'rb') as f:
    data = f.read()

# Write binary data to a file
with open('somefile.bin', 'wb') as f:
    f.write(b'Hello World')

with open('somefile.bin', 'rb') as f:
    data = f.read(16)
    text = data.decode('utf-8')

with open('somefile.bin', 'wb') as f:
    text = 'Hello World'
    f.write(text.encode('utf-8'))

파이썬의 바이너리 입출력은 배열이나 C 구조체 등도 별도의 형변환 없이 읽고 쓸 수 있다. 이는 버퍼에 직접 읽고 쓸 수 있는 강력한 기능을 제공하지만 플랫폼 의존적이며 정렬 제한 등을 맞춰줘야 하므로 주의가 필요하다.

import array
nums = array.array('i', [1, 2, 3, 4])
with open('data.bin','wb') as f:
    f.write(nums)

>>> a = array.array('i', [0, 0, 0, 0, 0, 0, 0, 0])
>>> with open('data.bin', 'rb') as f:
...     f.readinto(a)
...
16
>>> a
array('i', [1, 2, 3, 4, 0, 0, 0, 0])
>>>

5.5. Writing to a File That Doesn’t Already Exist

P : 파일 시스템에 존재하지 않을 경우에만 파일을 읽고 쓰고 싶다.

S : open() 함수에 인자를 xt, xb로 주면 된다.

>>> with open('somefile', 'wt') as f:
...     f.write('Hello\n')
...
>>> with open('somefile', 'xt') as f:
...     f.write('Hello\n')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'somefile'
>>>

5.6. Performing I/O Operations on a String

P : 문자열에 대한 입출력을 수행하는 파일과 비슷한 오브젝트를 만들고 싶다.

S : io.StringIO(), io.BytesIO()를 쓰면 된다.

>>> s = io.StringIO()
>>> s.write('Hello World\n')
12
>>> print('This is a test', file=s)
15
>>> # Get all of the data written so far
>>> s.getvalue()
'Hello World\nThis is a test\n'
>>>

>>> # Wrap a file interface around an existing string
>>> s = io.StringIO('Hello\nWorld\n')
>>> s.read(4)
'Hell'
>>> s.read()
'o\nWorld\n'
>>>

5.7. Reading and Writing Compressed Datafiles

P : gzip, bz2 압축된 파일을 읽고 쓰고 싶다.

S : open()으로 할 수 있다. 쓸 때는 압축 레벨을 compresslevel= 인자로 특정할 수 있다.

# gzip compression
import gzip
with gzip.open('somefile.gz', 'rt') as f:
    text = f.read()

# bz2 compression
import bz2
with bz2.open('somefile.bz2', 'rt') as f:
    text = f.read()

# gzip compression
with gzip.open('somefile.gz', 'wt') as f:
    f.write(text)

# bz2 compression
with bz2.open('somefile.bz2', 'wt') as f:
    f.write(text)

5.8. Iterating Over Fixed-Sized Records

P : 파일을 라인 단위로 읽고 쓰는 대신 고정된 크기의 레코드 단위로 읽고 쓰고 싶다.

S : iter()와 functools.partial()을 쓰면 된다.

from functools import partial

RECORD_SIZE = 32

with open('somefile.data', 'rb') as f:
    records = iter(partial(f.read, RECORD_SIZE), b'')
    for r in records:
        ...

5.9. Reading Binary Data into a Mutable Buffer

P : 바이너리 데이터를 변형 가능한 버퍼에 중간 복사 없이 읽고 쓰면서, 제자리에서의 데이터의 변형과 파일로부터의 출력도 수행하고 싶다.

S : readinto()를 쓴다. 이는 read()와 다르게 중간 복사를 하지 않는다. readinto()에서는 리턴값 (몇 바이트를 읽었는지)를 항상 체크하는 것이 좋다. 읽은 데이터 양이 버퍼보다 작으면 뭔가 잘못된 것이다.

import os.path

def read_into_buffer(filename):
    buf = bytearray(os.path.getsize(filename))
    with open(filename, 'rb') as f:
         f.readinto(buf)
    return buf

>>> # Write a sample file
>>> with open('sample.bin', 'wb') as f:
...      f.write(b'Hello World')
...
>>> buf = read_into_buffer('sample.bin')
>>> buf
bytearray(b'Hello World')
>>> buf[0:5] = b'Hallo'
>>> buf
bytearray(b'Hallo World')
>>> with open('newsample.bin', 'wb') as f:
...     f.write(buf)
...
11
>>>

5.10. Memory Mapping Binary Files

P : 바이너리 파일을 변형 가능한 바이트 배열로 매핑하면서, 원소에 대한 무작위 접근이나 제자리 수정을 허용하고 싶다.

S : mmap 모듈을 사용한다. 읽기 전용으로 쓰려면 mmap.ACCESS_READ, 수정은 하고 싶지만 원본 파일에 반영시키고 싶지 않다면 mmap.ACCESS_COPY를 사용한다.

import os
import mmap

def memory_map(filename, access=mmap.ACCESS_WRITE):
    size = os.path.getsize(filename)
    fd = os.open(filename, os.O_RDWR)
    return mmap.mmap(fd, size, access=access)

>>> m = memory_map('data')
>>> len(m)
1000000
>>> m[0:10]
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> m[0]
0
>>> # Reassign a slice
>>> m[0:11] = b'Hello World'
>>> m.close()

>>> # Verify that changes were made
>>> with open('data', 'rb') as f:
...      print(f.read(11))
...
b'Hello World'
>>>

메모리 매핑은 파일 전체를 메모리로 로딩하지 않는다. 그 대신에 OS가 파일 내용에 대한 가상 메모리를 잡는다.

5.11. Manipulating Pathnames

P : 파일/디렉토리 경로명을 다루고 싶다.

S : os.path 모듈을 사용한다.

>>> import os
>>> path = '/Users/beazley/Data/data.csv'

>>> # Get the last component of the path
>>> os.path.basename(path)
'data.csv'

>>> # Get the directory name
>>> os.path.dirname(path)
'/Users/beazley/Data'

>>> # Join path components together
>>> os.path.join('tmp', 'data', os.path.basename(path))
'tmp/data/data.csv'

>>> # Expand the user's home directory
>>> path = '~/Data/data.csv'
>>> os.path.expanduser(path)
'/Users/beazley/Data/data.csv'

>>> # Split the file extension
>>> os.path.splitext(path)
('~/Data/data', '.csv')
>>>

5.12. Testing for the Existence of a File

P : 파일이나 디렉토리가 있는지 테스트하고 싶다.

S : os.path 모듈을 사용한다. 단 퍼미션은 있어야 한다.

>>> import os
>>> os.path.exists('/etc/passwd')
True
>>> os.path.exists('/tmp/spam')
False
>>> # Is a regular file
>>> os.path.isfile('/etc/passwd')
True
>>> # Is a directory
>>> os.path.isdir('/etc/passwd')
False
>>> # Is a symbolic link
>>> os.path.islink('/usr/local/bin/python3')
True
>>> # Get the file linked to
>>> os.path.realpath('/usr/local/bin/python3')
'/usr/local/bin/python3.3'
>>> os.path.getsize('/etc/passwd')
3669
>>> os.path.getmtime('/etc/passwd')
1272478234.0
>>> import time
>>> time.ctime(os.path.getmtime('/etc/passwd'))
'Wed Apr 28 13:10:34 2010'

5.13. Getting a Directory Listing

P : 디렉토리 내 파일 목록을 얻고 싶다.

S : os.listdir()을 사용한다. 특정 확장자명 파일이나 특정 조건을 만족하는 파일명의 파일을 검색하는 것도 가능하다. 이 때 파일명의 인코딩에 주의하라.

import os
names = os.listdir('somedir')

import os.path
# Get all regular files
names = [name for name in os.listdir('somedir')
         if os.path.isfile(os.path.join('somedir', name))]

# Get all dirs
dirnames = [name for name in os.listdir('somedir')
            if os.path.isdir(os.path.join('somedir', name))]

pyfiles = [name for name in os.listdir('somedir')
           if name.endswith('.py')]

import glob
pyfiles = glob.glob('somedir/*.py')

from fnmatch import fnmatch
pyfiles = [name for name in os.listdir('somedir')
           if fnmatch(name, '*.py')]

5.14. Bypassing Filename Encoding

P : 파일명 인코딩에 신경쓰지 않고 파일 입출력을 하고 싶다.

S : 파일명을 바이트 문자열로 건네주면 된다.

>>> # Wrte a file using a unicode filename
>>> with open('jalape\xf1o.txt', 'w') as f:
...     f.write('Spicy!')
...
6
>>> # Directory listing (decoded)
>>> import os
>>> os.listdir('.')
['jalapeño.txt']

>>> # Directory listing (raw)
>>> os.listdir(b'.')        # Note: byte string
[b'jalapen\xcc\x83o.txt']

>>> # Open file with raw filename
>>> with open(b'jalapen\xcc\x83o.txt') as f:
...     print(f.read())
...
Spicy!
>>>

5.15. Printing Bad Filenames

P : 깨진 파일명을 에러 없이 출력하고 싶다.

S : 파일명을 인코딩해서 사용한다.

def bad_filename(filename):
    temp = filename.encode(sys.getfilesystemencoding(), errors='surrogateescape')
    return temp.decode('latin-1')

>>> import os
>>> files = os.listdir('.')
>>> files
['spam.py', 'b\udce4d.txt', 'foo.txt']
>>> for name in files:
...     try:
...             print(name)
...     except UnicodeEncodeError:
...             print(bad_filename(name))
...
spam.py
bäd.txt
foo.txt

5.16. Adding or Changing the Encoding of an Already Open File

P : 이미 열린 파일을 닫지 않고 인코딩을 변경 또는 추가하고 싶다.

S : io.TextIOWrapper()를 사용한다. 이는 그리 안전한 방법은 아니다.

import urllib.request
import io

u = urllib.request.urlopen('http://www.python.org')
f = io.TextIOWrapper(u,encoding='utf-8')
text = f.read()
>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='latin-1')
>>> sys.stdout.encoding
'latin-1'
>>>

5.17. Writing Bytes to a Text File

P : 텍스트 모드로 열린 파일에 바이트 문자열을 쓰고 싶다.

S : 내부 버퍼에 쓴다.

>>> import sys
>>> sys.stdout.write(b'Hello\n')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be str, not bytes
>>> sys.stdout.buffer.write(b'Hello\n')
Hello
5
>>>

5.18. Wrapping an Existing File Descriptor as a File Object

P : 이미 열린 OS단의 입출력 채널 (파일, 소켓 등)에 파이썬의 파일 오브젝트를 래핑하고 싶다.

S : open()의 첫 번째 인자에 저수준 파일 수식자를 넘겨주면 된다.

# Open a low-level file descriptor
import os
fd = os.open('somefile.txt', os.O_WRONLY | os.O_CREAT)

# Turn into a proper file
f = open(fd, 'wt')
f.write('hello world\n')
f.close()

# Create a file object, but don't close underlying fd when done
f = open(fd, 'wt', closefd=False)
...

5.19. Making Temporary Files and Directories

P : 프로그램이 실행될 때 만들고 종료될 때 소멸되는 임시 파일을 만들고 싶다.

S : tempfile 모듈을 사용한다.

from tempfile import TemporaryFile

with TemporaryFile('w+t') as f:
     # Read/write to the file
     f.write('Hello World\n')
     f.write('Testing\n')

     # Seek back to beginning and read the data
     f.seek(0)
     data = f.read()

# Temporary file is destroyed

f = TemporaryFile('w+t')
# Use the temporary file
...
f.close()
# File is destroyed

from tempfile import NamedTemporaryFile

with NamedTemporaryFile('w+t') as f:
    print('filename is:', f.name)
    ...

# File automatically destroyed

from tempfile import TemporaryDirectory
with TemporaryDirectory() as dirname:
     print('dirname is:', dirname)
     # Use the directory
     ...
# Directory and all contents destroyed

5.20. Communicating with Serial Ports

P : 시리얼 포트를 통해 입출력을 하고 싶다.

S : 파이썬의 빌트인 기능을 이용할 수도 있지만 pySerial 패키지를 쓰는 것이 낫다.

import serial
ser = serial.Serial('/dev/tty.usbmodem641',  # Device name varies
                     baudrate=9600,
                     bytesize=8,
                     parity='N',
                     stopbits=1)

ser.write(b'G1 X50 Y50\r\n')
resp = ser.readline()

5.21. Serializing Python Objects

P : 파이썬 객체를 바이트 스트림으로 직렬화하고 싶다.

S : pickle 모듈을 사용한다. pickle.load()를 신뢰할 수 없는 데이터에 절대 사용하지 말라.

import pickle

data = ...   # Some Python object
f = open('somefile', 'wb')
pickle.dump(data, f)

s = pickle.dumps(data)

# Restore from a file
f = open('somefile', 'rb')
data = pickle.load(f)

# Restore from a string
data = pickle.loads(s)

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중