ソースコード掲載(listings / minted)

LaTeX でソースコードを組むとき、listingsminted のどちらを選ぶかは配色の好みの問題ではありません。listings は TeX のマクロだけでシンタックスハイライトを実現していて、「どの語が予約語か」を人間が書いた一覧表として抱えています。minted はその仕事を Python 製の字句解析器 Pygments に丸ごと預けるので、色分けの精度は段違いですが、そのぶん LaTeX の外へ出ていきます。品質を取るか、可搬性を取るか——長らくこの二択でしたが、minted 3 でその前提そのものが書き換わりました。このページは、その現在地から二つのパッケージを整理します。

listingsminted の違い

違いは一点、ハイライトを誰が行うかです。listings は純粋な LaTeX マクロだけで完結するため、\usepackage{listings} と書けば Overleaf でも設定を触れない計算機室でもそのまま動きます。minted は外部プログラムを呼び出して解析結果を受け取るので、ハイライトの正確さでは勝ちますが、その外部プログラムが使える環境かどうかが前提条件になります。どちらを選ぶかは、突きつめれば「自分の文書が最終的にどこでコンパイルされるか」の予測です。

その「手書きの一覧表」は比喩ではありません。listings の言語定義は lstlang1.stylstlang3.sty という三つのファイルに収められていて、中身は \lst@definelanguage{ACSL}[90]{Fortran}{morekeywords={algorithm,cinterval,...}} のような、予約語をカンマで並べた記述の連続です。TeX Live 2024 同梱版で数えると約 95 言語。対する Pygments は 2006 年から Georg Brandl らが開発してきた独立した字句解析ライブラリで、手元の Pygments 2.19 で pygmentize -L lexers を実行すると 597 個のレキサーが並びます。数の差もさることながら、片方は語の一覧、もう片方は文法に沿って字句を切り出す解析器という、原理からして別物です。

listingsminted
highlightingキーワード表による近似Pygments による本物の字句解析
external toolsなし(純粋な LaTeX マクロ)Pygments。minted 3 は latexminted を同梱
-shell-escape不要minted 2 系は必須。TeX Live 2025 以降の 3 系は不要
languages約 95(TeX Live 2024 同梱)597 レキサー(Pygments 2.19)
UTF-8pdfLaTeX では致命的エラーで停止pdfLaTeX では文字が脱落(PDF は出る)

listings の基本——lstlisting 環境と \lstinputlisting

入り口は三つだけです。文書に直接コードを書くなら lstlisting 環境、外部ファイルをそのまま取り込むなら \lstinputlisting{sample.py}、本文の途中に短い断片を埋めるなら \lstinline を使います。そして見た目の指定は、呼び出しのたびに書くのではなく、プリアンブルで \lstset{...} に一度だけまとめるのが定石です。listings のオプションは百を超えますが、実際に触るのは次のコード例に出てくる十数個で足ります。

document.tex
\usepackage{listings}
\usepackage{xcolor}   % needed for the \color{...} styles below

\lstset{
  language=Python,
  basicstyle=\ttfamily\small,      % base font for the code
  keywordstyle=\color{blue}\bfseries,
  commentstyle=\color{teal}\itshape,
  stringstyle=\color{red!60!black},
  numbers=left,                    % line numbers in the left margin
  numberstyle=\tiny\color{gray},
  frame=single,                    % draw a thin frame around the block
  breaklines=true,                 % wrap lines that are too long
  showstringspaces=false,
  tabsize=2,
}

\begin{lstlisting}[caption={Factorial, computed recursively}, label=lst:fact]
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)
\end{lstlisting}

% pull in lines 37-45 of an external file, without line numbers
\lstinputlisting[language=C, firstline=37, lastline=45, numbers=none]{sample.c}

\lstset のキーは役割ごとに読めば覚えやすくなります。書体を決めるのが basicstyle\ttfamily\small が定番)、意味づけの色を決めるのが keywordstylecommentstylestringstyle周辺の装飾numbers=left(左に行番号、書体は numberstyle)と frame=single(枠線)です。breaklines=true は紙幅からはみ出す長い行を折り返す指定で、これを忘れるとコードが右余白を突き抜けます——実務でもっともよく踏む落とし穴です。caption=label= を与えるとブロックは図表と同じ「番号付きリスト」になり、\ref{lst:fact} で参照できます。

