假设我想<word>
使用以下代码来屏蔽脏话格罗利克斯,例如“你到底在干什么?!”我该如何定义一个命令\censor
,使其\censor{<word>}
产生一串这些符号?我不太确定在给定单词时应该使用哪些符号或多少个符号<word>
。我认为使用的符号数量应该大约等于中的字符数<word>
。
感谢您的输入!
答案1
从列表中取出一个随机符号并将其附加到标记列表;当累积符号的宽度大于单词的宽度减 2pt 时,打印符号,否则附加另一个符号。
\documentclass{article}
\usepackage{xparse,pgf}
\ExplSyntaxOn
\NewDocumentCommand{\censor}{m}
{
\pointer_censor:n { #1 }
}
\seq_new:N \g_pointer_grawlixes_seq
\tl_map_inline:nn { @ * \# ! \$ \% ? ! \# @ \% *}
{
\seq_gput_right:Nn \g_pointer_grawlixes_seq { #1 }
}
\int_const:Nn \c_pointer_grawlix_list_int { \seq_count:N \g_pointer_grawlixes_seq }
\dim_new:N \l_pointer_censor_dim
\dim_new:N \l_pointer_try_dim
\box_new:N \l_pointer_censor_box
\tl_new:N \l_pointer_grawlixes_tl
\cs_new_protected:Npn \pointer_censor:n #1
{
\tl_clear:N \l_pointer_grawlixes_tl
\hbox_set:Nn \l_pointer_censor_box { #1 }
\dim_set:Nn \l_pointer_censor_dim { \box_wd:N \l_pointer_censor_box }
\pointer_add_grawlix:
}
\cs_new_protected:Npn \pointer_add_grawlix:
{
\hbox_set:Nn \l_pointer_censor_box { \l_pointer_grawlixes_tl }
\dim_compare:nTF
{ \l_pointer_censor_dim - 2pt < \box_wd:N \l_pointer_censor_box }
{
\tl_use:N \l_pointer_grawlixes_tl
}
{
\pgfmathparse{random(1,\int_eval:n {\c_pointer_grawlix_list_int})}
\tl_put_right:Nx \l_pointer_grawlixes_tl
{ \seq_item:Nn \g_pointer_grawlixes_seq { \pgfmathresult } }
\pointer_add_grawlix:
}
}
\ExplSyntaxOff
\begin{document}
Censored
\censor{Censored}
\end{document}
答案2
一次谦虚的尝试lualatex
:
替换代码非常简单,我们需要更强大的代码。
\documentclass{article}
\directlua{
% my list of bad words
bad_words = { "fish", "cat", "dog", "horse", "alligator" }
% the replacement string
replacement = "duck"
% a replacement function which returns
% both the altered line and the number
% of occurrences
function replace(line)
for _, element in pairs(bad_words) do
if string.find(line, element) then
return string.gsub(line, element, replacement)
end
end
return line, 0
end
% my "naive" censor function, it simply
% replaces any occurrences of the
% list of bad words by the replacement
% string
function censor(line)
occurrences = 0
repeat
line, occurrences = replace(line)
until occurrences == 0
return line
end
% add the hook
callback.register('process_input_buffer', censor)}
\begin{document}
Once upon a time, there was a little cat who lived inside an igloo. Don't ask me what he was doing there.
One day, the cat was visited by his two other friends, the dog and the alligator!
--- ``What are you guys doing here?'', said the cat.
--- ``We came to visit you, mr.\ cat!'', said the dog.
--- ``Our friend horse will be late, he went to the store to buy some frozen fish for you``, replied the alligator.
\end{document}
输出:
故事的道德启示:我很不擅长讲故事。:)
现在,让我们添加 grawlixes。由于我需要更好的 Lua 代码,因此让我们创建一个外部文件censor.lua
并从我们的.tex
代码中调用它:
\begin{filecontents*}{censor.lua}
-- a list of symbols to represent the
-- grawlixe symbols
-- note that we need to escape
-- some chars
grawlixe_symbols = { "\\$", "\\#", "@", "!", "*", "\\&" }
-- generate a grawlixe of length s
-- note that the seed is not so random, so
-- same values of s might get the same
-- grawlixe pattern (I could add another seed
-- mid code, but I'm lazy)
function grawlixe(s)
math.randomseed(os.time())
local u = table.getn(grawlixe_symbols)
local i = math.random(u)
local r = grawlixe_symbols[i]
local current
local w = 1
repeat
current = math.random(u)
while current == i do
current = math.random(u)
end
i = current
r = r .. grawlixe_symbols[i]
w = w + 1
until w == s
return r
end
-- a list of bad words to be censored
bad_words = { "fish", "cat", "dog", "horse", "alligator" }
-- our replacement function, it returns
-- the new line and the number of
-- replacements made
-- note that this is a very naive replacement
-- function, there's a lot of room for
-- improvement
function replace(line)
for _, element in pairs(bad_words) do
if string.find(line, element) then
return string.gsub(line, element, grawlixe(string.len(element)))
end
end
return line, 0
end
-- the censor function, it repeats
-- ad nauseam until the line has
-- nothing more to be censored
function censor(line)
local occurrences = 0
repeat
line, occurrences = replace(line)
until occurrences == 0
return line
end
-- register the callback
callback.register('process_input_buffer', censor)
\end{filecontents*}
\documentclass{article}
\directlua{dofile('censor.lua')}
\begin{document}
Once upon a time, there was a little cat who lived inside an igloo. Don't ask me what he was doing there.
One day, the cat was visited by his two other friends, the dog and the alligator!
--- ``What are you guys doing here?'', said the cat.
--- ``We came to visit you, mr.\ cat!'', said the dog.
--- ``Our friend horse will be late, he went to the store to buy some frozen fish for you``, replied the alligator.
\end{document}
输出:
新故事的寓意:在文本中添加 grawlixes 会使其看起来很淘气。:)
答案3
抛开如何以及为什么这样做的利弊,我发现这是一个很好的小练习xstring
。以下是我的看法:
\documentclass{article}
\usepackage{xstring}
\def\grawlix{{\makeatletter@\makeatother}\textdollar{$\sharp$}*?!}
\newcommand{\censor}[1]{\StrLen{#1}[\result]%
\expandarg\StrLeft{\grawlix}{\result}[]}
\begin{document}
What the \censor{word} is this???
\end{document}
如您所见,限制在于 grawlix 将由同一组字符以相同顺序组成。(您可以重新定义字符集,或者找到在每次调用宏时随机化它们的方法……但那是另一回事!)但;)
您的 grawlix 将与被审查的单词一样长。
实际情况是,它xstring
有自己的扩展参数的方法,并且宏不能嵌套。因此,查找被审查单词长度的宏的结果将在另一个宏(此处想象地称为)中返回,该宏将(重新)用于在正确的位置\result
拆分预定义。\grawlix
宏\expandarg
确保扩展正确完成。根据xstring
文档,它将允许扩展传递的所有参数确切地因此,必须小心保护代表单身的带括号的字符(例如,\sharp
符号存在于数学模式中,但我们不希望分隔它的美元符号被视为单独的标记)。
答案4
交换符号的一个稳定方法是更改字体编码或重新编码字体。一个简单的例子
\documentclass{article}
\begin{document}
\newcommand\censor[1]{\fontencoding{OMS}\selectfont #1}
\censor{Censored}
\end{document}
使用 lualatex,将来(当界面更加稳定时)可能可以动态创建虚拟重新编码字体,目前应该已经可以使用功能文件来实现。