如何将双值文本数据转换为二进制(位表示)

如何将双值文本数据转换为二进制(位表示)

我有一个文本文件,two (2)仅包含可能的字符(可能还有新行\n)。例子:

ABBBAAAABBBBBABBABBBABBB

(尺寸24 bytes

如何将其转换为二进制文件,即一种位表示形式,并将两个可能值中的每一个分配给0or 1

生成的二进制文件 ( 0=A, 1=B):

011100001111101101110111     # 24 bits - not 24 ASCII characters

十六进制结果文件:

70FB77                       # 3 bytes - not 6 ASCII characters

我最感兴趣的是命令行解决方案(也许dd,,,,,,,)。另外,关于逆:如何取回原来的?xxdodtrprintfbc

答案1

另一个 perl:

perl -pe 'BEGIN { binmode \*STDOUT } chomp; tr/AB/\0\1/; $_ = pack "B*", $_'

证明:

$ echo ABBBAAAABBBBBABBABBBABBB | \
    perl -pe 'BEGIN { binmode \*STDOUT } chomp; tr/AB/\0\1/; $_ = pack "B*", $_' | \
    od -tx1
0000000 70 fb 77
0000003

上面一次读取一行输入。由您来确保这些线条完全符合它们的预期。

编辑:逆运算:

#!/usr/bin/env perl

binmode \*STDIN;

while ( defined ( $_ = getc ) ) {
    $_ = unpack "B*";
    tr/01/AB/;
    print;
    print "\n" if ( not ++$cnt % 3 );
}
print "\n" if ( $cnt % 3 );

这一次读取一个输入字节。

编辑2:更简单的反向操作:

perl -pe 'BEGIN { $/ = \3; $\ = "\n"; binmode \*STDIN } $_ = unpack "B*"; tr/01/AB/'

上面一次读取 3 个字节STDIN(但EOF在序列中间接收并不是致命问题)。

答案2

{   printf '2i[q]sq[?z0=qPl?x]s?l?x'
    tr -dc AB | tr AB 01 | fold -b24
}   <infile   | dc

在做出以下声明时,@lcd047 很好地解决了我之前的困惑状态:

您似乎对 的输出感到困惑od。用于od -tx1查看字节。od -x读取单词,并在交换字节的小端机器上。我没有密切关注上面的交换,但我认为您的初始版本是正确的,您根本不需要弄乱字节顺序。只用od -tx1,不用od -x

现在这让我感觉很多更好了——之前的需求dd conv=swab困扰了我一整天。我无法固定它,但我知道它出了问题。能够用自己的愚蠢来解释它是非常令人欣慰的——尤其是因为我学到了一些东西。

无论如何,这将删除每个不是 的字节[AB],然后tr将它们[01]相应地解释为 ,然后fold以每行 24 字节的方式生成结果流。dc ?一次读取一行,检查输入是否包含任何内容,如果包含,则P将该数字的字节值打印到标准输出。

man dc

  • P

    • 弹出堆栈顶部的值。如果它是一个字符串,则简单地打印它,不带尾随换行符。否则它是一个数字,其绝对值的整数部分被打印为“根据 (UCHAR_MAX+1)”字节流。
  • i

    • 将值从堆栈顶部弹出并使用它来设置输入基数。

一些 shell 自动化

下面是我根据上述内容编写的一个 shell 函数,它可以双向运行:

ABdc()( HOME=/dev/null  A='[fc[fc]]sp[100000000o]p2o[fc]' B=2i
        case    $1      in
        (-B) {  echo "$B"; tr AB 01      | paste -dP - ~      ; }| dc;;
        (-A) {  echo "$A"; od -vAn -tu1  | paste -dlpx - ~ ~ ~; }| dc|
         dc  |  paste - - - ~            | expand -t10,20,30     |
                cut -c2-9,12-19,22-29    | tr ' 01' AAB         ;;
        (*)     set '' "$1";: ${1:?Invalid opt: "'$2'"}         ;;
        esac
)

这将翻译ABABABA用 填充字节-B,所以你可以这样做:

ABdc -B <infile

但它会将任意输入转换为 24ABABABA每字节位编码的字符串 - 与问题中所呈现的形式相同 - w/ -B

seq 5 | ABdc -A | tee /dev/fd/2 | ABdc -B

AABBAAABAAAABABAAABBAABA
AAAABABAAABBAABBAAAABABA
AABBABAAAAAABABAAABBABAB
AAAABABAAAAAAAAAAAAAAAAA
1
2
3
4
5

对于-A输出,我加入了cutexpandod这里,我将在一分钟内介绍它们,但我还添加了另一个dc.我放弃了另一种方法的逐行?读取dc脚本,该方法一次使用一个数组- 这是一个将ull命令堆栈f打印到标准输出的命令。当然,因为是面向堆栈的fdcdc后进先出应用程序类型,这意味着full-stack 以其进入的相反顺序出现。

这可能是一个问题,但我dc无论如何都使用另一个,并将o输出基数设置为100000000尽可能简单地处理所有 0 填充。当它读到其他人的后进先出流,它再次应用这种逻辑,一切都水落石出。两者dc协同工作如下:

{   echo '[fc[fc]]sp[100000000o]p2o[fc]'
    echo some data | 
    od -An -tu1        ###arbitrary input to unsigned decimal ints
    echo lpx           ###load macro stored in p and execute
} | tee /dev/fd/2  |   ###just using tee to show stream stages
dc| tee /dev/fd/2  |dc 

...第一个流tee...

[fc[fc]]sp[100000000o]pc2o[fc]            ###dc's init cmd from 1st echo
 115 111 109 101  32 100  97 116  97  10  ###od's output
lpx                                       ###load p; execute

...每秒tee,从dcdc...

100000000o                             ###first set output radix
1010                                   ###bin/rev vs of od's out
1100001                                ###dc #2 reads it in, revs and pads it 
1110100                                
1100001
1100100
100000
1100101
1101101
1101111                                ###this whole process is repeated
1110011                                ###once per od output line, so
fc                                     ###each worked array is 16 bytes.

...第二个写入的输出dc是...

 01110011
 01101111
 01101101
 01100101
 00100000
 01100100
 01100001
 01110100
 01100001
 00001010

从那里该函数paste将其放在 <tabs> 上...

 01110011    01101111    01101101
 01100101    00100000    01100100
 01100001    01110100    01100001
 00001010

... expands <tabs> 到以 10 列间隔的空格...

 01110011  01101111  01101101
 01100101  00100000  01100100
 01100001  01110100  01100001
 00001010

...cut除了字节之外的所有内容都已消失2-9,12-19,22-29...

011100110110111101101101
011001010010000001100100
011000010111010001100001
00001010

...并将tr<spaces> 和零解释为A和那些B...

ABBBAABBABBABBBBABBABBAB
ABBAABABAABAAAAAABBAABAA
ABBAAAABABBBABAAABBAAAAB
AAAABABAAAAAAAAAAAAAAAAA

您可以在最后一行看到我添加的主要动机expand- 它是如此轻量级的过滤器,并且它很容易确保写入的每个序列 - 即使是最后一个 - 都被填充到 24 个编码位。当过程相反时,字符串被解码为-Byte 值,有两个附加的 NUL:

ABdc -B <<\IN | od -tc
ABBBAABBABBABBBBABBABBAB
ABBAABABAABAAAAAABBAABAA
ABBAAAABABBBABAAABBAAAAB
AAAABABAAAAAAAAAAAAAAAAA
IN

...如你看到的...

0000000   s   o   m   e       d   a   t   a  \n  \0  \0
0000014

真实世界数据

我使用了它,并尝试了一些简单、真实的流。我为分阶段报告构建了这个复杂的管道......

{                            ###dunno why, but I often use man man
    (                        ###as a test input source
        {   man man       |  ###streamed to tee
            tee /dev/fd/3 |  ###branched to stdout
            wc -c >&2        ###and to count source bytes
        }   3>&1          |  ###the branch to stdout is here
        ABdc -A           |  ###converted to ABABABA
        tee /dev/fd/3     |  ###branched again
        ABdc -B              ###converted back to bytes
        times >&2            ###the process is timed
    ) | wc -c >&2            ###ABdc -B's output is counted
} 3>&1| wc -c                ###and so is the output of ABdc -A

不过,我在这里没有任何良好的性能比较基础。我只能说我是被逼着去参加这个测试的(也许是天真)令人印象深刻,以至于这样做...

man man | ABdc -A | ABdc -B

man...它以与未过滤的命令相同的可辨别速度绘制了我的终端屏幕 w/的输出。测试的输出是...

37595                       ###source byte count
0m0.000000s 0m0.000000s     ###shell processor time nil
0m0.720000s 0m0.250000s     ###shell children's total user, system time
37596                       ###ABdc -B output byte count
313300                      ###ABdc -A output byte count

初步测试

剩下的只是一个更简单的概念证明,它确实有效......

printf %s ABBBAAAABBBBBABBABBBABBB|
tee - - - - - - - -|
tee - - - - - - - - - - - - - - - |
{   printf '2i[q]sq[?z0=qPl?x]s?l?x'
    tr -dc AB | tr AB 01 | fold -b24
} | dc        | od -tx1
0000000 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000020 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000040 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000060 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000100 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000120 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000140 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000160 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000200 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000220 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000240 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000260 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000300 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000320 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000340 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000360 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000400 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000420 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000440 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000460 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000500 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000520 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000540 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000560 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000600 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70
0000620 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB
0000640 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77 70 FB 77
0000660

答案3

珀尔:

my $len = 24;
my $str = "ABBBAAAABBBBBABBABBBABBB\n";
$str =~ s/\s//g;
(my $bin = $str) =~ y/AB/01/;
my $val = oct("0b".$bin);
printf "%s -> %s -> %X\n", $str, $bin, $val;

my ($filename, $fh) = ("temp.out");

# write the file
open $fh, '>', $filename;
print $fh pack("N", $val);      # this actually writes 4 bytes
close $fh;

# now read it, and convert back to a string:
open $fh, '<', $filename;
read $fh, my $data, 4;
close $fh;

my $new_val = unpack "N", $data;
my $new_bin = substr unpack("B32", $data), -$len;
(my $new_str = $new_bin) =~ y/01/AB/;

printf "%X -> %s -> %s\n", $new_val, $new_bin, $new_str;
ABBBAAAABBBBBABBABBBABBB -> 011100001111101101110111 -> 70FB77
70FB77 -> 011100001111101101110111 -> ABBBAAAABBBBBABBABBBABBB

感谢lcd047的完美回答,我的答案变成了:

my $str = "ABBBAAAABBBBBABBABBBABBB\n";
$str =~ s/\s//g;
(my $bin = $str) =~ y/AB/01/;
printf "%s -> %s\n", $str, $bin;

my ($filename, $fh) = ("temp.out");

# write the file
open $fh, '>', $filename;
print $fh pack("B*", $bin);
close $fh;

my $size = -s $filename;
print $size, "\n";

# now read it, and convert back to a string:
open $fh, '<', $filename;
read $fh, my $data, 1024;
close $fh;

my $new_bin = unpack("B*", $data);
(my $new_str = $new_bin) =~ y/01/AB/;

printf "%s -> %s\n", $new_bin, $new_str;
ABBBAAAABBBBBABBABBBABBB -> 011100001111101101110111
3
011100001111101101110111 -> ABBBAAAABBBBBABBABBBABBB

答案4

替换文本文件中的字符:

sed -i 's/A/0/g' file.in
sed -i 's/B/1/g' file.in

如果您用 \n 表示换行符,则将其替换为换行符:

sed 's/\\n/\'$'\n''/g' file.in

(ABBBAAAABBBBBABBABBBABBB 变为 011100001111101101110111)

将 file.in 中的 ascii(字符串)视为二进制数据以(按原样)写入二进制文件:

data=$(cat file.in)

# replace file
echo $(printf '%x\n' "$((2#$data))") | xxd -r -p > file.out
# or write to existing file without truncating
echo $(printf '%x\n' "$((2#$data))") | xxd -r -p - file.out

读取(3 字节)二进制文件时给出以下十六进制代码:

hd file.out
70fb77

要解码(反向),请使用 hd 或 xxd 读取二进制文件,将十六进制代码转换为二进制,然后将 0 和 1 交换为 A 和 B。

在 Ubuntu 16.04.7 上测试

相关内容