プリアンブルの設定は、個々のブロックの [ ] でいつでも上書きできます。\begin{lstlisting}[language=C, numbers=none] と書けば、その一箇所だけ C 言語かつ行番号なしになります。外部ファイルではさらに firstline=lastline= が効き、\lstinputlisting[firstline=37, lastline=45]{sample.c} のように必要な行だけを切り出せるのが実務では効きます——コードを本文にコピーせずに済むので、元のファイルを直せば文書も自動的に追随します。本文への埋め込みは \verb と同じ流儀で、\lstinline|while (i < n)| のように任意の記号を区切りに選びます。

日本語・中国語・韓国語を入れると Invalid UTF-8 byte sequence で止まる

これはパッケージの問題ではなく、エンジンの問題です。pdfLaTeX でコンパイルすると、コード中の漢字やハングルは listings では ! LaTeX Error: Invalid UTF-8 byte sequence という致命的エラーになり、PDF は一枚も出ません。minted に替えても解決しません——そちらは ! LaTeX Error: Unicode character を出したうえで、その文字を黙って落とした PDF を作ります。どちらも「一バイト=一文字」という pdfTeX の前提に、多バイト文字が収まらないことから来ています。

listingsutf8 を読み込めば直る、という助言が広く出回っていますが、CJK には効きません。このパッケージの README がその理由を明言しています——救済策が成立するのは「ファイルを変換できる一バイト符号化が存在する場合」だけで、しかも \lstinputlisting にしか作用しません。ヨーロッパ言語のアクセント付き文字なら latin1 などに落とせますが、漢字・かな・ハングルを収める一バイト符号化は存在しないので、変換先がありません。実際に \lstinputlisting[inputencoding=utf8/latin1]{sample.py} を通すと、エラーは消えるかわりにその文字が出力から消え失せます。エラーが消えたことを成功と勘違いしやすい、たちの悪い症状です。

