在德语中,像“z. B.”这样的缩写应该在中间留一个空格。在老式打字机时代,通常的写法是不留空格。即使在今天,我也经常在打字时不留空格。有没有办法自动用正确间距的版本(例如在\,
中间留空格)替换它们?
我使用 LuaLaTeX,因此 Lua 解决方案就可以了。
示例文档:
\documentclass{article}
\usepackage[ngerman]{babel}
\begin{document}
Dies ist z.B. ein Test.
\end{document}
z.\,B.
此输入应产生与文档中相同的输出。
答案1
(在与 OP 讨论并收到非常重要的编码帮助从@EgorSkriptunoff)
这是一个解决方案,它不预先指定所有应在内部句点(又称“句号”)后插入细空格的缩写列表。相反,它设置了一个模式匹配函数来捕获、、、、u.a.
以及更多此类情况。(请参阅下面代码中的函数以了解执行的确切模式匹配。)u.a.m.
u.v.a.m.
z.Zt.
Bem.d.Red.
insert_thinspaces
还要注意Lua 函数中的unicode.utf8.gsub
而不是的使用。这让代码能够正确处理缩写中可能出现的非 ASCII 编码字母,例如和。string.gsub
insert_thinspaces
ä
Ä
不利的一面(潜在的)是,如果缩写出现在句首,例如Z.T.
或U.U.
,则此解决方法无法捕获缩写;无论如何,您的平行答案目前也无法捕捉到这种情况,对吗?
process_input_buffer
Lua 函数通过名为 的 LaTeX 宏分配给回调\ExpandAbbrOn
。如果出于任何原因,您需要暂停 Lua 函数的运行,只需执行指令 即可\ExpandAbbrOff
。
代码会检查要处理的字符串是否位于逐字环境中,例如verbatim
、Verbatim
和lstlisting
;如果是,则不执行任何处理。而且,在最新迭代中,代码现在还会忽略参数中的材料排队-逐字类宏,例如\verb
、\Verb
、\lstinline
和\url
。当然,URL 字符串的内容永远不应该由 Lua 函数处理,对吧?
% !TeX program = lualatex
\documentclass{article}
\usepackage[ngerman]{babel}
\usepackage{fancyvrb} % for "Verbatim" env.
\usepackage[obeyspaces]{url} % for "\url" macro
\usepackage{listings} % for "\lstinline" macro
%% Lua-side code:
\usepackage{luacode} % for 'luacode*' environment
\begin{luacode*}
-- Names of verbatim-like environments
local verbatim_env = { "[vV]erbatim" , "lstlisting" }
-- By default, we're *not* in a verbatim-like env.:
local in_verbatim_env = false
-- Specify number of parameters for every macro; use neg. numbers
-- for macros that support matching pair of curly braces {}
local all_macros = {
verb = 1,
Verb = 1,
lstinline = -1,
url = -1
}
-- List all poss. delimiters
local all_delimiters = [[!"#$%&'*+,-./:;<=>?^_`|~()[]{}0123456789]]
-- Quick check if "s" contains an inline-verbatim-like macro:
function quick_check ( s )
if s:find("\\[vV]erb") or s:find("\\url") or s:find("\\lstinline") then
return true
else
return false
end
end
-- Function to process the part of string "s" that
-- does *not* contain inline-verbatim-like macros
local function insert_thinspaces ( s )
s = unicode.utf8.gsub ( s ,
"(%l%.)(%a%l?%l?%.)(%a%l?%l?%.)(%a%l?%l?%.)",
"%1\\,%2\\,%3\\,%4" ) -- e.g. "u.v.a.m.", "w.z.b.w."
s = unicode.utf8.gsub ( s ,
"(%l%.)(%a%l?%l?%.)(%a%l?%l?%.)",
"%1\\,%2\\,%3" ) -- e.g., "a.d.Gr."
s = unicode.utf8.gsub ( s ,
"(%u%l%l?%.)(%a%l?%l?%.)(%a%l?%l?%.)",
"%1\\,%2\\,%3" ) -- e.g., "Anm.d.Red."
s = unicode.utf8.gsub ( s ,
"(%l%.)(%a%l?%l?%.)",
"%1\\,%2" ) -- e.g., "z.T.", "z.Zt.", "v.Chr."
return s
end
-- Finally, the main Lua function:
function expand_abbr ( s )
-- Check if we're entering or exiting a verbatim-like env.;
-- if so, reset the 'in_verbatim_env' "flag" and break.
for i,p in ipairs ( verbatim_env ) do
if s:find( "\\begin{" .. p .. "}" ) then
in_verbatim_env = true
break
elseif s:find( "\\end{" .. p .. "}" ) then
in_verbatim_env = false
break
end
end
-- Potentially modify "s" only if *not* in a verbatim-like env.:
if not in_verbatim_env then
-- Quick check if "s" contains one or more inlike-verbatim-like macros:
if quick_check ( s ) then
-- See https://stackoverflow.com/a/45688711/1014365 for the source
-- of the following code. Many many thanks, @EgorSkriptunoff!!
s = s:gsub("\\([%a@]+)",
function(macro_name)
if all_macros[macro_name] then
return
"\1\\"..macro_name
..(all_macros[macro_name] < 0 and "\2" or "\3")
:rep(math.abs(all_macros[macro_name]) + 1)
end
end
)
repeat
local old_length = #s
repeat
local old_length = #s
s = s:gsub("\2(\2+)(%b{})", "%2%1")
until old_length == #s
s = s:gsub("[\2\3]([\2\3]+)((["..all_delimiters:gsub("%p", "%%%0").."])(.-)%3)", "%2%1")
until old_length == #s
s = ("\2"..s.."\1"):gsub("[\2\3]+([^\2\3]-)\1", insert_thinspaces):gsub("[\1\2\3]", "")
else
-- Since no inline-verbatim-like macro found in "s", invoke
-- the Lua function 'insert_thinspaces' directly.
s = insert_thinspaces ( s )
end
end
return(s)
end
\end{luacode*}
%% LaTeX-side code: Macros to assign 'expand_abbr'
%% to LuaTeX's 'process_input_buffer' callback.
\newcommand\ExpandAbbrOn{\directlua{%
luatexbase.add_to_callback("process_input_buffer",
expand_abbr, "expand_abbreviations")}}
\newcommand\ExpandAbbrOff{\directlua{%
luatexbase.remove_from_callback("process_input_buffer",
"expand_abbreviations")}}
\AtBeginDocument{\ExpandAbbrOn} % enabled by default
%% Just for this example:
\setlength\parindent{0pt}
\obeylines
\begin{document}
Dies ist u.U. ein Test.
\begin{Verbatim}
Dies ist u.U. ein Test.
\end{Verbatim}
z.B. u.a. u.Ä. u.ä. u.U. a.a.O. d.h. i.e. v.a.
i.e.S. z.T. m.E. i.d.F. z.Z. u.v.m. z.Zt.
u.v.a.m. b.z.b.w. v.Chr. a.d.Gr. Anm.d.Red.
\begin{verbatim}
z.B. u.a. u.Ä. u.ä. u.U. a.a.O. d.h. i.e. v.a.
i.e.S. z.T. m.E. i.d.F. z.Z. u.v.m. z.Zt.
u.v.a.m. b.z.b.w. v.Chr. a.d.Gr. Anm.d.Red.
\end{verbatim}
U.S.A. U.K. % should *not* be processed
\lstinline|u.a. u.Ä. u.ä.|; \Verb$u.a. u.Ä. u.ä.$
% nested verbatim-like macros
\Verb+u.U.\lstinline|u.U.|u.U.+ \lstinline+u.U.\[email protected][email protected].+
% 2 URL strings
u.U. \url{u.U.aaaa.z.T.bbb_u.v.a.m.com} u.U.
u.U. \url?u.U.aaaa.z.T.bbb_u.v.a.m.com? u.U.
\end{document}
答案2
这是该问题的一个简单解决方案。它使用 Lua 回调process_input_buffer
扫描每个输入行以查找给定的缩写之一并在其中插入一个小空格。
对于该操作,您只需指定要用空格版本(表值)替换的缩写(表键)。当然,该机制可用于简单地将任何内容(表键)替换为其他内容(表值)。
此解决方案还允许您使用逐字输入,但您必须让脚本知道逐字环境。如果脚本不检查逐字环境,则读者可以看到这些更改。
您应该注意到该函数有效,但可能不是最快的,因为它总是检查每一行是否是脚本已知的每个逐字环境的逐字开始/结束。
更新:我通过自动生成所需的空格简化了用户输入(字典)。讨论中提到的最成问题的部分是句子开头和内联逐字的缩写。第一个可以用这个版本处理(检查一个点、一个空格,然后检查大写字母的缩写),但第二个可能很难检测和更改。
\documentclass{article}
\usepackage[ngerman]{babel}
\usepackage{luacode}
\begin{luacode}
local tabbr = {"z.B.","u.a.","u.Ä.","u.ä.","u.U.","a.a.O.","d.h.","i.e.",
"i.e.S.","v.a.","z.T.","m.E.","i.d.F.","z.Z.","u.v.m.","u.v.a.m.",
"z.Hd."}
local verbenv = {"[vV]erbatim","lstlisting"}
local tsub = {}
local inverb = false
function createsubstitutes()
for i,p in ipairs(tabbr) do
tsub[p] = unicode.utf8.gsub(p:sub(1,p:len()-1), "%.", ".\\,") .. "."
end
end
function expandabbr(s)
for i,p in ipairs(verbenv) do
if s:find("\\begin{" .. p .. "}") then
inverb = true
break
end
if s:find("\\end{" .. p .. "}") then
inverb = false
break
end
end
if not inverb then
for k,v in pairs(tsub) do
s = unicode.utf8.gsub(s, k:gsub("(%.)","%%%1"), v)
end
end
return(s)
end
\end{luacode}
\AtBeginDocument{%
\luaexec{createsubstitutes()}
\luaexec{luatexbase.add_to_callback("process_input_buffer", expandabbr, "expand_abbreviations")}%
}
\begin{document}
Dies ist z.B. ein Test.\\
In dieser Zeile gibt es z.B. \verb+z.B.+
\begin{verbatim}
Test z.B.
\end{verbatim}
\end{document}
答案3
只是对@Mico 的答案做了一点小调整。我将替换放在循环中以匹配长度未知的缩写。缺点是缩写的每个部分都必须符合定义的模式之一。
例如,o.B.d.A.
将由第二个gsub
模式进行评估"(%l.)(%a)"
并替换为o.\,B.d.\,A.
。在下一个循环中,模式将不匹配,因为块B.d.
不匹配。我猜这两个模式的组合应该可以匹配几乎所有的缩写,但不会产生太多的误报。
我做的另一个调整是只检查与第一个开头匹配的结束逐字名称。这样,几个嵌套逐字环境的构造就可以正确评估。但是,内联逐字仍然缺失,还有其他异常,例如\url
。
编辑:我还编写了一个函数,可以正确解析内联逐字命令,但前提是每行只有一种内联逐字命令并且它以同一行结尾。
\documentclass{article}
\usepackage[ngerman]{babel}
\usepackage{fancyvrb} % for "Verbatim" environment
\usepackage{luacode}
\usepackage{url}
%% Lua-side code:
\begin{luacode}
local verbatim_env = { "verbatim" , "Verbatim" , "lstlisting" }
local verbatim_inl = { "verb" , "lstinline" }
-- by default, *not* in a verbatim-like env.:
local cur_verbatim_env = nil
local cur_verbatim_inl = nil
function replace_abbrs ( s )
local rep_rep1 = 1
local rep_rep2 = 1
while rep_rep1 ~= 0 or rep_rep2 ~= 0 do
s,rep_rep1 = unicode.utf8.gsub ( s , "(%l%.)(%a%.)(%a)", "%1\\,%2\\,%3" )
s,rep_rep2 = unicode.utf8.gsub ( s , "(%l%.)(%a)", "%1\\,%2" )
end
return(s)
end
function expand_inline_verb ( s , p )
local r = ""
while string.len(s) > 0 do
local spos,epos = s:find( p.."%A" )
if spos ~= nil then
r = r .. replace_abbrs(s:sub(0,spos-1))
r = r .. s:sub(spos,epos)
local delim = s:sub(epos,epos)
s = s:sub(epos+1 , string.len(s))
local verb_end = s:find( delim )
r = r .. s:sub(0,verb_end)
s = s:sub(verb_end+1 , string.len(s))
else
r = r .. replace_abbrs(s)
break
end
end
return(r)
end
function expandabbr ( s )
if cur_verbatim_env == nil then
for i,p in ipairs ( verbatim_env ) do
if s:find( "\\begin{" .. p .. "}" ) then
cur_verbatim_env = verbatim_env[i]
break
end
end
elseif s:find( "\\end{" .. cur_verbatim_env .. "}" ) then
cur_verbatim_env = nil
end
if cur_verbatim_env == nil and cur_verbatim_inl == nil then
for i,p in ipairs ( verbatim_inl ) do
pos = s:find( "\\" .. p )
if pos ~= nil then
cur_verbatim_inl = s[pos+string.len(p)+1]
break
end
end
elseif cur_veratim_inl ~= nil then
if s:find( cur_veratim_inl ) then
cur_verbatim_inl = nil
end
end
if cur_verbatim_env == nil then
for i,p in ipairs ( verbatim_inl ) do
if s:find( p ) then
return(expand_inline_verb( s , p ))
end
end
s = replace_abbrs ( s )
end
return(s)
end
\end{luacode}
%% LaTeX-side code:
\newcommand\ExpandAbbrOn{\directlua{%
luatexbase.add_to_callback("process_input_buffer",
expandabbr, "expand_abbreviations")}}
\newcommand\ExpandAbbrOff{\directlua{%
luatexbase.remove_from_callback("process_input_buffer",
"expand_abbreviations")}}
\AtBeginDocument{\ExpandAbbrOn} % enabled by default
%% Just for this example:
\setlength\parindent{0pt}
\obeylines
\begin{document}
Dies ist u.U. ein Test.
\begin{Verbatim}
Dies ist u.U. ein Test.
\end{Verbatim}
\begin{Verbatim}
\begin{verbatim}
\end{verbatim}
Dies ist u.U. ein schwierigerer Test.
\end{Verbatim}
z.B. u.a. u.Ä. u.ä. u.U. a.a.O. d.h. i.e.
i.e.S. v.a. z.T. m.E. i.d.F. z.Z. u.v.m.
z.Zt. o.B.d.A. a.d.Gr. n.Chr. Anm.d.Red.
\verb|z.Zt. o.B.d.A. a.d.Gr. n.Chr. Anm.d.Red.|
\begin{verbatim}
z.B. u.a. u.Ä. u.ä. u.U. a.a.O. d.h. i.e.
i.e.S. v.a. z.T. m.E. i.d.F. z.Z. u.v.m.
\end{verbatim}
U.S.A. U.K.
\ExpandAbbrOff % turn off abbreviation expansion
A tricky URL: \url{u.U.aaaa.z.T.bbb}
\end{document}