如何将某些字符一对一音译,保留某些字符不变,并将其他字符替换为相同的目标字符?

如何将某些字符一对一音译,保留某些字符不变,并将其他字符替换为相同的目标字符?

我有一个名为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

相关内容