我有一个文本文件,two (2)
仅包含可能的字符(可能还有新行\n
)。例子:
ABBBAAAABBBBBABBABBBABBB
(尺寸24 bytes
)
如何将其转换为二进制文件,即一种位表示形式,并将两个可能值中的每一个分配给0
or 1
?
生成的二进制文件 ( 0=A
, 1=B
):
011100001111101101110111 # 24 bits - not 24 ASCII characters
十六进制结果文件:
70FB77 # 3 bytes - not 6 ASCII characters
我最感兴趣的是命令行解决方案(也许dd
,,,,,,,)。另外,关于逆:如何取回原来的?xxd
od
tr
printf
bc
答案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
输出,我加入了cut
、expand
和od
这里,我将在一分钟内介绍它们,但我还添加了另一个dc
.我放弃了另一种方法的逐行?
读取dc
脚本,该方法一次使用一个数组- 这是一个将ull命令堆栈f
打印到标准输出的命令。当然,因为是面向堆栈的f
dc
dc
后进先出应用程序类型,这意味着f
ull-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
,从dc
到dc
...
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
... expand
s <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 个编码位。当过程相反时,字符串被解码为-B
yte 值,有两个附加的 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 上测试