파이썬 유니코드 통합 제안 버전: 1.8

소개:

이 제안서의 아이디어는 파이썬에 고유의 유니코드 3.0을 추가하는 것으로 유니코드 사용을 가능한한 간결하게 사용하게 함으로써 수 많은 함정들에 빠지지 않도록 하는 것입니다. 이 목적은 달성하기 쉽지 않습니다 -- 문자열은 파이썬에서 가장 근본적인 객체중의 하나이므로 -- 우리는 이 제안이 상당한 정련을 경험하리라 예상합니다.

이 제안의 현재 버전은 유니코드-파이썬 통합의 많은 양상들로 인하여 약간 정리되지 않았음을 주의하세요.

이 문서의 최신 버전은 항상 다음에서 볼 수 있습니다:

http://starship.python.net/~lemburg/unicode-proposal.txt

이전 버전은 다음에서 얻을 수 있습니다:

http://starship.python.net/~lemburg/unicode-proposal-X.X.txt

관례:

일반적인 주의사항:

유니코드 기본설정 코드전환:

유니코드 구현은 자신에게 건네지는 8-비트 문자열의 코드전환에 대하여 강제 형변환을 해야 한다고 가정하고 있으며, 아무런 특정한 코드전환이 주어지지 않으면 기본으로 지정된 코드전환으로 문자열을 유니코드 변환해야 한다고 가정하고 있습니다. 이 코드전환을 이 텍스트에서는 기본 코드전환(default encoding)이라고 부릅니다.

이 때문에, 유니코드의 구현은 하나의 전역 변수를 유지하는데 site.py 파이썬 시작 스크립트에 설정할 수 있습니다. 이후의 변경은 불가합니다. 기본 코드전환은 두개의 sys 모듈 API로 설정되고 열람될 수 있습니다:

sys.setdefaultencoding(encoding)
유니코드 구현이 사용하는 기본 코드전환을 설정한다. 코드전환은 파이썬 설치시에 지원되는 코드전환이어야 한다, 그렇지 않으면, LookupError가 일어난다.
주의: 이 API는 site.py에서만 사용할 수 있다! 사용한 다음에는 site.py에 의해서 sys 모듈로부터 제거된다.
sys.getdefaultencoding()
기본 코드전환을 반환한다.

그렇지 않고 정의되지 않았거나, 설정되지 않았다면, 기본 코드전환은 'ascii'가 기본값이 됩니다. 이 코드전환은 파이썬의 시작시의 기본값이기도 합니다. (그리고 site.py 가 실행되기 전에 효력을 발휘합니다).

주목할 것은 기본설정된 site.py 시작 모듈에는 설정취소 가능한 선택적인 코드가 포함되어 있는데 이 코드는 현재 로케일에 정의된 코드전환을 따라 기본 코드전환을 설정할 수 있습니다. locale 모듈은 운영체제 환경이 정의한 기본 로케일 설정으로부터 코드전환을 추출하는데 사용됩니다 (locale.py 참조). 만약 코드전환이 결정될 수 없다면, 알수 없거나, 지원되지 않는다면, 그 코드는 기본 코드전환을 'ascii'로 기본설정합니다. 이 코드를 가능하게 하려면, site.py 파일을 편집하거나 또는 적절한 코드를 파이썬의 설치본에 있는 sitecustomize.py 모듈안으로 집어 넣으세요.

유니코드 구성자:

파이썬은 __builtins__을 통하여 사용가능한 유니코드 문자열들을 위해서 다음과 같은 내장 구성자를 제공해야 합니다:

u = unicode(encoded_string[,encoding=<default encoding>][,errors="strict"])

u = u'<unicode-escape로 코드전환된 파이썬 문자열>'

u = ur'<raw-unicode-escape로 코드전환된 파이썬 문자열>'

'유니코드-탈출(unicode-escape)' 코드전환은 다음과 같이 정의됩니다:

에러에 대해 가능한 값들에 대한 설명을 보려면 아래의 코덱 섹션을 보세요.

예제:

u'abc'          -> U+0061 U+0062 U+0063
u'\u1234'       -> U+1234
u'abc\u1234\n'  -> U+0061 U+0062 U+0063 U+1234 U+005c

'raw-unicode-escape' 코드전환은 아래와 같이 정의됩니다:

주목할 것은 프로그램을 작성할 때 사용한 그 코드전환에 대하여 약간의 힌트를 제공해야 한다는 것입니다. 소스 파일의 첫 몇 줄의 주석에 프로그램의 한 부분으로서 제공하는 것입니다 (예를 들어, '# source file encoding: latin-1'). 만약 7-비트 아스키만을 사용한다면 모든 일은 문제가 없으며 그러한 주의는 필요없습니다, 그러나 만약 아스키에 정의되어 있지 않은 Latin-1 문자들을 포함한다면, 힌트를 포함하는 것이 분명히 가치가 있습니다. 다른 나라의 사람들도 역시 여러분의 소스에 있는 문자열을 읽을 수 있기를 바라기 때문이지요.

유니코드 형 객체:

유니코드 객체는 'unicode'라는 이름을 가지는 UnicodeType 형을 가져야 하며, 표준 types 모듈을 통하여 만들 수 있습니다.

유니코드 출력:

유니코드 객체는 메쏘드 .encode([encoding=<기본 코드전환>])를 가지는데 파이썬 문자열 코드전환을 주어진 전략을 사용하여 유니코드 문자열로 반환합니다. (코덱을 참조).

print u := print u.encode() # <기본 코드전환>을 사용한다;

str(u) := u.encode() # <기본 코드전환>을 사용한다;

repr(u) := "u%s" % repr(u.encode('unicode-escape'))

또한, C로 작성된 다른 API들이 유니코드 객체들을 어떻게 다룰 것인지에 대해 더 자세한 사항은 내부 인수 해석과 버퍼 인터페이스(Internal Argument Parsing and Buffer Interface)를 참조하세요.

유니코드 서수:

