7. 입력과 출력
**************

프로그램의 출력을 표현하는 여러 가지 방법이 있습니다; 사람이 일기에 적
합한 형태로 데이터를 인쇄할 수도 있고, 나중에 사용하기 위해 파일에 쓸
수도 있습니다. 이 장에서는 몇 가지 가능성을 논합니다.


7.1. 장식적인 출력 포매팅
=========================

So far we've encountered two ways of writing values: *expression
statements* and the "print" statement.  (A third way is using the
"write()" method of file objects; the standard output file can be
referenced as "sys.stdout". See the Library Reference for more
information on this.)

Often you'll want more control over the formatting of your output than
simply printing space-separated values.  There are two ways to format
your output; the first way is to do all the string handling yourself;
using string slicing and concatenation operations you can create any
layout you can imagine.  The string types have some methods that
perform useful operations for padding strings to a given column width;
these will be discussed shortly.  The second way is to use the
"str.format()" method.

"string" 모듈은 "Template" 클래스를 포함하는데, 값을 문자열에 치환하는
또 다른 방법을 제공합니다.

물론, 한가지 질문이 남아있습니다; 값을 어떻게 문자열로 변환하는가? 다
행히도, 파이썬은 어떤 종류의 값이라도 문자열로 변환하는 방법을 갖고 있
습니다; 그 값을 "repr()" 나 "str()" 함수로 전달하세요.

The "str()" function is meant to return representations of values
which are fairly human-readable, while "repr()" is meant to generate
representations which can be read by the interpreter (or will force a
"SyntaxError" if there is no equivalent syntax).  For objects which
don't have a particular representation for human consumption, "str()"
will return the same value as "repr()".  Many values, such as numbers
or structures like lists and dictionaries, have the same
representation using either function.  Strings and floating point
numbers, in particular, have two distinct representations.

몇 가지 예를 듭니다:

   >>> s = 'Hello, world.'
   >>> str(s)
   'Hello, world.'
   >>> repr(s)
   "'Hello, world.'"
   >>> str(1.0/7.0)
   '0.142857142857'
   >>> repr(1.0/7.0)
   '0.14285714285714285'
   >>> x = 10 * 3.25
   >>> y = 200 * 200
   >>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
   >>> print s
   The value of x is 32.5, and y is 40000...
   >>> # The repr() of a string adds string quotes and backslashes:
   ... hello = 'hello, world\n'
   >>> hellos = repr(hello)
   >>> print hellos
   'hello, world\n'
   >>> # The argument to repr() may be any Python object:
   ... repr((x, y, ('spam', 'eggs')))
   "(32.5, 40000, ('spam', 'eggs'))"

여기 제곱수와 세제곱수의 표를 쓰는 두 가지 방법이 있습니다:

   >>> for x in range(1, 11):
   ...     print repr(x).rjust(2), repr(x*x).rjust(3),
   ...     # Note trailing comma on previous line
   ...     print repr(x*x*x).rjust(4)
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

   >>> for x in range(1,11):
   ...     print '{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x)
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

(Note that in the first example, one space between each column was
added by the way "print" works: by default it adds spaces between its
arguments.)

이 예는 문자열 객체의 "str.rjust()" 메서드를 시연하는데, 왼쪽에 스페이
스를 채워서 주어진 폭으로 문자열을 우측 줄 맞춤합니다. 비슷한 메서드
"str.ljust()" 와 "str.center()" 도 있습니다. 이 메서드들은 어떤 것도
출력하지 않습니다, 단지 새 문자열을 돌려줍니다. 입력 문자열이 너무 길
면, 자르지 않고, 변경 없이 그냥 돌려줍니다; 이것이 칼럼 배치를 엉망으
로 만들겠지만, 보통 값에 대해 거짓말을 하게 될 대안보다는 낫습니다. (
정말로 잘라내기를 원한다면, 항상 슬라이스 연산을 추가할 수 있습니다,
"x.ljust(n)[:n]" 처럼.)

