golang学习记录:二进制文件判断实现与思路

Posted 河边小咸鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang学习记录:二进制文件判断实现与思路相关的知识,希望对你有一定的参考价值。

  • go version go1.15.14 linux/amd64
  • 相关项目地址为:点我跳转

一、前言

  最近在写一个golang实现的字符串搜索与替换程序练手,其中一个很大的问题就是程序不能识别二进制文件与文本文件,导致搜索出来的内容会乱码,非常的不雅观。如果再不小心替换一下的话,就会造成很大的影响,所以这个问题必须解决。

  我简单看了一下相关的标准库函数,发现golang标准库内并没有提供相关的函数来实现分辨二进制文件的功能。于是学习了一下编码相关的知识来自己实现一个二进制文件判断的功能。

二、思路

  文本文件其实本质也是二进制文件,只不过可以被计算机内置的编码方式来解读为可阅读的文本。所以本文中需要解决的问题实质为如何识别出无法被正常编码的文件。

  总所周知,ASCII为最初的编码方式,故基础字母数字和操作符都可以按照ASCII标准被编码。但是查看ASCII编码中的内容,可以发现其中一些内容是不可读的,比如编码为0-6的一些状态符。假如文件中出现了这类编码,那么肯定此文件不是正常的可读文本文件,即二进制文件。

  简单看一下ASCII表的内容,可以总结出正常的可视编码为32~127\\r(13)\\n(10)\\t(9),那么当文件中所有的字节都属于这个范围时,则此文件必定可以按ASCII编码正常显示,则为文本文件。

  但是这仅仅是英文,在文件中常常会出现中文等内容。对此,需要将常见的Unicode编码、GBK编码或者UTF8等编码考虑进去。这三种编码方式都是在ASCII编码的基础上实现的,其主要原理就是将ASCII编码中未使用的128~255范围进行使用,再使用多字节来组合出不同的内容。其均默认一个共识,即低字节(0-127)保持ASCII原本的内容,高字节(128-255)为新编码方式中定义的内容。 故将白名单编码范围扩展到32~255即可,主要的辨别对象还是0~31中的内容。

  故,主要的思路即为判定字节的内容,若存在非常规内容,即可进行判定。

三、实现

  我本来是直接遍历文件的前100个字节,若属于32~255\\r(13)\\n(10)\\t(9)则continue,否则则说明为二进制文件。但是这样在实际中存在一些问题。比如说100个字节可以不够,即可能文件的前100个字符都是正常的。另外判定方式也有一些参差,比如说\\a响铃符也是可能存在的(之前我就写过音乐软件练手)。

  初版写法如上,此种写法可以识别大多数的二进制文件,但是也有部分的漏网之鱼,所以还存在改进的空间。

  然后今天我抽空上网冲浪了一会,找到了一种更全面的算法(zlib的算法),本文中的最终版本识别二进制文件也是使用的这种算法,算法内容如下:

  1. 将一个字节的内容分为三类,白名单中包括32~255\\r(13)\\n(10)\\t(9);灰名单中包括\\a(7)\\b(8)\\v(11)\\f(12)sub(26)esc(27);黑名单中包括0~614~31
  2. 如果文件包含至少一个白名单中的字节,而且不包含黑名单中的字节,其即为文本文件;否则则为二进制文件

  算法主要就是上面那个,很简单,但是判定了空文件等若干情况。在此次的完善中,我规定的读取范围为前1024个字节,因为之前实现中100个字节有些少,但是全文读取又会影响效率,所以读取1024个字节。核心的判定算法更改为上文中的那个。代码如下:(github地址:点我跳转)

package detect

import (
    "os"
    "log"
    "bufio"
)

func DetectBinary(path string) bool {

    file, err := os.Open(path)
    if err != nil {
    ┊   log.Printf("\\033[31merror : IO error - \\033[0m%s", err)return false
    }
    defer file.Close()

    r := bufio.NewReader(file)
    buf := make([]byte, 1024)
    n, err := r.Read(buf)

    var white_byte int = 0
    for i := 0; i < n; i++ {if (buf[i] >= 0x20 && buf[i] <= 0xff) ||
    ┊   ┊   buf[i] == 9  ||
    ┊   ┊   buf[i] == 10 ||
    ┊   ┊   buf[i] == 13 {
    ┊   ┊   white_byte++} else if buf[i] <= 6 || (buf[i] >= 14 && buf[i] <= 31) {
    ┊   ┊   return true}
    }

    if white_byte >= 1 {return false
    }
    return true
}

  如上即为实现,返回值为true即为二进制文件。就我进行的测试来看,可以很好的判定二进制文件。

以上是关于golang学习记录:二进制文件判断实现与思路的主要内容,如果未能解决你的问题,请参考以下文章

golang判断文件是否存在

golang中如何判断文件是否有可执行权限

学习记录:gcc/g++ 编译与链接

golang zip 压缩,解压(含目录文件)

golang学习笔记2——容器和流程控制

golang比较浮点数是不是相等