유니코드 3.0은 32-비트 서수 문자 세트를 가지므로, 구현은 32-비트를 인식하는 서수 변환 API를 제공해야 합니다:

  ord(u[:1]) (이것은 확장되어 유니코드 객체와 작동하는 표준 ord() 함수이다)
        --> 유니코드 순서 번호 (32-비트)

  unichr(i) 
        --> 문자 i에 대한 유니코드 객체 (32-비트로 주어진다면);
            그렇지 않으면 ValueError

두 API는 문자열에 대한 상대적 함수 ord()와 chr() 함수처럼 __builtins__로 들어가야 합니다.

유니코드는 개인적인 코드전환을 위한 공간을 제공합니다. 이러한 것들을 사용하면 머신에 따라 서로 다른 출력 표현을 야기할 수 있습니다. 이 문제는 파이썬 혹은 유니코드의 문제가 아니라 머신 설정과 유지보수의 문제입니다.

비교 & 해쉬 값:

유니코드 객체는 다른 객체들이 유니코드로 강제 변환된 후에는 이러한 다른 객체들과 똑같이 비교되어야 합니다. 문자열에 대하여 이것은 기본 코드전환을 사용하여 유니코드 문자열로 번역된다는 것을 의미합니다.

유니코드 객체는 그들의 아스키 문자열과 동등한 해쉬 값을 반환해야 합니다. 유니코드 문자열이 비-아스키 값을 가지고 있으면 기본 코드전환된, 동등한 문자열 표현과, 같은 해쉬 값을 반환한다고 보증할 수 없습니다.

cmp() (혹은 PyObject_Compare())를 사용하여 비교될 때, 그 구현은 그 변환중에 일어나는 TypeError를 감추어서 그 문자열의 행위와 똑 같이 유지되어야 합니다. 문자열을 유니코드로 강제 변환하는 중에 일어나는 ValueErrors와 같은 다른 모든 에러들은 감추어져서는 안되고 사용자에게 건네져야 합니다.

포함 테스트에서 ('a' in u'abc' 그리고 u'a' in 'abc') 양쪽 편 모두 이 테스트를 적용하기 전에 강제로 유니코드로 변환되어야 합니다. 강제 변환도중에 발생하는 에러(예. None in u'abc')들은 감추어지면 안됩니다.

강제변환:

새로운 객체를 형성하기 위해서 파이썬 문자열과 유니코드 객체를 사용하는 것은 항상 더 정밀한 형식으로 강제변환되어야 합니다, 예를 들어, 유니코드 객체로 변환되어야 합니다.

  u + s := u + unicode(s)

  s + u := unicode(s) + u

관련된 모든 문자열을 유니코드로 변환하고 그 인수들을 같은 이름을 가진 유니코드 메쏘드에 적용함으로써, 모든 문자열 메쏘드들은 호출을 동등한 유니코드 객체 메쏘드 호출에 위임하여야 합니다

예를 들어,

  string.join((s,u),sep) := (s + sep) + u

  sep.join((s,u)) := (s + sep) + u

유니코드 객체에 대한 %-형식화 읽고/쓰기는, 형식화 표식설정자(Formatting Markers)를 참조하세요.

예외(Exceptions):

UnicodeError는 예외모듈에 ValueError의 하부클래스로 정의되어 있습니다. 그것은 PyExc_UnicodeError를 통하여 C 수준에서 사용가능합니다. 유니코드 코드전환/코드해석에 관련된 모든 예외들은 UnicodeError의 하부클래스여야 합니다.

코덱 (전환기/해석기) 참조:

코덱 (코덱 인터페이스 정의 참조) 탐색 저장소는 "codecs"모듈에 의해서 구현되어야 합니다:

                     codecs.register(search_function)

탐색 함수는 하나의 인수를 예상합니다, 모두 소문자로된 코드전환 이름을 취하는데 하이픈과 공백은 밑줄문자로 변환되고, 다음과 같은 인수들을 취하는 함수들(encoder, decoder, stream_reader, stream_writer)을 담은 터플을 반환합니다:

코드전환기(encoder) 그리고 코드해석기(decoder):
이것들은 함수 혹은 메쏘드로서 코덱 실체의 .encode/.decode 메쏘드와 같은 인터페이스를 가집니다 (코덱 인퍼페이스 참조). 이 함수/메쏘드들은 상태정보없이 수행된다고 예상됩니다.
스트림_입력기(stream_reader) 그리고 스트림_출력기(stream_writer):
이것들은 다음의 인터페이스를 가지는 공장 함수들일 필요가 있습니다:

      factory(stream,errors='strict')

이 공장(factory) 함수는 스트림입력기/스트림출력기(StreamWriter/StreamReader)에 의해서 각각 정의되는 인터페이스를 제공하는 객체들을 반환해야 합니다. (코덱 인터페이스 참조). Stream 코덱은 상태정보를 유지할 수 있습니다.

에러로 가능한 값들은 아래의 코덱 섹션에 정의 되어 있습니다.

탐색 함수가 주어진 코드전환을 발견할 수 없을 경우에는, None을 반환해야 합니다.

코드전환에 대한 별명 지원이 그 탐색(search) 함수에 남겨져 구현되어야 합니다.

코덱 모듈은 수행속도의 이유 때문에 코드전환의 임시저장을 유지할 것입니다. 코드전환은 먼저 임시저장장소에서 참조됩니다. 발견되지 않으면, 등록된 탐색 함수들의 리스트가 검색됩니다. 아무런 코덱 터플도 발견되지 않으면, LookupError가 일어납니다. 그렇지 않으면, 그 코덱 터플은 그 임시기억장소에 저장되고 호출자에게 반환됩니다.

Codec 실체를 열람하려면 다음의 API가 사용되야 합니다:

  codecs.lookup(encoding)

이것은 발견된 코덱 터플을 반환하던가 LookupError를 일으킵니다.

표준 코덱:

