对于 UTF-8 编码的文件如下:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
%
\newwrite\outtmp
\immediate\openout\outtmp=out.tmp
%
\begin{document}
\newcommand{\foo}{Résumé}
\foo
\immediate\write\outtmp{\foo}%
\end{document}
输出out.tmp
文件采用 ISO-8859-1 编码。原因:
- 不是 UTF-8?
- ISO-8859-1 而不是其他的?
答案1
这里的要点是输入文件不是 LaTeX,而是 LaTeX 和纯 TeX 的混合体,或者更确切地说是 LaTeX 文件中不支持的 TeX 原语(它们出现在包中和内核中,但使用它们意味着必须了解(有时没有正确记录的限制和解决它们的约定)。
因此 TeX 的书写方式就像排版一样(给定特定的字体编码(而不是文件编码)),@egreg 在他的回答中完美地描述了这一点。
LaTeX 在很大程度上定义了一种可以转换为各种字体编码或其他编码的内部编码,如果您想要获得 utf-8 输出,则需要首先切换到输出 utf-8 的“字体”编码,即将\'e
(LICR = LaTeX 内部字符编码)之类的内容转换为两个 utf-8 字节。现在还不存在,但从技术上讲可以正确设置。
使用\protected@write
LaTeX 的方式在 7 位上透明地输出内容,这样就可以读回,而不管当时有效的输入编码是什么
答案2
LaTeX 如何实现 UTF-8?
Unicode 字符é
在 UTF-8 中被编码为两个字节,准确地说<C3><A9>
(我将在本文中使用字节来表示,当它们是 TeX 的字符标记时也是如此)。\usepackage[utf8]{inputenc}
加载时,字节<C3>
被制作成积极的并定义为寻找以下字节,因为<C3>
在UTF-8中标记双字节字符。
因此 LaTeX 会收集<A9>
并形成控制序列
\csname u8:\string<C3>\string<A9>\endcsname
其定义为扩展为
\IeC {\@tabacckludge 'e}
可以看出
\documentclass{article}
\usepackage[utf8]{inputenc}
\begin{document}
\expandafter\show\csname u8:\string^^c3\string^^a9\endcsname
^^c3
TeX 表达我所表示的意思的方式是。<C3>
在终端上我们得到
> \u8:é=macro:
->\IeC {\@tabacckludge 'e}.
<recently read> \u8:é
l.4 ...r\show\csname u8:\string?\string?\endcsname
(é
第一行是因为我的终端设置为UTF-8)。
做什么\write
?
该操作\write
采用表示输出流的第一个参数和带括号的第二个参数,即完全展开写入操作实际执行的时间。所以我们需要知道要做什么\IeC
,然后\@tabacckludge
做。
在上面的例子中添加\show\IeC
和\makeatletter\show\@tabacckludge
,在终端上首先显示
> \IeC=macro:
->\ifx \protect \@typeset@protect \expandafter \@firstofone \else \noexpand \IeC \fi .
进而
> \@tabacckludge=macro:
#1->\expandafter \@changed@cmd \csname \string #1\endcsname \relax .
好的,我们还需要\@changed@cmd
,但本质上它只是起到了相当于的作用\'e
,因为我们不在tabbing
环境中。
就你的情况来说,\protect
是 \@typeset@protect
,就像平常一样;所以当我们这样做
\write\openout{é}
我们首先得到
\IeC{\@tabacckludge 'e}
并且由于条件为真,因此
\@firstofone{\@tabacckludge 'e}
进而变成
\@tabacckludge 'e
进而
\'e
这引发了复杂的发展,最终导致
\char223
因为声明
\DeclareTextComposite{\'}{T1}{e}{233}
已经t1enc.def
通过 加载了\usepackage[T1]{fontenc}
。现在 TeX 才真正写入一些东西,确切地说是字节数 233(十进制),也就是字节<E9>
。
<E9>
Latin-1 中的 恰好是,这其实并不是巧合é
,因为 T1 编码与 Latin-1 有很多共同之处。但并非全部。
我们如何使用 LaTeX(而不是 (Xe|Lua)LaTeX)编写 UTF-8?
您不希望发生扩展:
\write\outtmp{\unexpanded{Résumé}}
或者,不使用\unexpanded
,
\toks0={Résumé}
\write\outtmp{\the\toks0}
例子
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\begin{document}
\newwrite\outtmp
\immediate\openout\outtmp=\jobname.tmp
\immediate\write\outtmp{Résumé}
\immediate\write\outtmp{\unexpanded{Résumé}}
\toks0={Résumé}
\immediate\write\outtmp{\the\toks0 }
\stop
less
从写出的文件中得出的结果是
R<E9>sum<E9>
Résumé
Résumé
(总是因为终端是 UTF-8)。没有解释,我得到
R<E9>sum<E9>
R<C3><A9>sum<C3><A9>
R<C3><A9>sum<C3><A9>
因此第一行是错误的,而其他两行是预期的。
隐藏Résumé
在宏中只会让事情变得更加困难,因为你想扩大它。所以
\write\outtmp{\unexpanded\expandafter{\foo}}
会做。
还有什么?
如果你使用\protected@write
,那么事情就不同了:
\protected@write\outtmp{}{Résumé}
你被写
R\IeC {\'e}sum\IeC {\'e}
因为在这种情况下\protect
不是\@typeset@protect
,所以遵循错误分支。由于与有关的原因相同,因此的复杂转换\@tabacckludge 'e
最终为。这可能是您想要的,也可能不是您想要的。当然,该标记列表会打印为“Résumé”。\'e
\protect