Ruby文字列のUnicodeエスケープシーケンスをデコードする

JSON APIで取得した文字列は、普通日本語部分がUnicodeコードポイントにエスケープされています。

たとえばTwitterAPIで取得したJSONは、単純に文字列として見ると以下のようになります。

# wget http://api.twitter.com/1/users/lookup.json?screen_name=TwitterJP

[{“name”:”TwitterJP”,”time_zone”:”Tokyo”,”follow_request_sent”:null,”location”:”\u6771\u4eac\u8d64\u5742″,”profile_image_url”:”http:\/\/a0.twimg.com\/profile_images\/3407356865\/62f0d53222361fbd2c1fe9889f4cc559_normal.png”,”profile_sidebar_border_color”:”FFFFFF”,”id”:7080152,”followers_count”:1211377,”status”:{“possibly_sensitive”:false,”in_reply_to_status_id”:null,”truncated”:false,”in_reply_to_status_id_str”:null,”in_reply_to_screen_name”:null,”geo”:null,”source”:”web”,”in_reply_to_user_id_str”:null,”place”:null,”favorited”:false,”coordinates”:null,”id”:337745769207570433,”contributors”:null,”in_reply_to_user_id”:null,”id_str”:”337745769207570433″,”retweet_count”:54,”text”:”1\u9031\u9593\u306e\u9593\u306b\u30ea\u30c4\u30a4\u30fc\u30c8\u304c\u591a\u304b\u3063\u305f\u30c4\u30a4\u30fc\u30c8\u3067\u3059\u3002\u30e9\u30c6\u30a2\u30fc\u30c8\u306e\u9032\u6b69\u306b\u3082\u9a5a\u304d\u307e\u3059\u304c\u3001\u3053\u3093\u306a\u3068\u3053\u308d\u3067\u30d2\u30fc\u30ed\u30fc\u306b\u51fa\u4f1a\u3063\u305f\u6642\u306e\u30b7\u30e7\u30c3\u30af\u306f\u2026\u3002http:\/\/t.co\/ycbubP2iY6″,”retweeted”:false,”created_at”:”Fri May 24 01:44:01 +0000 2013″},”profile_background_tile”:true,”default_profile_image”:false,”profile_sidebar_fill_color”:”DDEEF6″,”is_translator”:false,”profile_banner_url”:”https:\/\/pbs.twimg.com\/profile_banners\/7080152\/1348004306″,”geo_enabled”:true,”lang”:”ja”,”favourites_count”:5,”utc_offset”:32400,”profile_image_url_https”:”https:\/\/si0.twimg.com\/profile_images\/3407356865\/62f0d53222361fbd2c1fe9889f4cc559_normal.png”,”screen_name”:”TwitterJP”,”profile_background_color”:”C0DEED”,”created_at”:”Tue Jun 26 01:54:35 +0000 2007″,”profile_background_image_url_https”:”https:\/\/si0.twimg.com\/profile_background_images\/669680452\/4158abf21ac976cdb0459ccf14acd5f2.jpeg”,”protected”:false,”description”:”\u65e5\u672c\u8a9e\u7248Twitter\u516c\u5f0f\u30a2\u30ab\u30a6\u30f3\u30c8\u3067\u3059\u3002″,”profile_background_image_url”:”http:\/\/a0.twimg.com\/profile_background_images\/669680452\/4158abf21ac976cdb0459ccf14acd5f2.jpeg”,”default_profile”:false,”following”:null,”verified”:true,”profile_link_color”:”0084B4″,”contributors_enabled”:false,”friends_count”:4307,”statuses_count”:2143,”id_str”:”7080152″,”profile_use_background_image”:true,”profile_text_color”:”333333″,”listed_count”:16516,”url”:”http:\/\/blog.jp.twitter.com”,”notifications”:null}]

JSONライブラリで戻す

Rubyを使っているなら、特に難しいことを考えずにJSONに読み込ませればunescapeしてくれます。

require 'json'
require 'open-uri'
url = 'http://api.twitter.com/1/users/lookup.json?screen_name=TwitterJP'
JSON.load(open(uri).read)

