17655


## 元質問

str型のオブジェクトに、UTF-16がUTF-8として入力されてしまっているため、これをUTF-8に変換したいと考えております。  
具体的な例を挙げると、`result =
"\\u3053\\u306e\\u4eba"`という形で与えられていて、ここから余計な`\`を取り除いた上で、「この人」という出力を得たいです([こちら](http://so-
zou.jp/web-app/text/encode-decode/)のサイトで変換することで元はUTF-16ということが判明)。

[code]

    result2 = "\u3053\u306e\u4eba"
    result2
    
[/code]

とすると、「この人」という結果が得られることから、`\`が余計に入っていることが原因だと考え、`re.sub(r"\\\\", "\\",
result)`を試しましたが、`error: bogus escape (end of line)`というエラーが発生して正規表現を使うことができません。

使用環境はMac OS 10.9、Python 3.4.3です。よろしくお願いいたします。

* * *

## 追記

早速反応を頂き、ありがとうございます。頂いたコメントを踏まえて追記します。

Takayuki SHIMIZUKAWAさん:  
元の質問にある`\\u3053\\u306e\\u4eba`を、[こちら](http://so-zou.jp/web-app/text/encode-
decode/)のサイトで変換させたところ、UTF-16だけで日本語として読むことのできる結果が返ってきたため、UTF-8ではなくUTF-16だろうと推測しました。  
作りかけの長いプログラムの一部分なので全てを貼り付けることはできないのですが、簡単に状況を説明しますと、Webスクレイピングの練習として作成しているプログラムになります。BeautifulSoup4で、対象としたサイトの情報を取得したのですが、一部の文字列が`<script
type="text/javascript"> </script>`のブロックに入っており、先ほどの`result =
"\\u3053\\u306e\\u4eba"`は、そこから取得した結果の冒頭部分になります。

Hidekiさん:  
確かにこれで欲しかった結果が返ってきました。ありがとうございます。お時間があれば、どのような仕組みだったのか解説いただけますと、今後の勉強に役立ちます。

argusさん:  
こちらでも上のHidekiさんと同様の結果が得られました。ありがとうございます。こちらも、どのような仕組みだったのか、大変興味があります。


CC BY-SA 3.0
2015-10-14T11:43:16.563
17648
2015-10-15T03:57:23.123
2015-10-14T14:17:50.847
12705
12705
question
python 正規表現
Pythonの文字コード変換: UTF-16からUTF-8への変換


まず、文字列中の連続する "\" を 一つの "\"
に置換する必要があれば、正規表現だとややこしいので、`replace`メソッドをお勧めします。(raw文字を使えばましにはなりますが説明のため以下では使いません)  

[code]

    # \ は エスケープされて \\ と表現されます
    s = 'ab\\\\cd\\\\ef'
    print(s)
    # 出力 -> ab\\cd\\ef
    s2 = s.replace('\\\\', '\\')
    print(s2)
    # 出力 -> ab\cd\ef
    
    # 正規表現の場合、正規表現のエスケープもあるので "\" が倍増します
    s3 = re.sub("\\\\\\\\", "\\\\", s)
    print(s3)
    # 出力 -> ab\cd\ef
    
[/code]

しかし、`result = "\\u3053\\u306e\\u4eba"` という形で代入されているなら、"\"
は連続していません。文字列中の連続する"\"はエスケープで表現された一つの"\"だからです。

[code]

    print(result)
    # 出力 -> \u3053\u306e\u4eba
    
[/code]

上のように print すると確認出来ますが "\" は一つです。  
この場合は他の方がコメントで書かれている方法で、エスケープを解釈し、変換すればよいです。

もし、resultの中身が本当に`"\\u3053\\u306e\\u4eba"`となっていて "\"
が連続しているならば、上記の`replace`をした上でエスケープの解釈の処理をするのがよいでしょう。

[code]

    # 文字列リテラルでは一つの \ の表現が \\ ですから、 \\ の表現は \\\\ となります
    result = "\\\\u3053\\\\u306e\\\\u4eba"
    s = result.replace('\\\\', '\\')
    print(s)
    # 出力 -> \u3053\u306e\u4eba
    
[/code]


CC BY-SA 3.0
2015-10-14T13:21:08.083
17652
2015-10-14T13:27:21.003
2015-10-14T13:27:21.003
3054
3054
17648
answer


おそらくそれはUTF-16ではなく、Pythonの文字列内部表現です。  
そもそもどうしてそのようなデータになってしまったのか、原因を修正したほうがよいでしょう。  
エンコーディングの変換がどこかでおかしくなっているのだと思います。

なお、以下のどちらかで期待する文字列に変換できると思います。

[code]

    r1 = result.decode('unicode-escape')
    r2 = result.encode().decode('unicode-escape')
    
[/code]

`unicode-escape`
は、Pythonの文字列内部表現をそのままascii文字列に変換するためのエンコーディングです。これが必要になるシーンはそれほど多くありませんが、今回のようなデータ変換ミスを戻すために使用する機会があると思います。

'unicode-escape' コーデックについては <http://docs.python.jp/3.4/library/codecs.html>
に記述があります。'unicode-escape'
コーデックは、Pythonソースコード内にASCIIで記述されたUnicodeリテラル文字列表現を、Pythonの文字列(Python3のstr)に変換するために使用します。  
Pythonがソースコードを解釈するときに使用する、ということから、evalと同等のことを行っているとも言えます。evalがPythonコードを全て解釈するのに対して、'unicode-
escape' コーデックは文字列のみを扱います。

なお、evalは極力使うべきではありません。もし、Webスクレイピングで取り出した文字列に、 `import os;os.system('rm -R
/')` という文字列が含まれていて、それをevalに渡してしまったら、Pythonはルートディレクトリ以下の全ファイルを削除しようとするでしょう。


CC BY-SA 3.0
2015-10-14T15:14:44.810
17655
2015-10-14T15:14:44.810
806
17648
answer


元がjavascriptの文字列リテラルということなので、javascriptの文字列リテラルを解釈できるパーサを通すのが正解です。手っ取り早いところはJSONパーサでしょう。

pythonはよくわからないので見よう見まねですが、

[code]

    import json
    s = "\\\\u3053\\\\u306e\\\\u4eba"
    print(json.loads('["' + s + '"]')[0])
    
[/code]

で「この人」が表示されました。


CC BY-SA 3.0
2015-10-15T02:19:01.453
17664
2015-10-15T03:57:23.123
2015-10-15T03:57:23.123
5793
5793
17648
answer