표준 코덱은 표준 파이썬 코드 라이브러리에 있는 encodings/ 패키지 디렉토리 안에 있어야 합니다. 그 디렉토리에 있는 __init__.py 파일은 코덱 참조 호환 탐색 함수를 포함하여야만 하는데 이는 코덱 참조에 기초하여 느린 모듈을 구현합니다.

파이썬은 가장 적절한 코드전환에 대하여 다수의 표준 코덱을 제공하여야 합니다, 예를 들어,

  'utf-8':              8-비트 가변 길이 코드전환
  'utf-16':             16-비트 가변 길이 코드전환 (작은값/큰값 종료형)
  'utf-16-le':          utf-16이지만 명시적으로 작은값 종료형
  'utf-16-be':          utf-16이지만 명시적으로 큰값 종료형
  'ascii':              7-비트 아스키 코드페이지
  'iso-8859-1':         ISO 8859-1 (Latin 1) 코드페이지
  'unicode-escape':     정의는 유니코드 구성자를 보세요
  'raw-unicode-escape': 정의는 유니코드 구성자를 보세요
  'native':             파이썬이 사용하는 내부 형식을 덤프한다

일반적인 별명도 기본값마다 제공되어야 합니다, 예를 들어, 'iso-8859-1'에 대해서는 'latin-1'이 제공되어야 함.

주의: 'utf-16'은 파일 입/출력에 대해서는 바이트 순서 표식설정(BOM)을 필수적으로 사용하여 구현되어야 합니다.

아시아 스크립트를 지원하는 CJK 코드 전환 같은 다른 모든 코드 전환은 핵심 파이썬 배포본에 포함되지 않는 별개의 패키지로 구현되어야 합니다. 그리고 이것은 이 제안서에서 다루는 부분이 아닙니다.

코덱 인터페이스 정의:

다음의 기본 클래스가 "codecs"모듈 에서 정의되어야 합니다. 코드전환 모듈 구현자가 사용할 임시 장소를 제공할 뿐만 아니라, 또한 그 유니코드 구현이 예상하고 있는 인터페이스를 정의합니다.

주목할 것은 여기에서 정의된 코덱 인터페이스는 광범위한 어플리케이션에 아주 잘 맞습니다. 유니코드 구현은, .encode()와 .write()에 대해서는 유니코드 객체가 입력될 것으로 간주하고 .decode()에 대해서는 문자 버퍼 호환 객체가 입력될 것으로 예상합니다. 함수 .encode()와 .read()의 출력은 파이썬 문자열이 되어야 합니다. 그리고 .decode()는 반드시 유니코드 객체를 반환해야 합니다.

먼저, 우리는 상태정보없는 코드전환기/코드해석기가 있습니다. 이것은 (아래에 있는) 스트림 코덱처럼 한 번에 일을 처리하지는 않습니다, 왜냐하면 모든 구성요소들이 메모리에서만 사용가능하다고 예상되기 때문입니다.

class Codec:

    """ 상태정보 없는 코드전환기/코드해석기에 대한 인터페이스를 정의한다.

        .encode()/.decode() 메쏘드는 에러 인수들을 제공함으로써 
        다른 에러 처리 전략을 구현할 수도 있다
        이러한 문자열 값들은 다음과 같이 정의된다:

         'strict' - 에러를 일으킨다 (or a subclass)
         'ignore' - 그 문자를 무시하고 다음 문자를 계속하여 처리한다
         'replace' - 적당한 대체 문자로 바꾼다;
                     파이썬은 내장 유니코드 코덱에 대하여 공식적인
                     U+FFFD 대체 문자(REPLACEMENT CHARACTER)를
                     사용할 것이다.

    """
    def encode(self,input,errors='strict'):
        
        """ 그 객체의 입력을 코드 전환하고
            터플(출력 객체, 처리된 길이)을 반환한다 .

            errors는 적용해야할 에러 처리를 정의한다.
            기본 값은 'strict' 처리이다.

            이 메쏘드는 상태정보를 코덱 실체에 저장하지 않을 수도 있다.
            효율적으로 코드전환/코드해석을 하기 위해서는
            상태정보를 저장해야만 하는 코덱으로
            StreamCodec을 사용하라.

        """
	...

    def decode(self,input,errors='strict'):

        """ 객체의 입력을 코드해석하고 터플을 반환한다
            (출력 객체, 처리된 길이).

            input은 반드시 bf_getreadbuf 버퍼 슬롯을 제공하는 객체라야 한다
            이 슬롯을 제공하는 객체의 예들은 
            파이썬 문자열, 버퍼 객체 그리고 메모리 짝짓기된 파일들이다.
        
            errors는 적용할 에러 처리를 정의한다.
            기본값으로 'strict' 처리이다.

            이 메쏘드는 코덱 실체에 상태를 저장하지 않을 수도 있다.
            코드해석/코드전환을 효율적으로 하려면 상태를 
            반드시 유지해야 하는 코덱으로 StreamCodec을 사용하라.

        """ 
        ...

스트림출력기(StreamWriter)와 스트림출력기(StreamReader)는 상태정보 있는 스트림에 대하여 작동하는 코드전환기/코드해석기(encoders/decoders)에 대하여 인터페이스를 정의합니다. 이렇게 하면 그 데이타를 한번에 처리하여 효율적으로 메모리를 사용할 수 있습니다. 만약 엄청 큰 문자열이 메모리에 있다면, 그것들을 cStringIO 객체로 포장하고 싶으실 텐데 그러면 이 코덱을 사용하세요. 일괄 처리도 할 수 있을 뿐만 아니라, 예를 들어, 사용자에게 처리 정보도 제공할 수 있습니다.

