【问题标题】:How to clean a data file from binary junk?如何从二进制垃圾中清除数据文件?
【发布时间】:2011-09-12 13:35:24
【问题描述】:

我有这个数据文件,它应该是一个普通的 ASCII 文件。但是,它在第​​一行的末尾有一些垃圾。它只在我用 vi 或更少的时候显示-->

  y mon d  h XX11 XX22 XX33 XX44 XX55 XX66^@
2011  6 6 10 14.0 15.5 14.3 11.3 16.2 16.1

grep 也说是二进制文件:Binary file data.dat matches

这在我的解析脚本中造成了一些问题。我正在分割每一行并将它们放入数组中。由于垃圾,第一个数组中的最后一个元素(XX66)以某种方式损坏,我无法匹配它。

如何清理该行或数组?我已经尝试将dos2unix 替换为文件并用s/\s+$// 替换数组成员。那到底是什么垃圾?不幸的是,我无法控制这些数据,它是第三方数据。

有什么想法吗?

【问题讨论】:

    标签: linux perl file binary


    【解决方案1】:

    Grep 试图变得聪明,当它看到一个不可打印的字符时,会切换到“二进制”模式。添加“-a”或“--text”以强制grep保持“文本”模式。

    至于 sed,试试sed -e 's/\([^ -~]*\)//g',它说,“将不在 spacetilde(分别为字符 0x20 和 0x7E)之间的所有内容更改为空”。这也会去除制表符,但您可以在空格前插入制表符以包含它们(或任何其他特殊字符)。

    “^@”是表示 NUL(又名“ascii(0)”或“\0”)的一种方式。如果某些程序以幼稚的方式实现,它们也可能会将其视为文件结尾。

    【讨论】:

    • 更多信息... ^@(或“C-@”)表示“控制-@”。由于历史上应用“控制”键会从应用它的键的 ascii 值中减去 64,并且“@”是 ascii(64),因此您会得到 ascii(0)。有时您会看到 M-x,其中“M”表示“meta-x”,并且历史上将 128 添加到 ascii 值。如果你真的很幸运,你偶尔会看到“M-C-x”,意思是两者都适用。
    • Perl 中字符的一些表示形式:"\0""\x00""\c@"use charnames qw(:full); "\N{NULL}"
    • sed 解决方案删除了​​太多。它只留下值内的点。但是@daxim 提供的解决方案解决了这个问题。我用s/\x00// 代替这条线。 s/\0// 也可以。
    • 嗯...那是方括号内的“caret”、“space”、“dash”、“tilde”,对吧?
    【解决方案2】:

    如果总是相同的代码(例如 ^@ 或相关代码),那么您可以找到/替换它们。

    以 Vim 为例:

    :%s/^@//g 在编辑模式下会清除所有这些字符。

    要输入^@ 等字符,请按住Ctrl 键,按'v',然后按您需要的字符- 在上述情况下,请记住按住Shift 键来获取@ 键。应该按住 Ctrl 键直到结束。

    【讨论】:

      【解决方案3】:

      ^@ 看起来像是一个控制字符。我不知道它应该是什么角色,但我想这并不重要。

      您可以使用 s/^@//g 删除它们,但您必须实际复制字符,只是将 ^ 和 @ 放在一起不会这样做。

      e:f;b.

      【讨论】:

        【解决方案4】:

        我创建了这个小脚本来从文件中删除所有二进制、非 ASCII 字符和一些烦人的字符。请注意,字符是基于八进制的:

        #!/usr/bin/perl
        use strict;
        use warnings;
        
        my $filename = $ARGV[0];
        open my $fh, '<', $filename or die "File not found: $!";
        open my $fh2, '>', 'report.txt' ;
        binmode($fh);
        
        my ($xdr, $buffer) = "";
        
        # read 1 byte at a time until end of file ...
        while (read ($fh, $buffer, 1) != 0) {   
            # append the buffer value to xdr variable
            $xdr .= $buffer; 
            if (!($xdr =~ /[\0-\11]/) and (!($xdr =~ /[\13-\14]/))and (!($xdr =~ /[\16-\37]/)) and (!($xdr =~ /[\41-\55]/)) and (!($xdr =~ /[\176-\177]/))) {
                print $fh2 $xdr;
            }
            $xdr = "";
        } 
        # finaly, clean all the characters that are not ASCII.
        system("perl -plne 's/[^[:ascii:]]//g' report.txt > $filename.clean.txt");
        

        【讨论】:

          【解决方案5】:

          使用 sed 剥离单个字符会非常缓慢,对于 100MB 的文件可能需要几分钟。

          或者,如果您知道文件的格式/结构,例如一个日志文件,其中文件的“好”行以时间戳开头,然后您可以 grep 出好行并将其重定向到新文件。

          例如,如果我们知道所有好的行都以 2021 年的时间戳开头,我们可以使用此表达式仅将这些行输出到新文件:

          grep -a "^2021" mylog.log > mylog2.log
          

          请注意,您必须在 grep 中使用 -a--text 选项来强制 grep 在检测到文件是二进制文件时输出行。

          【讨论】:

            猜你喜欢
            • 2023-03-23
            • 1970-01-01
            • 2019-05-20
            • 2013-02-24
            • 1970-01-01
            • 2016-09-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多