

PythonでUnicodeに変換するにはu"abc"とする方法と、unicode()を使う方法があると思います。  
下記を実行すると、結果が異なるのですが、どのような違いがあるのでしょうか。  
上は1, 下は6が返ります。

[code]

    print len(u"\u2192")
    print len(unicode("\u2192"))
    
[/code]

外部からUnicodeを読み込んで、文字として表示したいのですがここで躓いてしまいました。  
よろしくお願いいたします。


CC BY-SA 3.0
2015-11-28T06:20:41.290
19398
2015-11-29T09:38:04.903
2015-11-28T06:41:35.467
7290
13438
question
python
Pythonのuとunicode()の違いはありますか？


**Python3** で、Unicodeはデフォルトです。unicode()コンストラクタはありませｈ。

**Python2**

入力：

[code]

    print(unicode("\u2192"))
    
[/code]

結果：

[code]

    \u2192
    
[/code]

入力：

[code]

    print(u"\u2192")
    
[/code]

結果:

[code]

    →
    
[/code]

[Unicode HOWTO](http://docs.python.jp/2/howto/unicode.html)から、

> unicode() コンストラクタは unicode(string[, encoding, errors]) という用法を持っています。

unicode("\u2192") ≠ u"\u2192"


CC BY-SA 3.0
2015-11-28T14:53:52.700
19415
2015-11-28T14:53:52.700
8888
19398
answer


_#Python2である事は明らかですね_

# 文字列strとUnicodeとエンコード(UTF-8, UTF-16...)の関係

`unicode(str, [encoding,
errors])`はstrを受け取り、encodingは指定しなければデフォルトのエンコーディングになります。

まずデフォルトのエンコーディングを確認してみます。

[code]

    >>> import sys
    >>> sys.getdefaultencoding()
    'ascii'
    
[/code]

デフォルトはasciiのようです。`→`のUTF-8は0xe2 0x86 0x92なのですが

[code]

    >>> print unicode("\xe2\x86\x92")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)
    
[/code]

あの忌々しいエラーになります。なぜかというとASCIIデコーダは0x00-0x7Fが有効な範囲で0xe2は範囲外だから。そこで入力された文字列はUTF-8のエンコードである事を教えます。

[code]

    >>> print unicode("\xe2\x86\x92", "utf-8")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    UnicodeEncodeError: 'ascii' codec can't encode character u'\u2192' in position 0: ordinal not in range(128)
    
[/code]

またエラーです！しかしエラーメッセージをよく見てみると先ほどはdecodeエラーだったのが、今回はencodeエラーです。U2192のUnicodeオブジェクトはasciiエンコーダでは表現できないわけです。僕の使っている端末はUTF-8なので`.encode('utf-8')`でUTF-8の文字列に変換します。

[code]

    >>> print unicode("\xe2\x86\x92", "utf-8").encode('utf-8')
    →
    
[/code]

めでたく→が表示されました。今度はUTF-16でエンコードされた文字列を与えて、さらにUTF-8に変換して端末に表示してみます。utf-16beのbeはビッグエンディアンのことです。

[code]

    >>> print unicode("\x21\x92", "utf-16be").encode('utf-8')
    →
    
[/code]

うまくプリントできました！ちなみにutf-16leでリトルエンディアン指定だと、正しくない文字になります。

[code]

    >>> print unicode("\x21\x92", "utf-16le").encode('utf-8')
    鈡
    
[/code]

バイト順を変えれば正しく表示されます。

[code]

    >>> print unicode("\x92\x21", "utf-16le").encode('utf-8')
    →
    
[/code]

ただのUTF-16だと、次のような結果になったのでリトルエンディアンと同じ扱いになるみたいです。UTF-16の標準なのか、Pythonの標準なのかCPU依存なのかは調べていないので不明です。

[code]

    >>> print unicode("\x92\x21", "utf-16").encode('utf-8')
    →
    >>> print unicode("\x21\x92", "utf-16").encode('utf-8')
    鈡
    
[/code]

最後に、u""プリフィックスは文字列がそのファイルのエンコードで書かれているか、\uで指定したUTF-16の文字列をUnicodeオブジェクトとして扱うという意味です。ファイルの先頭に
coding:
utf-8とあれば、""の中のバイト列をUTF-8と解釈して、Unicodeオブジェクトに変換します。`u""`以外の場所で`\u`を指定してもUnicodeの意味になりません。

# 質問の内容の結果の違いについて。

以上文字列とユニコードとエンコーダ、デコーダの関係が分かったところで、質問の内容です。

[code]

    print len(u"\u2192")
    print len(unicode("\u2192"))
    
[/code]

１行目は正しくUTF-16のバイトを１文字のUnicodeオブジェクトに変換しています。2行目は\u2192という文字列なのですが、先に書いたように`u""`以外の場所で`\u`を指定してもUnicodeの意味になりません。つまり\とuと2と1と9と2と同じです。なので6文字をUnicodeオブジェクトに変換しても6文字の長さのままになります。

[code]

    >>> print repr("\u2192")
    '\\u2192'  # \がエスケープされて\\になっているが、\u2192というASCII文字列のまま
    >>> print repr(unicode("\u2192"))
    u'\\u2192' # \ u 2 1 9 2というASCII文字列をUnicodeオブジェクトに変換しただけなので６文字。
    >>> print repr(u"\u2192")
    u'\u2192'  # UTF16のU2192文字になっている
    
[/code]


CC BY-SA 3.0
2015-11-29T09:14:18.063
19431
2015-11-29T09:38:04.903
2020-06-17T08:14:45.997
-1
7837
19398
answer