本当の解決はエンジンを替えることです。XeLaTeX か LuaLaTeX で組めば、どちらのエンジンも入力を最初から Unicode として扱うため、listings も minted も日本語コメント入りのコードをそのまま通します。あとは等幅フォントにその文字が入っていればよく、fontspec\setmonofont で CJK を含む書体を指定します。ここで一つ注意——フォントは自分の言語しか持っていないことがあります。日本語フォントで中国語簡体字やハングルを組もうとすると Missing character の警告が並び、その文字だけが消えます。多言語のコードを載せるなら、対象の文字をすべて含む書体を選んでください。なお、ヨーロッパ言語のアクセント文字を数個だけ通したいなら、pdfLaTeX のまま \lstset{literate={é}{{\'e}}1} のように一文字ずつ教える古典的な手も使えます。

document.tex
% Compile this with xelatex or lualatex, not pdflatex.
% A monospaced face that actually covers the script you use.
\usepackage{fontspec}
\setmonofont{Noto Sans Mono CJK JP}

\usepackage{listings}
\lstset{basicstyle=\ttfamily\small}

\begin{lstlisting}[language=Python]
def factorial(n):
    # a comment written in your own language survives here
    return 1 if n <= 1 else n * factorial(n - 1)
\end{lstlisting}

minted の基本——\begin{minted}{python}\inputminted

構えは listings とほぼ同じで、違うのは言語を必須の引数として渡す点です。\begin{minted}{python} のように環境の引数に言語名を書き、外部ファイルなら \inputminted{python}{sample.py}、本文中の断片なら \mintinline{python}{print("hi")} と書きます。言語を省略できないのは、Pygments 側でレキサーを一つ選ばなければ解析が始まらないからです。listings のように「プリアンブルで一度決めて以後は省く」ことはできません。

document.tex
\usepackage{minted}

\usemintedstyle{monokai}          % one colour theme for the whole document
% \setminted{style=monokai, linenos, fontsize=\small}  % broader defaults

\begin{minted}[linenos, bgcolor=black!90, fontsize=\small]{python}
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)
\end{minted}

\mint{python}|print("Hello!")|            % one line, no environment
\mintinline{python}{print("Hello!")}     % inside running text
\inputminted[linenos]{python}{sample.py} % a whole external file

% "text" turns highlighting off without leaving minted
\begin{minted}{text}
plain output, no keywords coloured
\end{minted}

オプションは環境名の直後の [ ]key=value で並べます。よく使うのは行番号を出す linenos、Pygments の配色を選ぶ style=、背景色の bgcolor=、文字サイズの fontsize= です。文書全体に同じ配色を当てるなら \usemintedstyle{monokai}、既定値をまとめて決めるなら \setminted{style=monokai, linenos} を使います。Pygments が知らない言語や、あえて色を付けたくない箇所では言語名に text を指定してください。なお \mint はインライン用ではありません——一行のコードのために環境を書く手間を省くだけの命令で、本文に溶け込ませたいときは必ず \mintinline を使います。

-shell-escape が必要になる理由と、不要になった条件

minted は組版の途中で外部プログラムを起動するので、LaTeX に外部コマンドの実行を許す シェルエスケープ が要ります。許可なしで走らせると ! Package minted Error: You must invoke LaTeX with the -shell-escape flag. で止まります。pdfLaTeX なら -shell-escape を、MiKTeX なら -enable-write18 を付けてコンパイルしてください。

terminal
pdflatex -shell-escape document.tex
xelatex  -shell-escape document.tex

# MiKTeX uses the older spelling
pdflatex -enable-write18 document.tex

ここが minted 3 で変わった点です。従来は Python と Pygments を自分で入れたうえで無制限のシェルエスケープを開ける必要があり、それが「品質を取ると可搬性を失う」というこのページ冒頭の二択の正体でした。minted 3 は Python 側を latexminted という専用の実行ファイルにまとめ、TeX 配布に Python ホイールとして同梱する方式に変えています。作者 Geoffrey M. Poore はこれを「制限付きシェルエスケープの実行ファイルに対する LaTeX のセキュリティ要件に適合するよう設計した」と説明しています。その結果 TeX Live 2025 では latexminted が制限付きシェルエスケープの許可リストに入り、-shell-escape を付けずにコンパイルできるようになりました。Pygments を別途インストールする手間も消えています。

ただし手元の環境が古ければ話は別です。TeX Live 2024 に同梱されているのは minted 2.9(2023 年 12 月)で、これは従来どおり -shell-escape がなければ動きません。自分がどちらの世界にいるかは、上のエラーが出るかどうかで判別できます。そしてシェルエスケープを開けるということは、その文書に任意の外部コマンドを実行させる権限を与えるということです。出所の分からない .tex-shell-escape 付きで走らせてはいけません。 学会や出版社の投稿システムがシェルエスケープを禁じていることがあるのも同じ理由で、投稿前に一度、-shell-escape なしで通るかを確かめておくと安全です。

外部プロセスを呼ぶぶん、minted のコンパイルは listings より遅くなります。それを埋め合わせるのが キャッシュ で、minted はハイライト済みの断片を作業用ディレクトリに保存し、コードが変わらないかぎり Pygments を呼び直しません。手元の TeX Live 2024 で document.tex を組むと _minted-document/ というディレクトリができ、中にはコード片のハッシュを名前にした .pygtex ファイルが並びます。二回目以降が目に見えて速くなるのはこの仕組みのおかげです。キャッシュは cache=false で無効にでき、色がおかしいときや配色を変えたのに反映されないときは、このディレクトリごと消すのが手っ取り早い対処です。バージョン管理には入れないでください。

結局どちらを使うべきか

判断の軸は「その文書がどこでコンパイルされるか」の一本です。自分の手元だけ、あるいは Overleaf のように整った環境で組むなら minted の色分けが確実に上ですし、minted 3 と TeX Live 2025 以降の組み合わせなら以前のような代償も払わずに済みます。逆に、共著者の環境も投稿先の処理系も選べないなら、listings が黙って動くという性質は色の精度より価値があります。迷ったら、次の順に当てはめてください。

  • 外部ツールを入れられない、シェルエスケープが使えない → listings 一択。\usepackage{listings} だけで完結します。
  • ハイライトの正確さと対応言語の広さが最優先 → minted。Pygments による色分けは別格です。
  • コード中に日本語・中国語・韓国語が入る → パッケージではなくエンジンを替えます。XeLaTeX か LuaLaTeX で組み、\setmonofont でその文字を含む等幅フォントを指定してください。
  • 色分けも行番号も要らず、打ち込んだ通り出したいだけverbatimfancyvrb のほうが軽量です。
  • 動くコードではなく擬似コードでアルゴリズムを書きたいalgorithm2ealgpseudocode が専用の道具です。