다른 메서드도 있습니다, "str.zfill()". 숫자 문자열의 왼쪽에 0을 채웁니
다. 플러스와 마이너스 부호도 이해합니다:

   >>> '12'.zfill(5)
   '00012'
   >>> '-3.14'.zfill(7)
   '-003.14'
   >>> '3.14159265359'.zfill(5)
   '3.14159265359'

"str.format()" 메서드의 기본적인 사용법은 이런 식입니다:

   >>> print 'We are the {} who say "{}!"'.format('knights', 'Ni')
   We are the knights who say "Ni!"

The brackets and characters within them (called format fields) are
replaced with the objects passed into the "str.format()" method.  A
number in the brackets refers to the position of the object passed
into the "str.format()" method.

   >>> print '{0} and {1}'.format('spam', 'eggs')
   spam and eggs
   >>> print '{1} and {0}'.format('spam', 'eggs')
   eggs and spam

"str.format()" 메서드에 키워드 인자가 사용되면, 그 값들은 인자의 이름
을 사용해서 지정할 수 있습니다.

   >>> print 'This {food} is {adjective}.'.format(
   ...       food='spam', adjective='absolutely horrible')
   This spam is absolutely horrible.

위치와 키워드 인자를 자유롭게 조합할 수 있습니다:

   >>> print 'The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
   ...                                                    other='Georg')
   The story of Bill, Manfred, and Georg.

"'!s'" (apply "str()") and "'!r'" (apply "repr()") can be used to
convert the value before it is formatted.

   >>> import math
   >>> print 'The value of PI is approximately {}.'.format(math.pi)
   The value of PI is approximately 3.14159265359.
   >>> print 'The value of PI is approximately {!r}.'.format(math.pi)
   The value of PI is approximately 3.141592653589793.

선택적인 "':'" 과 포맷 지정자가 필드 이름 뒤에 올 수 있습니다. 이것으
로 값이 포맷되는 방식을 더 정교하게 제어할 수 있습니다. 다음 예는 원주
율을 소수점 이하 세 자리로 반올림합니다.

>>> import math
>>> print 'The value of PI is approximately {0:.3f}.'.format(math.pi)
The value of PI is approximately 3.142.

"':'" 뒤에 정수를 전달하면 해당 필드의 최소 문자 폭이 됩니다. 표를 예
쁘게 만들 때 편리합니다.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
   >>> for name, phone in table.items():
   ...     print '{0:10} ==> {1:10d}'.format(name, phone)
   ...
   Jack       ==>       4098
   Dcab       ==>       7678
   Sjoerd     ==>       4127

나누고 싶지 않은 정말 긴 포맷 문자열이 있을 때, 포맷할 변수들을 위치
대신에 이름으로 지정할 수 있다면 좋을 것입니다. 간단히 딕셔너리를 넘기
고 키를 액세스하는데 꺾쇠괄호 "'[]'" 를 사용하면 됩니다

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print ('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
   ...        'Dcab: {0[Dcab]:d}'.format(table))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

'**' 표기법을 사용해서 table을 키워드 인자로 전달해도 같은 결과를 얻을
수 있습니다.

   >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print 'Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table)
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

이 방법은 모든 지역 변수들을 담은 딕셔너리를 돌려주는 내장 함수
"vars()" 와 함께 사용할 때 특히 쓸모가 있습니다.

"str.format()" 를 사용한 문자열 포매팅의 완전한 개요는 Format String
Syntax 을 보세요.


7.1.1. 예전의 문자열 포매팅
---------------------------

"%" 연산자도 문자열 포매팅에 사용될 수 있습니다. 왼쪽 인자를 오른쪽 인
자에 적용되는 "sprintf()"-스타일 포맷 문자열로 해석하고, 이 포매팅 연
산의 결과로 얻어지는 문자열을 돌려줍니다. 예를 들어:

   >>> import math
   >>> print 'The value of PI is approximately %5.3f.' % math.pi
   The value of PI is approximately 3.142.

More information can be found in the String Formatting Operations
section.


7.2. 파일을 읽고 쓰기
=====================

