我有一个名为file.txt
包含:
MAL TIRRUEZF CR MAL RKZYIOL EX MAL OIY UAE RICF "MAL ACWALRM DYEUPLFWL CR ME DYEU MAIM UL IZL RKZZEKYFLF GH OHRMLZH"
我希望将字符替换如下:
M = T
A = H
L = E
C = O
R = F
E = I
X = S
(Any other letter) = _
(Anything else) = (itself)
我有固定字符覆盖:
tr MALCREX THEOFIS < file.txt
或者:
sed 'y/MALCREX/THEOFIS/' < file.txt
但我怎样才能执行我提到的最后两条规则呢?
答案1
我认为您可以利用这样一个事实:对于许多实际实现,如果字符在第一个集合中重复tr
,则最后一个实例生效。与重复语法相结合,您无需显式列出转换表中未出现的字母即可完成此操作。
对于 GNU 版本的 tr,以及我 Mac 上任何基于 FreeBSD 的版本,如下:
tr 'A-ZMALCREX' '[_*26]THEOFIS'
轮流
MAL TIRRUEZF CR MAL RKZYIOL EX MAL OIY UAE RICF "MAL ACWALRM DYEUPLFWL CR ME DYEU MAIM UL IZL RKZZEKYFLF GH OHRMLZH"
进入
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_ "THE HO_HEFT __I__E__E OF TI __I_ TH_T _E __E F___I___E_ __ __FTE__"
当然,假设A-Z
生成恰好 26 个字符,并且我不确定这是否适用于每个 tr 实现的每个语言环境。它应该在 C 语言环境中工作,例如 GNU 版本的 tr 无论如何除了原始 8 位字符之外不支持任何内容。
上面的代码在 Busybox 中不起作用,但这似乎是因为它不支持重复语法。在那里,您必须手动执行此操作:
busybox tr 'A-ZMALCREX' '__________________________THEOFIS'
(即 26 个下划线)
对于简单的基于表的实现来说,使用重复字符覆盖相同字符的早期实例是很自然的。如果您的tr
实施方式不同,您将需要使用其他答案中的解决方案。
答案2
比其他几个建议稍长,但可能更容易理解。
第一个建议:将不需要的字母映射到_
然后转置剩余的集合。
tr BDFGHIJKNOPQSTUVWYZ _ <file | tr MALCREX THEOFIS
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_
第二个建议:在一个命令中完成所有操作。 (GNU 和 BSD根据需要对替换源映射中的所有未映射字符tr
隐式重复替换目标的最后一个字符 ( _
),但此行为由POSIX简单地作为未指定.)
tr MALCREXBDFGHIJKNOPQSTUVWYZ THEOFIS_ <file
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_
答案3
我建议这个perl
替代方案:
$ perl -pe 's/(?![MALCREX])[A-Z]/_/g;y/MALCREX/THEOFIS/' file
THE __FF_I__ OF THE F_____E IS THE ___ _HI F_O_ "THE HO_HEFT __I__E__E OF TI __I_ TH_T _E __E F___I___E_ __ __FTE__"
它执行先行断言,查找范围内的所有字符A-Z
(除了 )MALCREX
,然后按照您的命令执行替换sed
。
作为史蒂芬·查泽拉斯 (Stéphane Chazelas) 评论,此解决方案的优点是可以用or[A-Z]
替换(并且可能添加以处理语言环境中的所有字符),因此它可以处理所有类型的字母。\w
\pL
-Mopen=locale
另一种方法是建议的比利叔叔的评论:
perl -pe y/MALCREXA-Z/THEOFIS_/
答案4
使用 Raku(以前称为 Perl_6)
raku -pe 'tr/MALCREX/THEOFIS/; s:g/ <+:Uppercase_Letter - [THEOFIS]> /_/;'
输入示例:
MAL TIRRUEZF CR MAL RKZYIOL EX MAL OIY UAE RICF "MAL ACWALRM DYEUPLFWL CR ME DYEU MAIM UL IZL RKZZEKYFLF GH OHRMLZH"
示例输出(在顶部运行 Raku 代码):
THE TIFF_I_F OF THE F___IOE IS THE OI_ _HI FIOF "THE HO_HEFT __I__EF_E OF TI __I_ THIT _E I_E F___I__FEF _H OHFTE_H"
Raku 解决这个问题的一个优势是默认支持 Unicode(但是,本答案中未提及)。
上面代码中的第二条语句使用了s///
带有定制字符类的全局替换<+:Uppercase_Letter - [THEOFIS]>
,可以更简单地表示为<+:Lu - [THEOFIS]>
。正如 @Stéphane Chazelas 在评论中指出的,可以用or:Lu
替换,这样它就可以处理所有类型的字母。:Letter
[\w]
对于所呈现的简单情况(仅"
双引号和空格非数字字符),上面代码中的第二个语句可以
tr///
与:complement
副词一起编写。语句tr:c/THEOFIS" /_/;
, 将"
双引号和空格字符添加到
THEOFIS
列表中。 (换句话说,'采取 :complement
前两个之间的所有字符 /…/
并将它们更改为后两个之间列出的字符 /…/
,在本例中是 _
下划线。)
raku -pe 'tr/MALCREX/THEOFIS/; tr:c/THEOFIS" /_/;'
示例输出:
THE TIFF_I_F OF THE F___IOE IS THE OI_ _HI FIOF "THE HO_HEFT __I__EF_E OF TI __I_ THIT _E I_E F___I__FEF _H OHFTE_H"
编辑:
正如我上面所做的那样,将音译分成两个步骤意味着我面临着用第二步覆盖“最终”字符的危险。为了避免这种情况,我可以在两个步骤中都转换为小写字符,这会产生与其他人看到的相同的输出(但是 - 见下文)。
最后,看起来OP在他们发布的音译表中犯了一个错误,因为更新的表/代码(如下)提供了更合理的输出:
raku -pe 'tr/MALCREX/theisof/; tr:c/theisof" /_/;'
更新的输出:
the __ss_o__ is the s_____e of the ___ _ho s_i_ "the hi_hest __o__e__e is to __o_ th_t _e __e s___o___e_ __ __ste__"
https://docs.raku.org/language/regexes#Predefined_character_classes
https://raku.org