Quellcode-Listings

Die Wahl zwischen listings und minted für Quellcode in LaTeX ist keine Frage des Farbgeschmacks. listings erzeugt seine Syntaxhervorhebung allein mit TeX-Makros, und sein gesamtes Wissen über eine Sprache besteht aus einer von Hand geschriebenen Liste, welche Wörter als Schlüsselwörter gelten. minted übergibt diese Aufgabe vollständig an Pygments, einen in Python geschriebenen Lexer; die Färbung spielt dadurch in einer anderen Liga, dafür verlässt das Paket den Bereich von LaTeX. Qualität oder Portabilität – jahrelang lief es auf diese Alternative hinaus, bis minted 3 die Voraussetzungen neu geschrieben hat. Diese Seite ordnet beide Pakete vom heutigen Stand aus.

Der Unterschied zwischen listings und minted

Der Unterschied läuft auf eine einzige Frage hinaus: wer die Hervorhebung vornimmt. listings ist in reinen LaTeX-Makros abgeschlossen, deshalb genügt \usepackage{listings} – auf Overleaf ebenso wie in einem Rechnerraum, dessen Konfiguration unantastbar ist. minted ruft ein externes Programm auf und liest dessen Analyse zurück; das gewinnt bei der Genauigkeit, macht aber die Verfügbarkeit dieses Programms zur Voraussetzung. Die Wahl zwischen beiden ist letztlich eine Prognose darüber, wo das eigene Dokument am Ende übersetzt wird.

Diese „von Hand geschriebene Liste“ ist keine Metapher. Die Sprachdefinitionen von listings stehen in drei Dateien, lstlang1.sty bis lstlang3.sty, und ihr Inhalt ist eine Folge von Einträgen der Form \lst@definelanguage{ACSL}[90]{Fortran}{morekeywords={algorithm,cinterval,...}} – Schlüsselwörter, durch Kommata getrennt. In der mit TeX Live 2024 ausgelieferten Fassung ergibt das rund 95 Sprachen. Pygments dagegen ist eine eigenständige Lexer-Bibliothek, die seit 2006 von Georg Brandl und anderen entwickelt wird; pygmentize -L lexers listet unter Pygments 2.19 genau 597 Lexer auf. Wichtiger als der Zahlenunterschied ist der prinzipielle: hier eine Wortliste, dort ein Analysator, der einen Zeichenstrom gemäß einer Grammatik in Token zerlegt.

listingsminted
highlightingNäherung über eine Schlüsselwortlisteechte lexikalische Analyse durch Pygments
external toolskeine (reine LaTeX-Makros)Pygments; minted 3 liefert latexminted mit
-shell-escapenicht nötigbei minted 2 erforderlich; bei minted 3 ab TeX Live 2025 nicht mehr
languagesetwa 95 (Stand TeX Live 2024)597 Lexer (Pygments 2.19)
UTF-8unter pdfLaTeX Abbruch mit fatalem Fehlerunter pdfLaTeX fallen Zeichen aus (ein PDF entsteht dennoch)

Grundlagen von listings: die Umgebung lstlisting und \lstinputlisting

Es gibt nur drei Einstiegspunkte. Code direkt im Dokument schreibt man in der Umgebung lstlisting, eine externe Datei bindet \lstinputlisting{sample.py} unverändert ein, und ein kurzes Fragment im Fließtext setzt \lstinline. Das Aussehen wird anschließend einmal in der Präambel mit \lstset{...} festgelegt, statt es an jeder Aufrufstelle zu wiederholen. listings bietet weit über hundert Optionen, doch das gute Dutzend aus dem folgenden Beispiel reicht im Alltag aus.

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}

Die Schlüssel von \lstset prägen sich leichter ein, wenn man sie nach Aufgaben gruppiert. Die Schrift bestimmt basicstyle (üblich ist \ttfamily\small). Die bedeutungstragende Farbe legen keywordstyle, commentstyle und stringstyle fest. Zur Umgebung des Blocks gehören numbers=left (Zeilennummern links, formatiert durch numberstyle) und frame=single (ein Rahmen). breaklines=true bricht Zeilen um, die über die Satzbreite hinausgehen; wer es vergisst, dessen Code läuft durch den rechten Rand hindurch – der Stolperstein, in den man in der Praxis am häufigsten tritt. Mit caption= und label= wird der Block zu einem nummerierten Listing auf einer Stufe mit Abbildungen und Tabellen und lässt sich mit \ref{lst:fact} referenzieren.

