awk替换ascii表规则bash

awk替换ascii表规则bash,bash,awk,Bash,Awk,我想在文本文件中执行一组分层(非递归)替换。 我想在ascii文件“table.txt”中定义规则,该文件包含一行行空白的列表字符串对: aaa 3 aa 2 a 1 我试图用一个awk脚本“substitute.awk”来解决这个问题: 我明白了 而不是期望的“3”。排列“table.txt”中的行没有帮助。谁能解释这里的问题是什么,以及如何规避它?(这是我实际任务的简化版本。我有一个大文件,其中包含ascii编码的语音符号,我想将其转换为Latex代码。符号的ascii编码包含{$,&,%

我想在文本文件中执行一组分层(非递归)替换。 我想在ascii文件“table.txt”中定义规则,该文件包含一行行空白的列表字符串对:

aaa 3
aa 2
a 1
我试图用一个awk脚本“substitute.awk”来解决这个问题:

我明白了

而不是期望的“3”。排列“table.txt”中的行没有帮助。谁能解释这里的问题是什么,以及如何规避它?(这是我实际任务的简化版本。我有一个大文件,其中包含ascii编码的语音符号,我想将其转换为Latex代码。符号的ascii编码包含{$,&,%,[a-z],[0-9],…)

任何意见和建议

附言:

当然,在本应用程序中,对于substitution table.txt:

aa ab
a  1
原始字符串:“aa”应转换为“ab”而不是“1b”。这意味着通过应用规则生成的字符串必须保持不变


如何解释这一点?

默认情况下,(i in subs)的循环顺序未定义

在较新版本的
awk
中,您可以使用
PROCINFO[“sorted_In”]
控制排序顺序。有关详细信息,请参阅部分和(链接的)部分

或者,如果您不能或不想这样做,您可以将替换项存储在
subs
中的数字索引项中,并手动按顺序遍历数组

要做到这一点,您需要将模式和替换都存储在数组的值中,这将需要一些注意组合。您可以考虑使用“代码>子SEP < /COD>或任何其他不能在模式或替换中的字符,然后<代码>分裂< /代码>值以获得循环中的模式和替换。

也请注意“代码”> GETLION <代码>,并考虑不要手动使用,而是使用<代码> NR==1 {…} /代码>并将列表“代码>表.txt < /Cord>作为第一个文件参数,以<代码> AWK< /COD> < /P>


编辑:实际上,对于手动循环版本,您也可以保留两个数组,一个将输入文件行号映射到要匹配的模式,另一个将模式映射到替换。然后,在行号数组上循环将获得该模式,并且该模式可以在第二个数组中使用以获得替换(对于
gsub

#!/usr/bin/env perl

use strict;
use warnings;

my %subs;
BEGIN{
    open my $f, '<', 'table.txt' or die "table.txt:$!";
    while(<$f>) {
        my ($k,$v) = split;
        $subs{$k}=$v;
    }
}
while(<>) {
  while(my($k, $v) = each %subs) {
    s/\b$k\b/$v/g;
  }
  print;
}
!/usr/bin/env perl
严格使用;
使用警告;
我的%subs;
开始{

打开我的$f,“下面是从另一个StackExchange站点获取的答案,来自一个非常类似的问题:

稍微不同的是,它按照与目标字符串长度相反的顺序进行替换(即,首先是最长的目标),但对于文本字符串的目标,这是唯一合理的顺序,在这个问题中也是如此

如果已安装,则可以使用以下shell函数,该函数将替换文件处理为
lex
生成的扫描程序,然后使用tcc的编译和运行选项进行编译和运行

# Call this as: substitute replacements.txt < text_to_be_substituted.txt
# Requires GNU sed because I was too lazy to write a BRE
substitute () { 
  tcc -run <(
  {
    printf %s\\n "%option 8bit noyywrap nounput" "%%"
    sed -r 's/((\\\\)*)(\\?)$/\1\3\3/;
            s/((\\\\)*)\\?"/\1\\"/g;
            s/^((\\.|[^[:space:]])+)[[:space:]]*(.*)/"\1" {fputs("\3",yyout);}/' \
        "$1"
    printf %s\\n "%%" "int main(int argc, char** argv) { return yylex(); }"
  } | lex -t)
}
调用上述命令的Shell函数: 您可以使用以下命令调用上述命令:

substitute file
其中,
file
是替换文件的名称。(文件名必须以
.txt
结尾,但不必键入文件扩展名。)

输入文件的格式是由一个目标字符串和一个替换字符串组成的一系列行。这两个字符串用空格分隔。可以在字符串中使用任何有效的C转义序列;也可以转义空格字符以将其包含在目标中。如果要包含文字\,则需要将其加倍

如果您不希望使用C转义序列,并且希望反斜杠不是元字符,则可以使用更简单的程序替换
sed
程序:

sed -r 's/([\\"])/\\\1/g' "$<"; \

sed-r's/([\\“])/\\\1/g'”$将替换项存储在按整数索引的两个数组中(一个数组用于替换字符串,一个数组用于替换),并按顺序迭代数组:

BEGIN {i=0; while (getline < file) { subs[i]=$1; repl[i++]=$2} 
  n = i}
  { for(i=0;i<n;i++) { gsub(subs[i],repl[i]); } 
     print tolower($0); 
  }
BEGIN{i=0;while(getline
b) 需要字符串时不要使用regexp(是的,这意味着不能使用sed)

c) while循环需要不断地移动到您已经更改的部分之外,否则您可能会进入一个无限循环

你需要这样的东西:

$ cat substitute.awk
NR==FNR {
    if (NF==2) {
        strings[++numStrings] = $1
        old2new[$1] = $2
    }
    next
}
{
    for (stringNr=1; stringNr<=numStrings; stringNr++) {
        old = strings[stringNr]
        new = old2new[old]
        slength = length(old)
        tail = $0
        $0 = ""
        while ( sstart = index(tail,old) ) {
            $0 = $0 substr(tail,1,sstart-1) new
            tail = substr(tail,sstart+slength)
        }
        $0 = $0 tail
    }
    print
}

$ echo aaa | awk -f substitute.awk table.txt -
3

$ echo aaaa | awk -f substitute.awk table.txt -
31
$ cat substitute.awk
NR==FNR {
    if (NF==2) {
        strings[++numStrings] = $1
        old2new[$1] = $2
    }
    next
}
{
    delete news
    for (stringNr=1; stringNr<=numStrings; stringNr++) {
        old = strings[stringNr]
        new = old2new[old]
        slength = length(old)
        tail = $0
        $0 = ""
        charPos = 0
        while ( sstart = index(tail,old) ) {
            charPos += sstart
            news[charPos] = new
            $0 = $0 substr(tail,1,sstart-1) RS
            tail = substr(tail,sstart+slength)
        }
        $0 = $0 tail
    }
    numChars = split($0, olds, "")
    $0 = ""
    for (charPos=1; charPos <= numChars; charPos++) {
        $0 = $0 (charPos in news ? news[charPos] : olds[charPos])
    }
    print
}
您的新需求需要这样的解决方案:

$ cat substitute.awk
NR==FNR {
    if (NF==2) {
        strings[++numStrings] = $1
        old2new[$1] = $2
    }
    next
}
{
    for (stringNr=1; stringNr<=numStrings; stringNr++) {
        old = strings[stringNr]
        new = old2new[old]
        slength = length(old)
        tail = $0
        $0 = ""
        while ( sstart = index(tail,old) ) {
            $0 = $0 substr(tail,1,sstart-1) new
            tail = substr(tail,sstart+slength)
        }
        $0 = $0 tail
    }
    print
}

$ echo aaa | awk -f substitute.awk table.txt -
3

$ echo aaaa | awk -f substitute.awk table.txt -
31
$ cat substitute.awk
NR==FNR {
    if (NF==2) {
        strings[++numStrings] = $1
        old2new[$1] = $2
    }
    next
}
{
    delete news
    for (stringNr=1; stringNr<=numStrings; stringNr++) {
        old = strings[stringNr]
        new = old2new[old]
        slength = length(old)
        tail = $0
        $0 = ""
        charPos = 0
        while ( sstart = index(tail,old) ) {
            charPos += sstart
            news[charPos] = new
            $0 = $0 substr(tail,1,sstart-1) RS
            tail = substr(tail,sstart+slength)
        }
        $0 = $0 tail
    }
    numChars = split($0, olds, "")
    $0 = ""
    for (charPos=1; charPos <= numChars; charPos++) {
        $0 = $0 (charPos in news ? news[charPos] : olds[charPos])
    }
    print
}

看起来不错,但当我给它加上“aaaa”时,它会返回“aaaa”,而不是“31”。如果您不想在结尾处匹配单词边界,请尝试删除
\b
并使用
s/\b$k/$v/g
William Pursel,不幸的是,这两个脚本都没有像我预期的那样工作。我希望尽可能多地应用所有规则,但在按照“table.txt”中列出的顺序进行每次匹配之后。所以“aaaa”应该得到31。但是您的perl脚本与“aaaa”完全不匹配。它返回“aaaa”。与其将替换项存储在关联数组中,不如将它们放在按整数索引的两个数组中(一个数组用于替换字符串,一个数组用于替换)然后按顺序迭代数组。谢谢你的建议。不幸的是,我在实现上有点困难。你能给出一些使用PROCINFO的代码行吗?
PROCINFO
是一个数组,你只需设置
PROCINFO[“sorted_in”]
到您希望循环中的
用于排序的任何值。其中一个
“@val_str_asc”
“@val_str_desc”
可能会执行您想要的操作,但不会获得您的输入文件行排序。为此,您可能需要手动循环。现在它工作得很好!我已经大致了解了来自Eta的消息
BEGIN {i=0; while (getline < file) { subs[i]=$1; repl[i++]=$2} 
  n = i}
  { for(i=0;i<n;i++) { gsub(subs[i],repl[i]); } 
     print tolower($0); 
  }
$ cat substitute.awk
NR==FNR {
    if (NF==2) {
        strings[++numStrings] = $1
        old2new[$1] = $2
    }
    next
}
{
    for (stringNr=1; stringNr<=numStrings; stringNr++) {
        old = strings[stringNr]
        new = old2new[old]
        slength = length(old)
        tail = $0
        $0 = ""
        while ( sstart = index(tail,old) ) {
            $0 = $0 substr(tail,1,sstart-1) new
            tail = substr(tail,sstart+slength)
        }
        $0 = $0 tail
    }
    print
}

$ echo aaa | awk -f substitute.awk table.txt -
3

$ echo aaaa | awk -f substitute.awk table.txt -
31
$ cat table.txt
aaa 3
aa 2
a 1
. 7
\ 4
* 9

$ cat foo
a.a\aa*a

$ awk -f substitute.awk table.txt foo
1714291
$ cat substitute.awk
NR==FNR {
    if (NF==2) {
        strings[++numStrings] = $1
        old2new[$1] = $2
    }
    next
}
{
    delete news
    for (stringNr=1; stringNr<=numStrings; stringNr++) {
        old = strings[stringNr]
        new = old2new[old]
        slength = length(old)
        tail = $0
        $0 = ""
        charPos = 0
        while ( sstart = index(tail,old) ) {
            charPos += sstart
            news[charPos] = new
            $0 = $0 substr(tail,1,sstart-1) RS
            tail = substr(tail,sstart+slength)
        }
        $0 = $0 tail
    }
    numChars = split($0, olds, "")
    $0 = ""
    for (charPos=1; charPos <= numChars; charPos++) {
        $0 = $0 (charPos in news ? news[charPos] : olds[charPos])
    }
    print
}
$ cat table.txt
1 a
2 b

$ echo "121212" | awk -f substitute.awk table.txt -
ababab