[{“name”=>”TwitterJP”, “time_zone”=>”Tokyo”, “follow_request_sent”=>false, “location”=>”東京赤坂”, “profile_image_url”=>”http://a0.twimg.com/profile_images/3407356865/62f0d53222361fbd2c1fe9889f4cc559_normal.png”, “profile_sidebar_border_color”=>”FFFFFF”, “profile_image_url_https”=>”https://si0.twimg.com/profile_images/3407356865/62f0d53222361fbd2c1fe9889f4cc559_normal.png”, “id”=>7080152, “followers_count”=>1211378, “status”=>{“possibly_sensitive”=>false, “in_reply_to_status_id”=>nil, “truncated”=>false, “in_reply_to_screen_name”=>nil, “geo”=>nil, “source”=>”web”, “place”=>nil, “favorited”=>false, “coordinates”=>nil, “id”=>337745769207570433, “in_reply_to_status_id_str”=>nil, “contributors”=>nil, “in_reply_to_user_id”=>nil, “id_str”=>”337745769207570433”, “retweet_count”=>54, “text”=>”1週間の間にリツイートが多かったツイートです。ラテアートの進歩にも驚きますが、こんなところでヒーローに出会った時のショックは…。http://t.co/ycbubP2iY6”, “in_reply_to_user_id_str”=>nil, “retweeted”=>false, “created_at”=>”Fri May 24 01:44:01 +0000 2013”}, “profile_background_tile”=>true, “default_profile_image”=>false, “profile_sidebar_fill_color”=>”DDEEF6”, “profile_banner_url”=>”https://pbs.twimg.com/profile_banners/7080152/1348004306”, “geo_enabled”=>true, “lang”=>”ja”, “favourites_count”=>5, “utc_offset”=>32400, “screen_name”=>”TwitterJP”, “profile_background_color”=>”C0DEED”, “created_at”=>”Tue Jun 26 01:54:35 +0000 2007”, “profile_background_image_url_https”=>”https://si0.twimg.com/profile_background_images/669680452/4158abf21ac976cdb0459ccf14acd5f2.jpeg”, “protected”=>false, “description”=>”日本語版Twitter公式 カウントです。”, “profile_background_image_url”=>”http://a0.twimg.com/profile_background_images/669680452/4158abf21ac976cdb0459ccf14acd5f2.jpeg”, “default_profile”=>false, “following”=>false, “verified”=>true, “profile_link_color”=>”0084B4”, “contributors_enabled”=>false, “friends_count”=>4307, “statuses_count”=>2143, “is_translator”=>false, “id_str”=>”7080152”, “profile_use_background_image”=>true, “profile_text_color”=>”333333”, “listed_count”=>16516, “url”=>”http://blog.jp.twitter.com”, “notifications”=>false}]

手動で戻す

しかし、何らかの事情で、通常ファイルやデータベースにこのUnicodeエスケープシーケンスが含まれていることもあるでしょう。
そんなときは、packを使って手動で処理してやります。

str = 'Twitter\u516c\u5f0f\u30a2\u30ab\u30a6\u30f3\u30c8\u3067\u3059\u3002'
puts str.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*') }
# => 日本語版Twitter公式アカウントです。

文字参照(16進表記)を解決する

HTMLなどで、文字参照の形式になっていることがあります。

Twitter公式アカウントです。

HTMLのほか、PDFから文字列を抽出するライブラリで、文字列の途中から急に文字参照になっていることもありました。

これも、先ほどと全く同じ方法で戻せます。

str.gsub(/&#x([\da-fA-F]+);/) { [$1].pack('H*').unpack('n*').pack('U') }

文字参照(10進表記)を解決する

文字参照は、10進表記もよく使われます。

Twitter公式アカウントです。

この場合も似たような感じで処理してやりましょう。

str.gsub(/&#(\d+);/) { [$1.to_i].pack('U') }

文字参照に関してはhtmlentitiesを使う方が簡単ですが、やっていることは似たような感じです。

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

baba

ゆとりプログラマー。 高校時代から趣味でプログラミングを初め、そのままコードを書き続けて現在に至る。慶應義塾大学環境情報学部(SFC)卒業。BPS設立初期に在学中から参加している最古参メンバーの一人。Ruby on Rails、PHP、Androidアプリ、Windows/Macアプリ、超縦書の開発などを気まぐれにやる。軽度の資格マニアで、情報処理技術者試験(15区分 + 情報処理安全確保支援士試験)、技術士(情報工学部門)、CITP、Ruby Programmer Goldなどを保有。

babaの書いた記事

週刊Railsウォッチ

インフラ

BigBinary記事より

ActiveSupport探訪シリーズ