Linuxなどのメモ書き
リンク

その他のWiki
Linuxメモ
Xnuメモ

会社
(有)ビットハイブ
受託開発やってます。

よくやる仕事

・Webシステム開発(LAMP環境)
・Linuxサーバー設定関連
サーバー移転作業代行

開発事例にデジタルカタログ/マンガビューワーを追加しました。

検索

Adsense

Python3で文字列中の16進コードを変換


概要

文字列中に'\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88test'のように\xhh形式の16進コードが埋め込まれている場合に、これを元の日本語(「テストtest」)に戻したい。URLに日本語を含むようなページのあるWebサイトだと、Apacheのアクセスログに\xhhのような16進コードが埋め込まれていることがある(*1)ので、これを元の日本語で確認したいためだ。

(*1) http://httpd.apache.org/docs/2.4/ja/mod/mod_log_config.html より。

セキュリティ上の理由により 2.0.46 より、 %r, %i, %o に入っている、 印字不可能な文字と他の特別な文字は、\xhh という形式の文字列でエスケープされるようになりました。hh は そのままのバイトの値の 16 進での値です。この規則の例外には、 バックスラッシュを使ってエスケープされる " と \ と、 C 形式の表記法が使われる空白文字 (\n, \t など) があります。2.0.46 以前のバージョンではエスケープ処理は行われませんので、 生ログファイルを扱う際に注意が必要です。

Perlでは

Perlでは以下のようにできるのを今回はPython3で行いたい。

#!/usr/bin/perl

while (my $line = <>) {
    $line =~ s/\\x([0-9a-fA-F]{2})/pack("H2",$1)/eg;
    print $line;
}

 

Pythonでは

Python3では以下のように処理できた(\xhhで16進化されている箇所はUTF-8でエンコーディングされているとする)。

#!/usr/bin/python3

import sys
import re

for line in sys.stdin:
    line = re.sub(rb'\\x([0-9a-fA-F]{2})', lambda m:  bytes([int(m.group(1), 16)]), line.encode('utf-8')).decode('utf-8')
    print(line, end = '')

基本的にPerlの処理と同じだが、置換処理の前後でstring <-> bytesの変換を行っている点に注意が必要。最初、stringのまま処理しようとしてハマった。

ログの例

127.0.0.1 - - [04/May/2018:13:58:40 +0900] "GET /\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88test HTTP/1.1" 301 264 "-" "curl/7.55.1"

 

実行例

# python3 conv.py < access_log
127.0.0.1 - - [04/May/2018:13:58:40 +0900] "GET /テストtest HTTP/1.1" 301 264 "-" "curl/7.55.1"

 

まあ、Perlでやってもよかったのだが。

 


最終更新 2018/05/04 17:58:11 - kztomita
(2018/05/04 17:58:11 作成)