"open()" returns a file object, and is most commonly used with two
arguments: "open(filename, mode)".

   >>> f = open('workfile', 'w')
   >>> print f
   <open file 'workfile', mode 'w' at 80a0960>

첫 번째 인자는 파일 이름을 담은 문자열입니다. 두 번째 인자는 파일이 사
용될 방식을 설명하는 몇 개의 문자들을 담은 또 하나의 문자열입니다.
*mode* 는 파일을 읽기만 하면 "'r'", 쓰기만 하면 "'w'" (같은 이름의 이
미 존재하는 파일은 삭제됩니다) 가 되고, "'a'" 는 파일을 덧붙이기 위해
엽니다; 파일에 기록되는 모든 데이터는 자동으로 끝에 붙습니다. "'r+'"
는 파일을 읽고 쓰기 위해 엽니다. *mode* 인자는 선택적인데, 생략하면
"'r'" 이 가정됩니다.

On Windows, "'b'" appended to the mode opens the file in binary mode,
so there are also modes like "'rb'", "'wb'", and "'r+b'".  Python on
Windows makes a distinction between text and binary files; the end-of-
line characters in text files are automatically altered slightly when
data is read or written.  This behind-the-scenes modification to file
data is fine for ASCII text files, but it'll corrupt binary data like
that in "JPEG" or "EXE" files.  Be very careful to use binary mode
when reading and writing such files.  On Unix, it doesn't hurt to
append a "'b'" to the mode, so you can use it platform-independently
for all binary files.


7.2.1. 파일 객체의 매소드
-------------------------

이 섹션의 나머지 예들은 "f" 라는 파일 객체가 이미 만들어졌다고 가정합
니다.