class StreamWriter(Codec):

    def __init__(self,stream,errors='strict'):

        """ StreamWriter 실체를 생성한다.

            스트림은 파일-비슷한 객체로서 (이진) 데이타를
            쓰기 위하여 열려진다.

            StreamWrite는 에러 키워드 인수를 제공함으로써
            다른 방식의 에러 처리 전략을 구현할 수 있다.
            이러한 매개변수들은 다음과 같이 정의된다:

             'strict' - ValueError (또는 하부클래스)를 일으킨다
             'ignore' - 그 문자를 무시하고 계속하여 다음 문자를 처리한다
             'replace'- 적절한 대체 문자로 바꾼다

        """
        self.stream = stream
        self.errors = errors

    def write(self,object):

        """ 객체의 내용을 코드전환하여 self.stream에 쓴다.
        """
        data, consumed = self.encode(object,self.errors)
        self.stream.write(data)
        
    def writelines(self, list):

        """ 문자열들을 담은 연결 리스트를 스트림에 
            .write()를 사용하여 쓴다.
        """
        self.write(''.join(list))
        
    def reset(self):

        """ 상태정보를 유지하는데 사용하던 코덱 버퍼를 청소하고 새로이 설정한다.

            이 메쏘드를 호출하는 이유는 출력 상태에 있는 데이타가 
            깨끗한 상태로 된 것을 확인하는 것이며, 
            이렇게 함으로써, 상태정보를 회복하기 위하여
            그 전체 스트림을 재탐색할 필요없이 
            새로이 신선한 데이타를 추가할 수 있다.

        """
        pass

    def __getattr__(self,name,

                    getattr=getattr):

        """ 다른 모든 메쏘드들은 하부의 스트림으로부터 상속받는다.
        """
        return getattr(self.stream,name)

class StreamReader(Codec):

    def __init__(self,stream,errors='strict'):

        """ StreamReader 실체를 생성한다.

            스트림은 읽기 (이진) 데이타로 열려진
            파일-비슷한 객체여야 한다.

            스트림입력기(StreamReader)는 errors 키워드 인수를
            제공함으로써 다른 에러 처리 전략을 구현해도 좋다.
            이러한 매개변수들은 다음과 같이 정의된다:

             'strict' - ValueError를 일으킨다 (or a subclass)
             'ignore' - 그 문자를 무시하고 다음을 계속하여 처리한다
             'replace'- 적절한 대체 문자로 대체한다;

        """
        self.stream = stream
        self.errors = errors

    def read(self,size=-1):

        """ 스트림 self.stream으로부터 코드해석한다 
            그리고 그 결과 객체를 반환한다.

            size는 코드해석을 위하여 그 스트림으로부터 읽어야 할
            최대의 바이트 개수를 나타낸다. 코드해석기는 이 설정을
            적절하게 변경할 수 있다. 기본 값은 -1인데
            가능한 최대로 읽고 코드해석하라는 것을 의미한다. 
            size의 목적은 거대한 파일을 한 번에 코드해석하는 것을
            막는 것이다.

            이 메쏘드는 욕심껏 읽기 전략을 사용하여야 하는데
            다시말하면 주어진 크기와 코드전환의 정의안에서
            최대한의 데이타를 읽어야만 한다는 것이다.
            예를 들어, 만약 선택적인 코드전환 종료표시 혹은 상태정보 표식설정이
            그 스트림에 사용가능하다면, 이것들 역시 읽혀져야 한다.

        """
        # 조각썰지 않은 읽기:
        if size < 0:
            return self.decode(self.stream.read())[0]
        
        # 조각썰린 읽기:
        read = self.stream.read
        decode = self.decode
        data = read(size)
        i = 0
        while 1:
            try:
                object, decodedbytes = decode(data)
            except ValueError,why:
                # 이 메쏘드는 느리지만 상당히 많은 조건아래에서
                # 잘 작동한다; 기껏해야 10번정도 시도한다
                i = i + 1
                newdata = read(1)
                if not newdata or i > 10:
                    raise
                data = data + newdata
            else:
                return object

    def readline(self, size=None):

        """ 한 라인을 입력으로부터 읽어서 
            코드해석된 데이타를 반환한다.

            주의: 이 메쏘드는, .readlines() 메쏘드와는 다르게, 하부의
            .readline() 메쏘드로부터 라인 넘김 지식을 상속받는다.
            -- 현재로는 라인 버퍼링의 부재로 인하여 코덱 코드해석기를 
            사용한 라인 넘김을 지원하지 않는다. 
            그렇지만, 가능하다면, 하부클래스들은 자신이 가진 라인 넘김지식을
            사용하여 이 메쏘드를 구현하도록 노력해야 한다.

            크기(size)는, 주어진다면, 그 스트림의 .readline() 메쏘드에
            크기 인수로 건네진다.
            
        """
        if size is None:
            line = self.stream.readline()
        else:
            line = self.stream.readline(size)
        return self.decode(line)[0]

    def readlines(self, sizehint=0):

        """ 입력 스트림에서 가능한 모든 라인을 읽는다
            그리고 그것들을 라인의 리스트로 반환한다.

            줄 넘김은 코덱의 코드해석 메쏘드를 사용하여
            구현되고 그 리스트 항목에 포함된다.
            
            크기 힌트(sizehint)는, 주어진다면, 그 스트림의 .read()
            메쏘드에 크기 인수로 건네진다.

        """
        if sizehint is None:
            data = self.stream.read()
        else:
            data = self.stream.read(sizehint)
        return self.decode(data)[0].splitlines(1)

    def reset(self):

        """ 상태정보를 유지하는데 사용된 코덱 버퍼를 재설정한다.

            스트림 위치 설정이 다시 일어나면 안되므로 주의하라.
            이 메쏘드의 목적은 주로 코드해석중의 에러로부터
            복구를 하는 것이다.

        """
        pass

    def __getattr__(self,name,

                    getattr=getattr):

        """ 다른 모든 메쏘드는 하부의 스트림으로부터 상속받는다.
        """
        return getattr(self.stream,name)

스트림 코덱 구현자는 자유롭게 스트림출력기와 스트림입력기 인터페이스를 하나의 클래스에 결합할 수 있습니다. 심지어는 이 모든 것들을 코덱 클래스와 결합하는 것도 가능합니다.

구현자들은 자유롭게 추가적인 메쏘드들을 추가하여 코덱의 기능을 개선하거나 메쏘드들이 작동하는데 필요한 여분의 상태 정보를 제공할 수 있습니다. 그렇지만, 내부 코덱 구현은 위의 인터페이스만을 사용할 것입니다.

