首頁 > 軟體

Golang基於檔案魔數判斷檔案型別的案例程式碼

2023-02-20 06:00:37

本文介紹基於魔數判斷檔案型別,涉及檔案查詢讀取內容、檔案魔數、位元組比較,最後還介紹函數引數的知識。

查詢位置

File.Seek()函數可以設定偏移位置,為下一次讀或寫確定偏移量,具體起點有whence確定:0標識相對檔案開始位置、1相對當前位置、2相對檔案結尾。函數返回新的位置及錯誤。請看下面範例:

package main
 
import (
   "os"
   "fmt"
   "log"
)
 
func main() {
   file, _ :- os.Open("test.txt")
   defer file.Close()
 
   // Offset 表示偏移量
   // Offset 可以為正數或負數
   var offset int64 - 5
 
   // Whence 偏移參考點,具體取值說明
   // 0 - Beginning of file
   // 1 - Current position
   // 2 - End of file
   var whence int - 0
   newPosition, err :- file.Seek(offset, whence)
   if err !- nil {
      log.Fatal(err)
   }
   fmt.Println("Just moved to 5:", newPosition)
 
   // 從當前位置回走2個位元組
   newPosition, err - file.Seek(-2, 1)
   if err !- nil {
      log.Fatal(err)
   }
   fmt.Println("Just moved back two:", newPosition)
 
   // 通過移動零位元組返回當前位置
   currentPosition, err :- file.Seek(0, 1)
   fmt.Println("Current position:", currentPosition)
 
   // 回到檔案起始點
   newPosition, err - file.Seek(0, 0)
   if err !- nil {
      log.Fatal(err)
   }
   fmt.Println("Position after seeking 0,0:", newPosition)
}

執行程式結果如下:

Just moved to 5: 5
Just moved back two: 3
Current position: 3
Position after seeking 0,0: 0

檔案型別

魔數是檔案前幾個位元組,用於唯一標識檔案型別,從而無需關注複雜檔案結構就能夠確定檔案型別。舉例,jpeg檔案總是ffd8 ffe0。下面列舉常見檔案型別的魔數:

  • 影象檔案
File typeTypical extensionHex digits xx - variableAscii digits . - not an ascii char
Bitmap format.bmp42 4dBM
FITS format.fits53 49 4d 50 4c 45SIMPLE
GIF format.gif47 49 46 38GIF8
Graphics Kernel System.gks47 4b 53 4dGKSM
IRIS rgb format.rgb01 da
ITC (CMU WM) format.itcf1 00 40 bb
JPEG File Interchange Format.jpgff d8 ff e0
NIFF (Navy TIFF).nif49 49 4e 31IIN1
PM format.pm56 49 45 57VIEW
PNG format.png89 50 4e 47.PNG
Postscript format.[e]ps25 21%!
Sun Rasterfile.ras59 a6 6a 95Y.j.
Targa format.tgaxx xx xx
TIFF format (Motorola - big endian).tif4d 4d 00 2aMM.*
TIFF format (Intel - little endian).tif49 49 2a 00II*.
X11 Bitmap format.xbmxx xx
XCF Gimp file structure.xcf67 69 6d 70 20 78 63 66 20 76gimp xcf
Xfig format.fig23 46 49 47#FIG
XPM format.xpm2f 2a 20 58 50 4d 20 2a 2f/* XPM */
  • 壓縮檔案型別
File typeTypical extensionHex digits xx = variableAscii digits . = not an ascii char
Bzip.bz42 5aBZ
Compress.Z1f 9d
gzip format.gz1f 8b
pkzip format.zip50 4b 03 04PK…
  • 歸檔檔案型別
File typeTypical extensionHex digits xx = variableAscii digits . = not an ascii char
TAR (pre-POSIX).tarxx xx(a filename)
TAR (POSIX).tar75 73 74 61 72ustar (offset by 257 bytes)
  • 可執行檔案型別
File typeTypical extensionHex digits xx = variableAscii digits . = not an ascii char
MS-DOS, OS/2 or MS Windows4d 5aMZ
Unix elf7f 45 4c 46.ELF

有了上面的基礎知識,我們就可以讀檔案前幾個位元組判斷檔案型別。

實現基礎函數

首先定義檔案魔數標識變數:

