【问题标题】:Transliteration script for linux shelllinux shell的音译脚本
【发布时间】:2014-10-09 21:36:45
【问题描述】:

我有多个包含字母表文本的 .txt 文件;我想将文本音译成其他字母;字母 1 的某些字符与字母 2 的字符是 1:1(即 a 变为 e),而另一些字符是 1:2(即 x 变为ch)。

我想使用 Linux shell 的简单脚本来完成此操作。

使用 trsed 我可以转换 1:1 字符:

sed -f y/abcdefghijklmnopqrstuvwxyz/nopqrstuvwxyzabcdefghijklm/

a 将变为 nb 将变为 o 等等(我认为是凯撒密码)

但是我该如何处理 1:2 的字符呢?

【问题讨论】:

    标签: linux shell sed tr


    【解决方案1】:

    使用 awk:

    #!/usr/bin/awk -f
    BEGIN {
        FS = OFS = ""
        table["a"] = "e"
        table["x"] = "ch"
        # and so on...
    }
    {
        for (i = 1; i <= NF; ++i) {
            if ($i in table) {
                $i = table[$i]
            }
        }
    }
    1
    

    用法:

    awk -f script.awk file
    

    测试:

    # echo "the quick brown fox jumps over the lazy dog" | awk -f script.awk
    the quick brown foch jumps over the lezy dog
    

    【讨论】:

    • 完美!非常感谢!
    • +1 但不是显式填充表格,而是这样做以节省一些冗余编码:split("a e x ch ...",t,/ /); for (i=1; i in t; i+=2) table[t[i]] = t[i+1].
    • @EdMorton :谢谢,但我做不到;但是,我实际上喜欢明确填充表格的想法(请参阅我对@TomFenech 的评论)
    • @mus_siluanus 如果您告诉我们您“无法使其工作”的方式,我们可以为您提供帮助。即使您现在不使用它,它也是使用初始值填充数组的常见 awk 习惯用法,因此您可能希望在某个时候这样做。如果您愿意,可以将 2 个阵列填充到另一个阵列中。我将添加一个答案,以便向您展示它的格式。
    【解决方案2】:

    这可以使用 Perl 单行非常简洁地完成:

    perl -pe '%h=(a=>"xy",c=>"z"); s/(.)/defined $h{$1} ? $h{$1} : $1/eg'
    

    或等效地(thanks jaypal):

    perl -pe '%h=(a=>"xy",c=>"z"); s|(.)|$h{$1}//=$1|eg'
    

    %h 是一个包含字符(键)和它们的替换(值)的散列。 s 是替换命令(如在 sed 中)。 g 修饰符意味着替换是全局的,e 意味着替换部分被评估为表达式。它一个一个地捕获每个字符,并用哈希中的值(如果存在)替换它们,否则保留原始值。 -p 开关表示自动打印输入中的每一行。

    测试一下:

    $ perl -pe '%h=(a=>"xy",c=>"z"); s|(.)|$h{$1}//=$1|eg' <<<"abc"
    xybz
    

    【讨论】:

    • 非常感谢!我喜欢使用单线的想法。但我更喜欢@konsolebox 的脚本,因为对于一长串替换(如音译),他的方法可以让我更清楚地了解我将要做什么......有点像一个漂亮的嵌入式字符映射......
    • @glenn 感谢您的编辑-我认为a="&gt;xy" 中间的双引号是错字?它似乎在第一个实例中起作用,我猜这只是使用单线的一个症状。
    • 对这两点都适用。使用use strict,会看到Bareword "z" not allowed while "strict subs" in use
    • @TomFenech 可以简化为perl -pe'%h=(a=&gt;"xy",b=&gt;"z");s|(.)|$h{$1}//=$1|eg' &lt;&lt;&lt;"abc"//= 是在 5.8 之后引入的,所以应该可以工作,除非使用古老的 perl
    【解决方案3】:

    不是答案,只是为了展示一种更简洁、惯用的方式来根据相关 cmets 中讨论的 @konsolebox 的答案填充 table[] 数组:

    BEGIN {
        split("a  e b", old)
        split("x ch o", new)
        for (i in old)
            table[old[i]] = new[i]
        FS = OFS = ""
    }
    

    所以旧字符到新字符的映射清楚地显示在第一个 split() 中的 char 映射到它下面的 char(s) 并且对于您想要的任何其他映射,您只需要更改 string(s ) 在 split() 中,不改变 26 位显式分配给 table[]。

    您甚至可以创建一个通用脚本来进行映射,并将新旧字符串作为变量传递:

    BEGIN {
        split(o, old)
        split(n, new)
        for (i in old)
            table[old[i]] = new[i]
        FS = OFS = ""
    }
    

    然后在 shell 中是这样的:

    old="a  e b"
    new="x ch o"
    awk -v o="$old" -v b="$new" -f script.awk file
    

    并且您可以保护自己免受错误填充字符串的影响,例如:

    BEGIN {
        numOld = split(o, old)
        numNew = split(n, new)
    
        if (numOld != numNew) {
            printf "ERROR: #old vals (%d) != #new vals (%d)\n", numOld, numNew | "cat>&1"
            exit 1
        }
    
        for (i=1; i <= numOld; i++) {
            if (old[i] in table) {
                printf "ERROR: \"%s\" duplicated at position %d in old string\n", old[i], i | "cat>&2"
                exit 1
            }
            if (newvals[new[i]]++) {
                printf "WARNING: \"%s\" duplicated at position %d in new string\n", new[i], i | "cat>&2"
            }
            table[old[i]] = new[i]
        }
    }
    

    如果你写 b 映射到 x,然后错误地写 b 映射到 y,那不是很好吗?以上确实是最好的方法,但当然是你的电话。

    这是一个完整的解决方案,如下面的 cmets 中讨论的那样

    BEGIN {
        numOld = split("a  e b", old)
        numNew = split("x ch o", new)
    
        if (numOld != numNew) {
            printf "ERROR: #old vals (%d) != #new vals (%d)\n", numOld, numNew | "cat>&1"
            exit 1
        }
    
        for (i=1; i <= numOld; i++) {
            if (old[i] in table) {
                printf "ERROR: \"%s\" duplicated at position %d in old string\n", old[i], i | "cat>&2"
                exit 1
            }
            if (newvals[new[i]]++) {
                printf "WARNING: \"%s\" duplicated at position %d in new string\n", new[i], i | "cat>&2"
            }
            map[old[i]] = new[i]
        }
    
        FS = OFS = ""
    }
    {
        for (i = 1; i <= NF; ++i) {
            if ($i in map) {
                $i = map[$i]
            }
        }
        print
    }
    

    我将 table 数组重命名为 map 只是因为 iMHO 更能代表数组的用途。

    将上述内容保存在文件script.awk 中并以awk -f script.awk inputfile 运行它

    【讨论】:

    • 我再次尝试了您的代码,但没有输出;也许我错过了什么?我做了什么:将代码复制到一个名为 script.awk 的新文件中;按照建议运行脚本。我既没有错误也没有输出。
    • 我只是展示了如何以不同的方式填充映射表,您仍然需要发布的脚本@konsolebox 的其余部分来实际执行该映射。等一下,我会用完整的解决方案更新它。
    • 现在它输出相同的输入文本。我将您的新代码复制到一个新文件中,然后在我的 shell 中:echo "ae" | awk -f 脚本.awk。输出为:ae
    • 我在整理完整的解决方案时忘记添加FS和OFS的设置,现在更新。
    • 现在可以了!非常感谢;我喜欢它搜索错误的能力
    【解决方案4】:

    使用 sed

    编写一个文件transliterate.sed,其中包含:

    s/a/e/g
    s/x/ch/g
    

    然后从你的命令行运行,从input.txt获取音译的output.txt

    sed -f transliterate.sed input.txt > output.txt
    

    如果您更经常需要此功能,请考虑将#!/bin/sed -f 添加为第一行,并使用chmod 744 transliterate.sed 使您的文件可执行,如at the Wikipedia page for sed 所述。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 2012-05-12
      • 2011-05-07
      • 2014-03-26
      • 1970-01-01
      相关资源
      最近更新 更多