유니코드 구현이 이러한 기본 클래스들을 사용해야 하는 것은 아닙니다, 오직 인터페이스만 일치하면 됩니다; 이것으로 코덱을 확장 형으로 작성할 수 있습니다.

한계선으로서, 거대한 짝짓기 테이블은 정적 C 데이타를 사용하여 별개의 (공유) 확장 모듈로 구현되어야 합니다. 그렇게 하면 여러 프로세스들이 같은 데이타를 공유할 수 있습니다.

유니코드 짝짓기 파일을 짝짓기 모듈로 자동-변환하는 도구가 제공되어 추가적인 짝짓기에 대한 지원을 간단하게 하여 주어야 합니다 (References 참조).

공백:

.split() 메쏘드는 유니코드에서 어떤 것이 공백으로 간주되는지를 알아야 할 필요가 있을 것입니다.

대소문자 변환:

대소문자 변환은 유니코드 데이타에는 약간 복잡합니다, 왜냐하면 돌아보아야 할 많은 다른 조건들이 있기 때문입니다. 다음을 보시면

                http://www.unicode.org/unicode/reports/tr21/ 
대소문자 변환을 구현하는 것에 대한 약간의 가이드라인을 보실 수 있습니다.

파이썬에 대하여, 우리는 유니코드에 포함된 1-1 변환만을 구현해야 합니다. 로케일에 의존적인 그리고 다른 특수한 대소문자 변환은 (다음을 참조: Unicode standard file SpecialCasing.txt) 사용자의 영역 루틴에 남겨져야 하며 인터프리터의 핵심에 들어가지 않아야 합니다.

메쏘드 .capitalize()와 .iscapitalized()는 위의 기술적 보고서에 정의된 대소문자 짝짓기 알고리즘을 가능한한 근접하게 따라야 합니다.

줄 바꿈:

줄 바꿈은 CRLF, CR, LF와의 조합과 함께 B 속성을 가지는 모든 유니코드 문자열들과 표준에서 정의된 다른 특수한 라인 분리자들에 대하여 (그 순서로 번역되어) 수행되어야 합니다. 다음을 보시면

  http://www.unicode.org/unicode/reports/tr13/ 
줄 바꿈과 새로운라인(newline) 처리에 관한 약간의 가이드라인을 보실 수 있습니다.

Unicode 형은 .splitlines() 메쏘드를 제공해야 하는데 이것은 위의 지정에 따른 라인의 리스트를 반환합니다. 유니코드 메쏘드들을 참조하세요.

유니코드 문자 속성들:

별개의 "unicodedata" 모듈은 표준 UnicodeData.txt 파일에 정의된 모든 유니코드 문자 속성들에 대하여 간략한 인터페이스를 제공해야 합니다.

무엇보다도, 이러한 속성들은 숫자, 기호, 공백(numbers, digits, spaces, whitespace) 등등을 인식하는 방법을 제공합니다.

이 모듈은 모든 유니코드 문자들에 대한 접근을 제공해야 할 것이므로, 결국 이 모듈이 포함해야할 데이타는 UnicodeData.txt에 있는데 대략 600kB나 점유할 것입니다. 이러한 이유로, 그 데이타는 정적인(static) C 데이타로 저장되어야 합니다. 이렇게 하면 공유 모듈로 컴파일 할 수 있어서 밑에 깔린 운영체제가 그것을 프로세스들 사이에 공유할 수 있습니다 (보통의 파이썬 코드 모듈과는 다릅니다).

이 정보에 접근하기 위한 표준 파이썬 인터페이스가 있어야 하며 그렇게 해야 다른 구현자들이 자신만이 가능한 개선된 버전, 예를 들어, 그 데이타를 분주하게 풀어내는 것과 같은 버전을 꽂아 넣을 수 있습니다.

사적 코드 포인트 영역(Private Code Point Areas):

이에 대한 지원은 사용자의 땅 코덱에 남겨져 있으며 명시적으로 파이썬의 핵심에 통합되어 들어가지 않습니다. 주목할 것은 내부 형식이 구현된 방식 때문에 사적 코드전환에 대하여 오직 \uE000에서 \uF8FF까지의 영역만이 사용가능합니다.

내부 형식(Internal Format):

파이썬 객체에 대한 내부 형식은 파이썬에 특유한 고정된 형식 <PythonUnicode>을 사용해야 합니다. 이것은 'unsigned short' 또는 (16 비트를 가지는 또 다른 부호없는 수치 형)으로 구현되어 있습니다 . 바이트 순서는 플랫폼에 의존적입니다.

이 형식은 상응하는 유니코드 서수의 UTF-16 코드전환을 유지할 것입니다. 파이썬 유니코드 구현은 이러한 값들에 마치 UCS-2 값인 것처럼 접근합니다. UCS-2와 UTF-16은 현재 정의된 모든 유니코드 문자 포인트에 대하여 같습니다. 대리 없는 UTF-16은 약 64k 문자에 대한 접근을 제공하며 유니코드의 BMP에 정의된 모든 문자들을 처리합니다.

유니코드 객체 구성자에 코덱이 보내는 데이타가 이러한 가정을 반영하는가 확인하는 것은 코덱의 책임입니다. 유니코드 객체 구성자는 유니코드에 맞는 데이타인지 혹은 대리를 사용하고 있는지에 대하여 점검하지 않습니다.

미래의 구현은 32 비트 제한을 확장하여 UTF-16이 접근할 수 있는 모든 문자(약 1M문자)를 포함한 완전한 세트까지 확장할 것입니다.

유니코드 API는 <PythonUnicode>에서 컴파일러의 wchar_t로의 인터페이스 루틴을 제공해야 합니다. wchar_t는 compiler/libc/platform이 사용되는데 따라서 16 혹은 32 비트일 수 있습니다.

