Pygments - ユニコードとエンコーディング
初回公開:2018/10/14
最終更新:未
【 目次 】
ここでユニコードの扱いについて復習しておこう。
lexer
すべてのレクサーはユニコード文字列を内部的に使用します。
そのため、間違ったエンコーディングで文字列を渡すと、時折UnicodeDecodeErrorが発生することがあります。
デフォルトでは、すべてのレクサーの入力エンコードがguess(推測)に設定されています。
つまり、次のエンコーディングが試されます。
- UTF-8(BOM処理を含む)
- ロケールのエンコーディング(つまり、locale.getpreferredencoding()の結果)
- 最後の手段として、latin1
レクサーにバイト文字列オブジェクト(ユニコードではない)を渡すと、このエンコーディングを使用してデータをデコードしようとします。
encodingまたはinencodingレクサーオプションを使用して、エンコーディングをオーバーライドできます。
lexerのencodingオプションとinencodingオプション
lexerクラスのコンストラクタのソース(C:\Python27\Lib\site-packages\pygments\lexer.py)をのぞいてみるとencodingオプションとinencodingオプションに以下のような処理をおこなっている。
def __init__(self, **options): === 途中略 === self.encoding = options.get('encoding', 'guess') self.encoding = options.get('inencoding') or self.encoding === 以降略 ===
つまりinencodingオプションとencodingオプションが指定されていない場合はinencodingオプションが優先され、指定されたいない場合はencodingが、また両方とも指定されていない場合はguess
「guess」(推測)となる。
chardet
chardetライブラリがインストールされていて、エンコーディングがchardetに設定されているなら、 chardetはエンコードはテキストを分析し、それは自動的に正しい考えているエンコーディングを使用します。
from pygments.lexers import PythonLexer lexer = PythonLexer(encoding='chardet')
Pygmentsのunicodeオブジェクトを渡すのが最善の方法です。
その場合、予期せぬ出力を得ることはありません。
formatter
フォーマッタは、出力encodingを設定しないと、Unicodeオブジェクトをストリームに送ります。
フォーマッタにencodingオプションを渡すことで、そうすることができます
from pygments.formatters import HtmlFormatter f = HtmlFormatter(encoding='utf-8')
ソースに非ASCII文字があり、出力ストリームがUnicodeを受け取っていない場合、このオプションを設定する必要があります。
これは、すべての通常のファイルとターミナルスの場合に当てはまります。
注:ターミナルフォーマッタはスマートになります。
出力ストリームに エンコーディング属性があり、オプションを設定していない場合、このエンコーディングを持つUnicode文字列をエンコードしてから書き込みます。
例えば、sys.stdoutの場合です 。他のフォーマッターにはそのような振る舞いはありません。
別の注記:Pygmentsをコマンドライン経由で呼び出す(pygmentize)と、エンコーディングは異なった方法で処理されます。
コマンドラインのドキュメントを参照してください。
encodingオプションが指定されていればオーバーライドするoutencodingオプションも受け入れるようになりました。
これにより、レクサーとフォーマッタで単一のオプション辞書を使用することができますが、入力と出力のエンコーディングは変わりません。
formatterのencodingオプションとoutencodingオプション
formatterクラスのコンストラクタのソース(C:\Python27\Lib\site-packages\pygments\formatter.py)をのぞいてみるとencodingオプションとoutencodingオプションに以下のような処理をおこなっている。
def __init__(self, **options): === 途中略 === self.encoding = options.get('encoding', None) or None if self.encoding in ('guess', 'chardet'): # can happen for e.g. pygmentize -O encoding=guess self.encoding = 'utf-8' self.encoding = options.get('outencoding') or self.encoding self.options = options
つまりoutencodingオプションがencodingオプションを上書きするようになっている。
公式ドキュメントを訳してみたけど、lexerにencodingオプションとinencodingオプションが、そしてformatterにencodingオプションとoutencodingオプションが。
なぜ二つのエンコードオプションが存在する必要があるのか、理解不足で意味不明。