Python2系で文字参照のエスケープとか
ほぼ同様の関数はHTMLParser.HTMLParser.unescape
やlxml.html.fromstring
あたりにもあるけれど、
前者は
U+10000-U+10FFFF
が無理HTMLParser.HTMLParser()
を一度生成しないと使えない←結構気に食わない
だし、後者はunescape
のためにわざわざlxml
をimport
するのも気が引けるので。
2系に限っているのは3系には既にあるため。
基本戦略はHTMLParser.unescape
と同じく(←書いてから知った)re.sub
を使う方法で。
re.sub
は第3引数(置換後文字列)に関数オブジェクトを置けるというのがポイントで、そこに文字参照→対応するユニコード文字(列)なる関数を置いてやればよい。
def unescape_charref(escaped): return re.sub('CHARREF REGEXP',lambda s:REPLACE(s),escaped)
名前または数字の解決
正規表現で簡単に書ける。
r'&((?P<char>[a-z]+)|#(?P<decimal>\d+)|#x(?P<hex>[\da-f]+));'
マッチした場合入れ子になった3グループのうち必ず2グループは空文字列となり、1グループは空文字列にならない。
よって、この正規表現にマッチしたre.MatchObject
をmatchobj
として、
from htmlentitydefs import name2codepoint def getunichr(matchobj): if matchobj.group('char'): # Character entity references num=name2codepoint.get(matchobj.group('char'),0) elif matchobj.group('decimal'): # Numeric character reference (decimal) num=int(matchobj.group('decimal')) elif matchobj.group('hex'): # Numeric character reference (hexadecimal) num=int(matchobj.group('hex'),16) return num
という感じにすればよい。これで名前→数値はOK。
UCS-2とUCS-4
Python処理系はUCS-2あるいはUCS-4がビルド時にオプションとして指定されていて、その違いによりunichr
等の挙動が異なる。
ちなみにPython.orgのWin-x64のバイナリは(俺のがそうなので多分)UCS-2が指定されている。この場合unichrは0<num<0x10000
つまりU+0000-U+FFFF
の部分しか対応していない。
UCS-2とUCS-4の区別はsys.maxunicode
でできる。
リンク先のコピペに近いけど、
from sys import maxunicode def uchr(c): if 0<c<=maxunicode: # BMP(UCS-2) / whole(UCS-4) return unichr(c) elif maxunicode<c<=0x10ffff: # SMP(UCS-2) / None(UCS-4) c-=0x10000 return unichr(c>>10|0xD800)+unichr(c&0x3FF|0xDC00) else: # c==0 or c is out of Unicode return ''
という感じ。Unicodeから外れてたらとりあえず空文字列を返す。
SMPに対応する意味あんの?とか実は書きながら思ってたんだけど、最近で言うと絵文字とかもここに属するらしいので、案外重要なのかもしれない。
まとめて
こういう感じになりました。
本当に3系と同等にするのならばhtmlentitydefs.name2codepoint
でなく3系のhtml.entities.html5
あたりも対応すべきかもしれないけど、面倒なので省略。