Die Einstellungen aus der Präambel lassen sich an jedem einzelnen Block in [ ] überschreiben. \begin{lstlisting}[language=C, numbers=none] macht genau diesen einen Block zu C-Code ohne Zeilennummern. Bei externen Dateien kommen zusätzlich firstline= und lastline= ins Spiel: \lstinputlisting[firstline=37, lastline=45]{sample.c} schneidet genau die benötigten Zeilen heraus – in der Praxis ein Gewinn, denn nichts wird ins Dokument kopiert, und eine Korrektur an der Originaldatei schlägt automatisch durch. Für Code im Fließtext gilt die Schreibweise von \verb: ein beliebiges Zeichen als Trenner wählen, etwa \lstinline|while (i < n)|.

Warum CJK im Listing mit Invalid UTF-8 byte sequence abbricht

Das ist kein Problem der Pakete, sondern ein Problem der Engine. Beim Übersetzen mit pdfLaTeX wird ein Han-Zeichen oder eine Hangul-Silbe im Code unter listings zum fatalen ! LaTeX Error: Invalid UTF-8 byte sequence – und es entsteht überhaupt kein PDF. Ein Wechsel zu minted hilft nicht: minted meldet ! LaTeX Error: Unicode character und erzeugt dann stillschweigend ein PDF, in dem das Zeichen fehlt. Beide Symptome haben dieselbe Wurzel: Mehrbytezeichen passen nicht in die Annahme von pdfTeX, ein Byte sei ein Zeichen.

Der weit verbreitete Rat, listingsutf8 zu laden, hilft bei CJK nicht. Die README des Pakets nennt den Grund unmissverständlich: Der Behelf greift nur, wenn eine Einbyte-Kodierung existiert, in die sich die Datei konvertieren lässt, und er wirkt ausschließlich bei \lstinputlisting. Akzentbuchstaben europäischer Sprachen lassen sich auf latin1 herunterbrechen, doch keine Einbyte-Kodierung fasst Han-Zeichen, Kana oder Hangul – es gibt schlicht kein Ziel für die Konvertierung. Führt man \lstinputlisting[inputencoding=utf8/latin1]{sample.py} tatsächlich aus, verschwindet zwar der Fehler, und mit ihm die Zeichen selbst aus der Ausgabe. Das ist gerade deshalb tückisch, weil ein stiller Durchlauf wie ein Erfolg aussieht.

Die eigentliche Lösung heißt: Engine wechseln. Wird mit XeLaTeX oder LuaLaTeX gesetzt – beide behandeln ihre Eingabe von vornherein als Unicode –, lassen listings wie minted Code mit japanischen Kommentaren unverändert durch. Danach muss nur noch die dicktengleiche Schrift die Zeichen enthalten; ausgewählt wird sie mit \setmonofont aus fontspec. Eine Warnung an dieser Stelle: Eine Schrift deckt meist nur ihre eigene Sprache ab. Setzt man vereinfachtes Chinesisch oder Hangul in einer japanischen Schrift, erscheint eine Reihe von Missing character-Warnungen, und genau diese Zeichen verschwinden. Für Code mit mehreren Schriftsystemen also eine Schrift wählen, die alle abdeckt. Geht es dagegen nur um eine Handvoll akzentuierter europäischer Buchstaben, funktioniert unter pdfLaTeX weiterhin der klassische Weg: sie einzeln beibringen, etwa mit \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}

Grundlagen von minted: \begin{minted}{python} und \inputminted

Der Aufbau gleicht dem von listings weitgehend, mit einem Unterschied: Die Sprache ist ein Pflichtargument. Ihr Name steht im Argument der Umgebung, etwa \begin{minted}{python}; für eine externe Datei lautet es \inputminted{python}{sample.py}, für ein Fragment im Fließtext \mintinline{python}{print("hi")}. Weglassen lässt sich die Sprache nicht, weil Pygments genau ein Lexer übergeben werden muss, bevor die Analyse überhaupt beginnen kann. Ein „einmal in der Präambel festlegen und danach weglassen“ wie bei listings gibt es hier nicht.

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}

Optionen stehen als key=value-Paare in [ ] direkt nach dem Umgebungsnamen. Gebräuchlich sind linenos für Zeilennummern, style= zur Wahl eines Pygments-Farbschemas, bgcolor= für einen Hintergrund und fontsize=. Ein Schema für das ganze Dokument setzt \usemintedstyle{monokai}, einen Satz Voreinstellungen \setminted{style=monokai, linenos}. Für eine Sprache, die Pygments nicht kennt, oder einen bewusst schmucklosen Block dient text als Sprachangabe. Ein Hinweis noch: \mint ist nicht der Inline-Befehl – es erspart lediglich die Umgebung um eine einzelne Codezeile. Für Code im Fließtext gehört immer \mintinline verwendet.