To read a file's contents, call "f.read(size)", which reads some
quantity of data and returns it as a string.  *size* is an optional
numeric argument.  When *size* is omitted or negative, the entire
contents of the file will be read and returned; it's your problem if
the file is twice as large as your machine's memory. Otherwise, at
most *size* bytes are read and returned.  If the end of the file has
been reached, "f.read()" will return an empty string ("""").

   >>> f.read()
   'This is the entire file.\n'
   >>> f.read()
   ''

"f.readline()" reads a single line from the file; a newline character
("\n") is left at the end of the string, and is only omitted on the
last line of the file if the file doesn't end in a newline.  This
makes the return value unambiguous; if "f.readline()" returns an empty
string, the end of the file has been reached, while a blank line is
represented by "'\n'", a string containing only a single newline.

   >>> f.readline()
   'This is the first line of the file.\n'
   >>> f.readline()
   'Second line of the file\n'
   >>> f.readline()
   ''

파일에서 줄들을 읽으려면, 파일 객체에 대해 루핑할 수 있습니다. 이것은
메모리 효율적이고, 빠르며 간단한 코드로 이어집니다:

   >>> for line in f:
           print line,

   This is the first line of the file.
   Second line of the file

파일의 모든 줄을 리스트로 읽어 들이려면 "list(f)" 나 "f.readlines()"
를 쓸 수 있습니다.

"f.write(string)" writes the contents of *string* to the file,
returning "None".

   >>> f.write('This is a test\n')

To write something other than a string, it needs to be converted to a
string first:

   >>> value = ('the answer', 42)
   >>> s = str(value)
   >>> f.write(s)

"f.tell()" returns an integer giving the file object's current
position in the file, measured in bytes from the beginning of the
file.  To change the file object's position, use "f.seek(offset,
from_what)".  The position is computed from adding *offset* to a
reference point; the reference point is selected by the *from_what*
argument.  A *from_what* value of 0 measures from the beginning of the
file, 1 uses the current file position, and 2 uses the end of the file
as the reference point.  *from_what* can be omitted and defaults to 0,
using the beginning of the file as the reference point.

   >>> f = open('workfile', 'r+')
   >>> f.write('0123456789abcdef')
   >>> f.seek(5)      # Go to the 6th byte in the file
   >>> f.read(1)
   '5'
   >>> f.seek(-3, 2)  # Go to the 3rd byte before the end
   >>> f.read(1)
   'd'

When you're done with a file, call "f.close()" to close it and free up
any system resources taken up by the open file.  After calling
"f.close()", attempts to use the file object will automatically fail.

   >>> f.close()
   >>> f.read()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ValueError: I/O operation on closed file

It is good practice to use the "with" keyword when dealing with file
objects.  This has the advantage that the file is properly closed
after its suite finishes, even if an exception is raised on the way.
It is also much shorter than writing equivalent "try"-"finally"
blocks:

   >>> with open('workfile', 'r') as f:
   ...     read_data = f.read()
   >>> f.closed
   True

파일 객체는 "isatty()" 나 "truncate()" 같은 몇 가지 메서드를 더 갖고
있는데, 덜 자주 사용됩니다; 파일 객체에 대한 완전한 안내는 라이브러리
레퍼런스를 참조하세요.


7.2.2. "json" 으로 구조적인 데이터를 저장하기
---------------------------------------------

문자열은 파일에 쉽게 읽고 쓸 수 있습니다. 숫자는 약간의 수고를 해야 하
는데, "read()" 메서드가 문자열만을 돌려주기 때문입니다. 이 문자열을
"int()" 같은 함수로 전달해야만 하는데, "'123'" 같은 문자열을 받고 숫자
값 123을 돌려줍니다. 중첩된 리스트나 딕셔너리 같은 더 복잡한 데이터를
저장하려고 할 때, 수작업으로 파싱하고 직렬화하는 것이 까다로울 수 있습
니다.

사용자가 반복적으로 복잡한 데이터형을 파일에 저장하는 코드를 작성하고
디버깅하도록 하는 대신, 파이썬은 JSON (JavaScript Object Notation) 이
라는 널리 쓰이는 데이터 교환 형식을 사용할 수 있게 합니다. "json" 이라
는 표준 모듈은 파이썬 데이터 계층을 받아서 문자열 표현으로 바꿔줍니다;
이 절차를 *직렬화 (serializing)* 라고 부릅니다. 문자열 표현으로부터 데
이터를 재구성하는 것은 *역 직렬화 (deserializing)* 라고 부릅니다. 직렬
화와 역 직렬화 사이에서, 객체를 표현하는 문자열은 파일이나 데이터에 저
장되거나 네트워크 연결을 통해 원격 기계로 전송될 수 있습니다.

주석: JSON 형식은 데이터 교환을 위해 현대 응용 프로그램들이 자주 사
  용합니 다. 많은 프로그래머가 이미 이것에 익숙하므로, 연동성을 위한
  좋은 선 택이 됩니다.

객체 "x" 가 있을 때, 간단한 한 줄의 코드로 그것의 JSON 문자열 표현을
볼 수 있습니다:

   >>> import json
   >>> json.dumps([1, 'simple', 'list'])
   '[1, "simple", "list"]'

Another variant of the "dumps()" function, called "dump()", simply
serializes the object to a file.  So if "f" is a *file object* opened
for writing, we can do this:

   json.dump(x, f)

To decode the object again, if "f" is a *file object* which has been
opened for reading:

   x = json.load(f)

이 간단한 직렬화 테크닉이 리스트와 딕셔너리를 다룰 수 있지만, 임의의
클래스 인스턴스를 JSON 으로 직렬화하기 위해서는 약간의 수고가 더 필요
합니다. "json" 모듈의 레퍼런스는 이 방법에 대한 설명을 담고 있습니다.

더 보기: "pickle" - 피클 모듈

  JSON 에 반해, *pickle* 은 임의의 복잡한 파이썬 객체들을 직렬화할 수
  있는 프로토콜입니다. 파이썬에 국한되고 다른 언어로 작성된 응용 프로
  그램들과 통신하는데 사용될 수 없습니다. 기본적으로 안전하지 않기도
  합니다: 믿을 수 없는 소스에서 온 데이터를 역 직렬화할 때, 숙련된 공
  격자에 의해 데이터가 조작되었다면 임의의 코드가 실행될 수 있습니다.
