如何横向镜像ascii艺术?

时间:2012-10-28 20:22:33

标签: image-processing awk ascii-art

所以......我知道我可以使用tac或其他一些工具来反转文件中的行顺序,但是如何在其他维度(即水平?)重新排序?我尝试使用以下awk脚本执行此操作:

{
    out="";
    for(i=length($0);i>0;i--) {
        out=out substr($0,i,1)}
    print out;
}

这似乎可以扭转这些角色,但它会出现乱码,而我却没有看到原因。我错过了什么?

我在awk中这样做,但还有更好的东西吗?也许是sed

这是一个例子。输入数据如下所示:

$ cowsay <<<"hello"
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

输出如下:

$ cowsay <<<"hello" | rev
_______ 
> olleh <
------- 
^__^   \        
_______\)oo(  \         
\/\)       \)__(            
| w----||                
||     || 

请注意,无论是使用rev还是使用我自己的awk脚本,输出都是相同的。正如你所看到的,事情是相反的,但......它已经被破坏了。

6 个答案:

答案 0 :(得分:7)

rev很不错,但它不会填充输入行。它只是逆转了它们。

你看到的“破坏”是因为一行可能是20个字符长,而下一行可能是15个字符长。在输入文本中,它们共享一个左侧列。但是在输出文本中,他们需要共享一个右栏。

所以你需要填充。哦,和不对称的逆转,正如约阿希姆所说。

这是我的revawk

#!/usr/bin/awk -f

# 
length($0)>max {
    max=length($0);
}

{
    # Reverse the line...
    for(i=length($0);i>0;i--) {
        o[NR]=o[NR] substr($0,i,1);
    }
}

END {
    for(i=1;i<=NR;i++) {
        # prepend the output with sufficient padding
        fmt=sprintf("%%%ds%%s\n",max-length(o[i]));
        printf(fmt,"",o[i]);
    }
}

(我在gawk执行此操作;我认为我没有使用任何gawkisms,但如果您使用的是更经典的awk变体,则可能需要对此进行调整。)

使用此方法与使用rev。

相同
ghoti@pc:~$ echo hello | cowsay | ./revawk | tr '[[]()<>/\\]' '[][)(><\\/]'
                    _______ 
                   < olleh >
                    ------- 
            ^__^   /        
    _______/(oo)  /         
/\/(       /(__)            
   | w----||                
   ||     ||                

如果您愿意这样做,您甚至可以通过将其添加到上一个printf行来从awk脚本中运行翻译:

        printf(fmt," ",o[i]) | "tr '[[]()<>/\\]' '[][)(><\\/]'";

但我不推荐它,因为它使revawk命令对其他应用程序不那么有用。

答案 1 :(得分:5)

以下是使用GNU awkrev

的一种方法

运行如:

awk -f ./script.awk <(echo "hello" | cowsay){,} | rev

script.awk的内容:

FNR==NR {
    if (length > max) {
        max = length
    }
    next
}

{
    while (length < max) {
        $0=$0 OFS
    }
}1

或者,这是单行:

awk 'FNR==NR { if (length > max) max = length; next } { while (length < max) $0=$0 OFS }1' <(echo "hello" | cowsay){,} | rev

结果:

                    _______ 
                   > olleh <
                    ------- 
            ^__^   \        
    _______\)oo(  \         
\/\)       \)__(            
   | w----||                
   ||     ||                

----------------------------------------------- -----------------------------------------------

这是使用GNU awk的另一种方式:

运行如:

awk -f ./script.awk <(echo "hello" | cowsay){,}

script.awk的内容:

BEGIN {
    FS=""
}

FNR==NR { 
    if (length > max) {
        max = length
    }
    next
}

{
    while (length < max) {
        $0=$0 OFS
    }
    for (i=NF; i>=1; i--) {
        printf (i!=1) ? $i : $i ORS
    }
}

或者,这是单行:

awk 'BEGIN { FS="" } FNR==NR { if (length > max) max = length; next } { while (length < max) $0=$0 OFS; for (i=NF; i>=1; i--) printf (i!=1) ? $i : $i ORS }' <(echo "hello" | cowsay){,}

结果:

                    _______ 
                   > olleh <
                    ------- 
            ^__^   \        
    _______\)oo(  \         
\/\)       \)__(            
   | w----||                
   ||     ||                

----------------------------------------------- -----------------------------------------------

<强> 说明:

以下是对第二个答案的解释。我假设有awk的基本知识:

FS=""                 # set the file separator to read only a single character
                      # at a time.

FNR==NR { ... }       # this returns true for only the first file in the argument
                      # list. Here, if the length of the line is greater than the
                      # variable 'max', then set 'max' to the length of the line.
                      # 'next' simply means consume the next line of input

while ...             # So when we read the file for the second time, we loop
                      # through this file, adding OFS (output FS; which is simply
                      # a single space) to the end of each line until 'max' is
                      # reached. This pad's the file nicely.

for ...               # then loop through the characters on each line in reverse.
                      # The printf statement is short for ... if the character is
                      # not at the first one, print it; else, print it and ORS.
                      # ORS is the output record separator and is a newline.

您可能需要了解的其他一些事项:

{,}通配符后缀是重复输入文件名两次的简写。 不幸的是,它不是标准的Bourne shell。但是,您可以改为使用:

<(echo "hello" | cowsay) <(echo "hello" | cowsay)

此外,在第一个示例中,{ ... }1{ ... print $0 }

的缩写

HTH。

答案 2 :(得分:4)

你的线长度不一样,所以逆转牛会破坏它。你需要做的是将线“填充”到相同的长度,然后反转。

例如;

cowsay <<<"hello" | awk '{printf "%-40s\n", $0}' | rev

将其填充到40列,然后反转。

编辑:@ghoti做了一个肯定胜过这种简单反转的剧本,看看他的回答。

答案 3 :(得分:2)

您也可以使用bash,coreutils和sed(使其与zsh一起工作,while循环需要包含在tr ' ' '\x01' | while ... | tr '\x01' ' '中,不知道为什么):

say=hello
longest=$(cowsay "$say" | wc -L)

echo "$say" | rev | cowsay | sed 's/\\/\\\\/g' | rev |
  while read; do printf "%*s\n" $longest "$REPLY"; done |
  tr '[[]()<>/\\]' '[][)(><\\/]'

输出:

                    _______ 
                   < hello >
                    ------- 
            ^__^   /        
    _______/(oo)  /         
/\/(       /(__)            
   | w----||                
   ||     ||                

这会留下很多多余的空格,附加| sed 's/ *$//'来删除。

解释

需要引用cowsay输出,尤其是通过复制它们来处理的反斜杠。要使用正确的行宽printf '%*s' len str,使用len作为字符串长度参数。最后,非对称字符被其对应字符替换,就像在 ghoti的答案中所做的那样。

答案 4 :(得分:1)

我不知道你是否可以在AWK中做到这一点,但这里是必要的步骤:

确定原始最长线的长度,您需要为任何较小的线提供适当的间距。

    (__)\       )\/\

对于每一行的最后一个字符,根据您从第一步获得的内容,确定开始行空间的需要。

< hello >
//Needs ??? extra spaces, because it ends right after '>'.
//It does not have spaces after it, making it miss it's correct position after reverse.
        (__)\       )\/\
< hello >???????????????

对于每一行,应用行所需的空格数,然后按相反顺序应用原始字符。

                    _______ 
                   > olleh <
                    ------- 
            ^__^   \        
    _______\)oo(  \         
\/\)       \)__(            
   | w----||                
   ||     || 

最后,用水平相对的字符替换所有非水平对称的字符。 (<>[]等)

                    _______ 
                   < olleh >
                    ------- 
            ^__^   /        
    _______/(oo)  /         
/\/(       /(__)            
   | w----||                
   ||     || 

需要注意两件事:

  • 正如您所见,文字无法正确进行逆转。
  • $%&等字符不是水平对称的, 除非你使用专门的,否则也可能没有相反的情况 Unicode块。

答案 5 :(得分:0)

我会说你可能需要每一行都是固定的列宽,所以每一行都是相同的长度。因此,如果第一行是后跟LF的字符,则需要在反转之前用空格填充反向。