Warum minted -shell-escape braucht – und seit wann nicht mehr

minted startet mitten im Satz ein externes Programm und benötigt darum shell escape, also die Erlaubnis, dass LaTeX externe Befehle ausführen darf. Ohne sie bricht der Lauf mit ! Package minted Error: You must invoke LaTeX with the -shell-escape flag. ab. Unter pdfLaTeX ist mit -shell-escape zu übersetzen, unter MiKTeX mit -enable-write18.

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

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

Genau das hat minted 3 verändert. Früher musste man Python und Pygments selbst installieren und danach uneingeschränktes shell escape freigeben – darin bestand die eingangs erwähnte Alternative zwischen Qualität und Portabilität. minted 3 fasst die Python-Seite zu einer eigenen ausführbaren Datei namens latexminted zusammen und liefert sie als Python-Wheel innerhalb der TeX-Distributionen mit. Ihr Autor Geoffrey M. Poore beschreibt sie als eigens so entworfen, dass sie den Sicherheitsanforderungen von LaTeX an ausführbare Dateien für eingeschränktes shell escape genügt. Das Ergebnis: Unter TeX Live 2025 steht latexminted auf der Freigabeliste für eingeschränktes shell escape, und es lässt sich ganz ohne -shell-escape übersetzen – die separate Pygments-Installation entfällt ebenfalls.

Bei einer älteren lokalen Installation sieht es allerdings anders aus. TeX Live 2024 liefert minted 2.9 vom Dezember 2023 mit, und diese Fassung läuft weiterhin nicht ohne -shell-escape. In welcher Welt man sich befindet, verrät schlicht, ob die obige Fehlermeldung erscheint. Zudem heißt shell escape freizugeben, genau diesem Dokument die Erlaubnis zu erteilen, beliebige externe Befehle auszuführen. Eine .tex-Datei unbekannter Herkunft darf niemals mit -shell-escape laufen. Aus demselben Grund untersagen Einreichungssysteme von Konferenzen und Verlagen shell escape mitunter vollständig; vor dem Einreichen empfiehlt es sich daher, einmal zu prüfen, ob das Dokument auch ohne die Option übersetzt.

Der Aufruf eines externen Prozesses macht minted beim Übersetzen langsamer als listings. Ausgeglichen wird das durch Caching: minted legt jedes hervorgehobene Fragment in einem Arbeitsverzeichnis ab und ruft Pygments nur dann erneut auf, wenn sich der Code ändert. Beim Satz von document.tex unter TeX Live 2024 entsteht hier ein Verzeichnis _minted-document/ mit .pygtex-Dateien, die nach einem Hash des Codefragments benannt sind. Diesem Mechanismus verdankt man die spürbar schnelleren Folgeläufe. Abschalten lässt sich das Caching mit cache=false; sehen die Farben falsch aus oder schlägt ein gewechseltes Farbschema partout nicht durch, ist das Löschen des gesamten Verzeichnisses die schnellste Abhilfe. In die Versionsverwaltung gehört es nicht.

Was also nehmen

Im Grunde gibt es nur eine Achse: wo dieses Dokument übersetzt wird. Geschieht das ausschließlich auf dem eigenen Rechner oder in einer gepflegten Umgebung wie Overleaf, ist die Färbung von minted schlicht besser – und mit minted 3 unter TeX Live 2025 oder neuer entfällt der frühere Preis dafür. Lassen sich dagegen weder die Rechner der Mitautorinnen und Mitautoren noch die Verarbeitungskette am anderen Ende bestimmen, wiegt der Umstand, dass listings einfach funktioniert, schwerer als genaue Farben. Im Zweifel hilft die folgende Reihenfolge.

  • Externe Werkzeuge lassen sich nicht installieren oder shell escape nicht nutzen → eindeutig listings. Mehr als \usepackage{listings} ist es nicht.
  • Genauigkeit der Hervorhebung und Sprachabdeckung stehen an erster Stelle → minted. Die Färbung über Pygments spielt für sich allein.
  • Im Code stehen japanische, chinesische oder koreanische Zeichen → nicht das Paket wechseln, sondern die Engine. Mit XeLaTeX oder LuaLaTeX setzen und \setmonofont auf eine dicktengleiche Schrift richten, die das Schriftsystem abdeckt.
  • Weder Farben noch Zeilennummern, nur der Text wie eingegebenverbatim oder fancyvrb sind leichter.
  • Geschrieben werden soll Pseudocode statt lauffähigem Codealgorithm2e und algpseudocode sind die dafür gebauten Werkzeuge.