유니코드 객체는 캐쉬된 파이썬 문자열 객체 <defenc>에 대하여 포인터를 가져야 합니다. <defenc>는 그 객체의 값을 <기본 코드전환>을 사용하여 유지합니다. 이것이 필요한 이유는 수행속도와 내부 해석의 문제때문입니다 ('내부 인수 해석'을 참조하세요). 첫번째로 <기본 코드전환>에 대한 변환 요구가 유니코드 객체에 대하여 제출되면 그 버퍼가 채워집니다.

내부화는 (현재로는) 불필요합니다, 왜냐하면 파이썬 식별자들은 오직 아스키로만 정의되어 있기 때문입니다.

codecs.BOM은 내부적으로 사용되는 형식에 대하여 바이트 순서 표식설정(BOM)을 반환해야 합니다. 코덱 모듈은 다음의 상수들을 편의와 참조를 위하여 추가적으로 제공해야 합니다 (codecs.BOM은 플랫폼에 따라서 BOM_BE 혹은 BOM_LE 이어야 합니다):

  BOM_BE: '\376\377' 
    (큰값 종료형 플랫폼에서 UTF-16으로 유니코드
     U+0000FEFF에 상응한다 == ZERO WIDTH NO-BREAK SPACE)

  BOM_LE: '\377\376' 
    (작은값 종료형 플랫폼에서 UTF-16으로 유니코드
     U+0000FFFE에 상응한다 == 불법적인 유니코드 문자로 정의된다)

  BOM4_BE: '\000\000\376\377'
    (UCS-4로 유니코드 U+0000FEFF에 상응한다)

  BOM4_LE: '\377\376\000\000'
    (UCS-4로 유니코드 U+0000FFFE에 상응한다)

유니코드는 큰 값 종료형 바이트 순서를 "올바르다"라고 간주한다는 것을 주목하세요. 바뀌어진 순서는 "잘못된" 형식, 즉 불법적인 문자 정의라고 간주됩니다.

환경설정 스크립트는 파이썬이 고유의 wchar_t 형을 쓸 수 있을지 없을 지 결정하는데 도움을 주어야 합니다. (그것은 16-비트 unsigned 형을 가져야 합니다).

버퍼 인터페이스(Buffer Interface):

bf_getcharbuf에 대해서는 <defenc> 파이썬 문자열 객체를 기본으로 사용하고 bf_getreadbuf에 대해서는 내부 버퍼를 사용하여 버퍼 인터페이스를 구현합니다. 만약 bf_getcharbuf가 요구되고 <defenc> 객체가 아직 존재하지 않는다면, 먼저 그것이 생성됩니다.

주목할 것은 특별한 사례로서, 해석기 표식설정자인 "s#"는 (bf_getreadbuf 가 반환하는) 미가공 유니코드 UTF-16 데이타를 반환하지 않을 것이라는 것입니다, 오히려 그 유니코드 객체를 기본 코드전환을 사용하여 코드전환하려고 노력합니다 그리고나서 그 결과 문자열 객체에 대한 포인터를 반환합니다. (또는 그 변환이 실패하면 예외를 일으킵니다). 이것의 목적은 우연하게 이진 데이타를 출력 스트림에 쓰게 될 때 상대방이 인지하지 못하는 것을 방지하고자 하는 것입니다.

이것의 이점은 추가적으로 사용해야할 코드전환을 지정할 필요없이 (전형적으로 이러한 인터페이스를 사용하는) 출력 스트림에 쓸 수 있다는 것입니다.

만약 유니코드 객체의 읽기 버퍼 인터페이스에 접근할 필요가 있다면 PyObject_AsReadBuffer() 인터페이스를 사용하세요.

내부 형식은 '내부화된-유니코드' 코덱을 사용하여, 예를 들어, u.encode('unicode-internal')를 통하여 또한 접근될 수 있습니다.

절이기/배열하기(Pickle/Marshalling):

고유의 유니코드 객체를 지원해야 합니다. 그 객체들은 플랫폼에 독립적인 코드전환을 사용하여 코드전환되어야 합니다.

배열하기(Marshal)은 UTF-8를 사용해야만 하고 절이기(Pickle)는 (텍스트 모드에서) Raw-Unicode-Escape 또는 (이진 모드에서) UTF-8을 코드전환으로 사용해야만 합니다. UTF-16 대신에 UTF-8을 사용하면 BOM 표식을 저장할 필요가 없는 이점이 있습니다.

정규 표현식(Regular Expressions):

'Secret Labs AB'는 유니코드를-인식하는 정규 표현식 머신에 대하여 연구하고 있습니다. 그 정규 표현식은 평문 8-비트, UCS-2, 그리고 (선택적으로) UCS-4 내부 문자 버퍼에 작동합니다.

다음을 보시면

        http://www.unicode.org/unicode/reports/tr18/
유니코드 정규표현식을 다루는 법에 대한 평가를 볼 수 있습니다.

형식화 표식 설정자(Formatting Markers):

형식화 표식설정자는 파이썬의 형식화 문자열에 사용된다. 만약 파이썬 문자열이 형식화 문자열로 사용된다면, 다음의 번역은 효력을 발휘해야 합니다:

  '%s':                 유니코드 객체에 대하여 이것은 그 전체 형식화 문자열을
                        강제로 유니코드로 변환하게 만들 것이다.
                        주목할 것은 수행속도의 문제 때문에 처음부터
                        유니코드 형식화 문자열을 사용하여야 한다.

형식화 문자열이 유니코드 객체인 경우에, 모든 매개변수들은 먼저 강제로 유니코드로 변환되고 그리고 나서 다시 조립되어 그 형식화 문자열에 맞게 형식화됩니다. 수치들은 먼저 문자열로 변환되고 다음에 유니코드로 변환됩니다.

  '%s':         파이썬 문자열은 <기본 코드전환>을 사용하여
                유니코드 문자열로 번역된다.
                유니코드 객체는 있는 그대로 받아들인다.

다른 모든 문자열 형식화설정자들도 적절하게 작동해야 합니다.

Example:

u"%s %s" % (u"abc", "abc")  ==  u"abc abc"

내부 인수 해석(Internal Argument Parsing):

이러한 표식설정자들은 PyArg_ParseTuple() API에 의해서 사용됩니다:

  "U":  유니코드 객체인가를 점검하고 그것에 대한 참조점을 반환한다

  "s":  유니코드 객체에 대하여: 그 객체의 <defenc> 버퍼에 대한
        참조점을 반환한다 (이것은 <기본 코드전환>을 사용한다).

  "s#": 그 유니코드 객체의 기본 코드전환 버전에 접근한다. (버퍼 인터페이스 참조);
        그 길이는 기본 코드전환 문자열의 길이에 관련된다.
        그 유니코드 객체의 길이에 관련되는 것이 아니라는 것을 주목하라.

  "t#": 다음과 동일 "s#".

  "es": 
	두 개의 매개변수를 취한다: (const char *)과 buffer (char **). 

	입력 객체는 먼저 통상적인 방식으로 유니코드로 강제 변환된다
	그리고 나서 주어진 코드전환을 사용하여 문자열로 코드전환된다.

	출력시에, 필요한 길이 만큼의 버퍼가 할당되고
	*buffer를 통하여 NULL-종료 문자열로 반환된다.
	코드전환된 문자열은 내장된 NULL 문자들을 포함할 수 없다.
	호출자는 PyMem_Free()을 호출해서 사용하고 난 다음에는
	할당된 *buffer를 풀어줄 책임이 있다.

  "es#":
	세 개의 매개 변수를 취한다: encoding (const char *),
	buffer (char **) 그리고 buffer_len (int *).
	
	입력 객체는 먼저 통상적인 방식으로 유니코드로 강제변환된다
	그리고 나서 주어진 코드전환으로 문자열로 코드전환된다.

	*buffer가 non-NULL이라면, *buffer_len은 출력시에 반드시
	sizeof(buffer)로 설정되어야 한다.
	출력은 그러면 *buffer에 복사된다.

	만약 *buffer가 NULL이면, 필요한 길이의 버퍼가 할당되고
	출력은 그 버퍼에 복사된다. 그러면 *buffer는 갱신되어
	할당된 메모리 지역을 가르킨다.
	호출자는 사용 후에 PyMem_Free()를 호출하여
	할당된 *buffer를 풀어줄 책임이 있다.

	두 경우 모두 *buffer_len는 씌여진 문자열의 개수로 갱신된다
	(이끌리는 NULL-바이트는 제외한다).
	출력 버퍼는 확실하게 NULL-종료형이어야 한다.

예제:

자동-할당과 함께 "es#"를 사용하기:

    static PyObject *
    test_parser(PyObject *self,
		PyObject *args)
    {
	PyObject *str;
	const char *encoding = "latin-1";
	char *buffer = NULL;
	int buffer_len = 0;

	if (!PyArg_ParseTuple(args, "es#:test_parser",
			      encoding, &buffer, &buffer_len))
	    return NULL;
	if (!buffer) {
	    PyErr_SetString(PyExc_SystemError,
			    "buffer is NULL");
	    return NULL;
	}
	str = PyString_FromStringAndSize(buffer, buffer_len);
	PyMem_Free(buffer);
	return str;
    }

NULL-종료형 문자열을 반환하는 자동-할당과 함께 "es"를 사용하기:    
    
    static PyObject *
    test_parser(PyObject *self,
		PyObject *args)
    {
	PyObject *str;
	const char *encoding = "latin-1";
	char *buffer = NULL;

	if (!PyArg_ParseTuple(args, "es:test_parser",
			      encoding, &buffer))
	    return NULL;
	if (!buffer) {
	    PyErr_SetString(PyExc_SystemError,
			    "buffer is NULL");
	    return NULL;
	}
	str = PyString_FromString(buffer);
	PyMem_Free(buffer);
	return str;
    }

미리-할당된 버퍼와 함께 "es#"을 사용하기:
    
    static PyObject *
    test_parser(PyObject *self,
		PyObject *args)
    {
	PyObject *str;
	const char *encoding = "latin-1";
	char _buffer[10];
	char *buffer = _buffer;
	int buffer_len = sizeof(_buffer);

	if (!PyArg_ParseTuple(args, "es#:test_parser",
			      encoding, &buffer, &buffer_len))
	    return NULL;
	if (!buffer) {
	    PyErr_SetString(PyExc_SystemError,
			    "buffer is NULL");
	    return NULL;
	}
	str = PyString_FromStringAndSize(buffer, buffer_len);
	return str;
    }

파일/스트림 출력(File/Stream Output):

file.write(object)와 대부분의 다른 스트림 작성기들은 "s#" 또는 "t#" 인수 해석 표식설정자를 사용하여 쓰기할 데이타를 열람하므로, 유니코드 객체의 기본 코드전환된 문자열 버전은 그 스트림에 쓰여질 것입니다 (버퍼 인터페이스 참조).

유니코드를 사용하여 명시적으로 파일을 처리하기 위해서, 표준 스트림 코덱은 그 코덱 모듈을 통해서 사용가능한 그대로 사용되어야 합니다.

코덱 모듈은 단축 open(filename,mode,encoding) 함수를 사용가능하도록 제공하여야 하며 또한 필요할 때, 모드(mode)가 'b'문자를 포함하는 것을 확인하여야 합니다.

파일/스트림 입력:

사용자만이 입력 데이타가 사용하는 코드전환을 알 수 있습니다, 그래서 특별한 마법이 적용되지 않습니다. 사용자는 명시적으로 그 문자열 데이타를 유니코드 객체로 필요할 때 마다 변환해야 하거나 또는 코덱 모듈에 정의된 파일 포장자를 사용해야 할 것입니다. (파일/스트림 출력을 참조).

유니코드 메쏘드 & 속성:

모든 파이썬 문자열 메쏘드, 그리고 또:

  .encode([encoding=<기본 코드전환>][,errors="strict"]) 
     --> 유니코드 출력을 참조

  .splitlines([include_breaks=0])
     --> 유니코드 문자열을 (유니코드)라인의 리스트로 자른다;
         만약, include_breaks가 참이라면 라인 브레이크가 포함된
         그 라인들을 반환한다. 라인 브레이크를 수행하는 방법에
         대해서는 줄 넘김(Line Breaks)를 참조하라.

Code Base:

우리는 프레드릭 룬트(Fredrik Lundh)의 유니코드 객체 구현을 토대로 사용해야 합니다. 그의 구현은 이미 필요한 문자열 메쏘드의 상당수를 구현하고 있으며 잘 작성된 코드 기초를 제공하여 줌으로써 우리는 그 위에 구축할 수 있습니다.

프레드릭의 구현에 있는 sharing 객체는 생략되야 합니다.

테스트 사례:

테스트 케이스는 Lib/test/test_string.py에 있는 규칙들을 준수해야 하며 코덱 레지스트리와 표준 코덱에 대한 추가적인 점검을 포함해야 합니다.

참조:

유니코드 콘솔티엄:
        http://www.unicode.org/

유니코드 질문과 답변(FAQ):
        http://www.unicode.org/unicode/faq/

유니코드 3.0:
        http://www.unicode.org/unicode/standard/versions/Unicode3.0.html

유니코드-기술 보고서:
        http://www.unicode.org/unicode/reports/techreports.html

유니코드-짝짓기:
        http://www.unicode.org/Public/MAPPINGS/

유니코드 개론 (약간 오래되었지만 여전히 읽을 만함):
        http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html

비교를 위해:
	Introducing Unicode to ECMAScript (aka JavaScript) --
	http://www-4.ibm.com/software/developer/library/internationalization-support.html

IANA 문자 세트 이름들:
	ftp://ftp.isi.edu/in-notes/iana/assignments/character-sets

포식스와 리눅스에 대한 유니코드와 UTF-8을 지원하는 문제에 대한 논의:
	http://www.cl.cam.ac.uk/~mgk25/unicode.html

코드전환:

    개관:
            http://czyborra.com/utf/

    UTC-2:
            http://www.uazone.com/multiling/unicode/ucs2.html

    UTF-7:
            RFC2152에 정의됨,
            http://www.uazone.com/multiling/ml-docs/rfc2152.txt

    UTF-8:
            RFC2279에 정의됨,.
            http://info.internet.isi.edu/in-notes/rfc/files/rfc2279.txt

    UTF-16:
            http://www.uazone.com/multiling/unicode/wg2n1035.html


이 제안서의 변경기록:
-------------------------
1.8: Fixed some URLs to the unicode.org site.
1.7: Added note about the changed behaviour of "s#".
1.6: Changed <defencstr> to <defenc> since this is the name used in the
     implementation. Added notes about the usage of <defenc> in the
     buffer protocol implementation.
1.5: Added notes about setting the <기본 코드전환>. Fixed some
     typos (thanks to Andrew Kuchling). Changed <defencstr> to <utf8str>.
1.4: Added note about mixed type comparisons and contains tests.
     Changed treating of Unicode objects in format strings (if used
     with '%s' % u they will now cause the format string to be
     coerced to Unicode, thus producing a Unicode object on return).
     Added link to IANA charset names (thanks to Lars Marius Garshol).
     Added new codec methods .readline(), .readlines() and .writelines().
1.3: Added new "es" and "es#" parser markers
1.2: Removed POD about codecs.open()
1.1: Added note about comparisons and hash values. Added note about
     case mapping algorithms. Changed stream codecs .read() and
     .write() method to match the standard file-like object methods
     (bytes consumed information is no longer returned by the methods)
1.0: changed encode Codec method to be symmetric to the decode method
     (they both return (object, data consumed) now and thus become
     interchangeable); removed __init__ method of Codec class (the
     methods are stateless) and moved the errors argument down to the
     methods; made the Codec design more generic w/r to type of input
     and output objects; changed StreamWriter.flush to StreamWriter.reset
     in order to avoid overriding the stream's .flush() method;
     renamed .breaklines() to .splitlines(); renamed the module unicodec
     to codecs; modified the File I/O section to refer to the stream codecs.
0.9: changed errors keyword argument definition; added 'replace' error
     handling; changed the codec APIs to accept buffer like objects on
     input; some minor typo fixes; added Whitespace section and
     included references for Unicode characters that have the whitespace
     and the line break characteristic; added note that search functions
     can expect lower-case encoding names; dropped slicing and offsets
     in the codec APIs
0.8: added encodings package and raw unicode escape encoding; untabified
     the proposal; added notes on Unicode format strings; added
     .breaklines() method
0.7: added a whole new set of codec APIs; added a different encoder
     lookup scheme; fixed some names
0.6: changed "s#" to "t#"; changed <defencbuf> to <defencstr> holding
     a real Python string object; changed Buffer Interface to delegate
     requests to <defencstr>'s buffer interface; removed the explicit
     reference to the unicodec.codecs dictionary (the module can implement
     this in way fit for the purpose); removed the settable default
     encoding; move UnicodeError from unicodec to exceptions; "s#"
     not returns the internal data; passed the UCS-2/UTF-16 checking
     from the Unicode constructor to the Codecs
0.5: moved sys.bom to unicodec.BOM; added sections on case mapping,
     private use encodings and Unicode character properties
0.4: added Codec interface, notes on %-formatting, changed some encoding
     details, added comments on stream wrappers, fixed some discussion
     points (most important: Internal Format), clarified the 
     'unicode-escape' encoding, added encoding references
0.3: added references, comments on codec modules, the internal format,
     bf_getcharbuffer and the RE engine; added 'unicode-escape' encoding
     proposed by Tim Peters and fixed repr(u) accordingly
0.2: integrated Guido's suggestions, added stream codecs and file
     wrapping
0.1: first version

Written by Marc-Andre Lemburg, 1999-2000, mal@lemburg.com
한글판 2001.12.05 johnsonj