var(
   PDF        = []byte{0x25, 0x50, 0x44, 0x46}
	RAR        = []byte{0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00}
	GZIP       = []byte{0x1F, 0x8B, 0x08}
	ZIP_0      = []byte{0x50, 0x4B, 0x03, 0x04}
	ZIP_1      = []byte{0x50, 0x4B, 0x05, 0x06}
	ZIP_2      = []byte{0x50, 0x4B, 0x07, 0x08}
	WEBP       = []byte{0x52, 0x49, 0x46, 0x46}
   ...
)

下面定義幾個讀檔案函數。

首先是從ReadSeeker開始位置起讀取幾個位元組函數:

func readUntil(l int, r io.ReadSeeker) ([]byte, error) {
	buff := make([]byte, l)

	_, err := r.Read(buff)
	if err != nil {
		return nil, err
	}

	r.Seek(0, io.SeekStart)

	return buff, nil
}

基於魔數位節陣列讀檔案魔數:

func checkBuffer(r io.ReadSeeker, t []byte) ([]byte, error) {
   // 根據提供引數獲取長度
	l := len(t)

	buff, err := readUntil(l, r)
	if err != nil {
		return make([]byte, 0), err
	}

	return buff, nil
}

基於引數比較檔案魔數:

func genericCompareBuffer(r io.ReadSeeker, t []byte) bool {
	buff, err := checkBuffer(r, t)
	if err != nil {
		return false
	}

	valid := bytes.Compare(t, buff)
	return valid == 0
}

比較檔案包括多個魔數情況比較:

func genericMultipleCompareBuffer(r io.ReadSeeker, t [][]byte) bool {
	buff, err := checkBuffer(r, t[0])
	if err != nil {
		return false
	}

	for _, v := range t {
		if bytes.Compare(v, buff) == 0 {
			return true
		}
	}

	return false
}

型別判斷函數

有了上面的基礎函數,我們可以提供上層應用介面函數。

首先是常用型別判斷函數,注意這裡PNG、JPEG是前面定義的位元組陣列變數。

// IsPng function will return true if File is a valid PNG
func IsPng(r io.ReadSeeker) bool {
	return genericCompareBuffer(r, PNG)
}

// IsJpeg function will return true if File is a valid JPEG
func IsJpeg(r io.ReadSeeker) bool {
	return genericCompareBuffer(r, JPEG)
}

// IsPdf function will return true if File is a valid PDF
func IsPdf(r io.ReadSeeker) bool {
	return genericCompareBuffer(r, PDF)
}

// IsGif function will return true if File is a valid GIF
func IsGif(r io.ReadSeeker) bool {
	return genericCompareBuffer(r, GIF)
}

同類檔案可能有不同魔數場景:

// IsMpg function will return true if File is a valid MPG
func IsMpg(r io.ReadSeeker) bool {
	return genericMultipleCompareBuffer(r, [][]byte{
		MPG_0,
		MPG_1,
	})
}

最後提供一個同時判斷多種檔案型別的函數,利用函數型別引數:

// IsOneOf function will validate File with multiple function
func IsOneOf(r io.ReadSeeker, functions ...function) bool {
	for _, f := range functions {
		valid := f(r)
		if valid {
			return true
		}
	}

	return false
}

測試程式碼

下面測試前面定義的函數,函數包括檔名稱引數,判斷該檔案型別:

package main

import (
	"fmt"
	"os"
)

func main() {
	args := os.Args

	if len(args) < 2 {
		fmt.Println("required input file")
		os.Exit(1)
	}

   // 開啟檔案
	inputFileArg := args[1]
	inFile, err := os.Open(inputFileArg)

	if err != nil {
		fmt.Println("error open input file ", err)
		os.Exit(1)
	}

   // 支援錯誤處理的關閉方式
	defer func() { 
      err := inFile.Close() 
      if err != nil {
         fmt.Println("error close input file ", err)
      }
   }()

   // 一次性判斷多種型別,如:是否為影象檔案
	valid := IsOneOf(inFile, filesig.Is3gp, filesig.IsPng, filesig.IsJpeg)
	fmt.Println(valid)

   // 當然也可以判斷單個型別
   valid = filesig.Is3gp(inFile)
	fmt.Println(valid)
}

總結

本文介紹了基於檔案魔數判斷檔案型別的方法,主要涉及如何ReadSeek讀取檔案指定位元組內容,然後介紹檔案魔數,最後給出範例基於魔數判斷檔案型別。參考程式碼:https://github.com/telkomdev/go-filesig

到此這篇關於Golang基於檔案魔數判斷檔案型別的文章就介紹到這了,更多相關go檔案型別內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com