8

Python を使用すると、次のことができます。

equals = filecmp.cmp(file_old, file_new)

go言語でそれを行う組み込み関数はありますか? 私はそれをグーグルで検索しましたが、成功しませんでした。

パッケージでいくつかのハッシュ関数を使用することもできますhash/crc32が、それは上記の Python コードよりも多くの作業です。

4

8 に答える 8

14

@captncraig の回答を完成させるために、2 つのファイルが同じかどうかを知りたい場合は、OS パッケージのSameFile(fi1, fi2 FileInfo)メソッドを使用できます。

SameFile は、fi1 と fi2 が同じファイルを記述しているかどうかを報告します。たとえば、Unix では、これは、下にある 2 つの構造体のデバイス フィールドと inode フィールドが同一であることを意味します。

それ以外の場合、ファイルの内容を確認したい場合は、メモリ内のファイル全体のロードを回避して、2 つのファイルを 1 行ずつチェックするソリューションを次に示します。

最初の試行: https://play.golang.org/p/NlQZRrW1dT


編集:ファイルのサイズが同じでない場合、バイトチャンクで読み取り、高速に失敗します。https://play.golang.org/p/YyYWuCRJXV

const chunkSize = 64000

func deepCompare(file1, file2 string) bool {
    // Check file size ...

    f1, err := os.Open(file1)
    if err != nil {
        log.Fatal(err)
    }
    defer f1.Close()

    f2, err := os.Open(file2)
    if err != nil {
        log.Fatal(err)
    }
    defer f2.Close()

    for {
        b1 := make([]byte, chunkSize)
        _, err1 := f1.Read(b1)

        b2 := make([]byte, chunkSize)
        _, err2 := f2.Read(b2)

        if err1 != nil || err2 != nil {
            if err1 == io.EOF && err2 == io.EOF {
                return true
            } else if err1 == io.EOF || err2 == io.EOF {
                return false
            } else {
                log.Fatal(err1, err2)
            }
        }

        if !bytes.Equal(b1, b2) {
            return false
        }
    }
}
于 2015-05-04T19:39:14.870 に答える
11

その機能があなたが思っていることをするかどうかはわかりません。ドキュメントから、

浅いが与えられ、false でない限り、同一の os.stat() 署名を持つファイルは等しいと見なされます。

あなたの呼び出しは の署名のみを比較していos.statます。これには以下のみが含まれます:

  1. ファイルモード
  2. 修正時刻
  3. サイズ

Go では、これら 3 つのことすべてをos.Stat関数から学ぶことができます。これは、それらが文字通り同じファイルであるか、同じファイルへのシンボリックリンクであるか、またはそのファイルのコピーであることを示すだけです。

さらに詳しく知りたい場合は、両方のファイルを開いて比較できます (python バージョンは一度に 8k を読み取ります)。

crc または md5 を使用して両方のファイルをハッシュすることもできますが、長いファイルの先頭に違いがある場合は、早期に停止する必要があります。各リーダーから一度に数バイトを読み取り、 と比較することをお勧めしbytes.Compareます。

于 2015-04-08T05:00:58.953 に答える
1

equalfileのようなパッケージを使用できます

メイン API:

func CompareFile(path1, path2 string) (bool, error)

ゴドック: https://godoc.org/github.com/udhos/equalfile

例:

package main

import (
    "fmt"
    "os"
    "github.com/udhos/equalfile"
 )

func main() {
    if len(os.Args) != 3 {
        fmt.Printf("usage: equal file1 file2\n")
        os.Exit(2)
    }

    file1 := os.Args[1]
    file2 := os.Args[2]

    equal, err := equalfile.CompareFile(file1, file2)
    if err != nil {
        fmt.Printf("equal: error: %v\n", err)
        os.Exit(3)
    }

    if equal {
        fmt.Println("equal: files match")
        os.Exit(0)
    }

    fmt.Println("equal: files differ")
    os.Exit(1)
}
于 2016-11-18T17:31:26.373 に答える
0

これがio.Reader私が打ち出したものです。_, err := io.Copy(ioutil.Discard, newCompareReader(a, b))2 つのストリームが同じコンテンツを共有していない場合、エラーが発生する可能性があります。この実装は、不要なデータのコピーを制限することでパフォーマンスを最適化します。

package main

import (
    "bytes"
    "errors"
    "fmt"
    "io"
)

type compareReader struct {
    a    io.Reader
    b    io.Reader
    bBuf []byte // need buffer for comparing B's data with one that was read from A
}

func newCompareReader(a, b io.Reader) io.Reader {
    return &compareReader{
        a: a,
        b: b,
    }
}

func (c *compareReader) Read(p []byte) (int, error) {
    if c.bBuf == nil {
        // assuming p's len() stays the same, so we can optimize for both of their buffer
        // sizes to be equal
        c.bBuf = make([]byte, len(p))
    }

    // read only as much data as we can fit in both p and bBuf
    readA, errA := c.a.Read(p[0:min(len(p), len(c.bBuf))])
    if readA > 0 {
        // bBuf is guaranteed to have at least readA space
        if _, errB := io.ReadFull(c.b, c.bBuf[0:readA]); errB != nil { // docs: "EOF only if no bytes were read"
            if errB == io.ErrUnexpectedEOF {
                return readA, errors.New("compareReader: A had more data than B")
            } else {
                return readA, fmt.Errorf("compareReader: read error from B: %w", errB)
            }
        }

        if !bytes.Equal(p[0:readA], c.bBuf[0:readA]) {
            return readA, errors.New("compareReader: bytes not equal")
        }
    }
    if errA == io.EOF {
        // in happy case expecting EOF from B as well. might be extraneous call b/c we might've
        // got it already from the for loop above, but it's easier to check here
        readB, errB := c.b.Read(c.bBuf)
        if readB > 0 {
            return readA, errors.New("compareReader: B had more data than A")
        }

        if errB != io.EOF {
            return readA, fmt.Errorf("compareReader: got EOF from A but not from B: %w", errB)
        }
    }

    return readA, errA
}
于 2020-10-23T10:22:13.643 に答える
0

標準的な方法は、それらを統計して os.SameFile を使用することです。

-- https://groups.google.com/g/golang-nuts/c/G-5D6agvz2Q/m/2jV_6j6LBgAJ

os.SameFilePython とほぼ同じことを行う必要がありますfilecmp.cmp(f1, f2)(つまりshallow=true、stat によって取得されたファイル情報のみを比較することを意味します)。

func SameFile(fi1, fi2 FileInfo) bool

SameFile は、fi1 と fi2 が同じファイルを記述しているかどうかを報告します。たとえば、Unix では、これは、下にある 2 つの構造体のデバイス フィールドと inode フィールドが同一であることを意味します。他のシステムでは、決定はパス名に基づく場合があります。SameFile は、このパッケージの Stat によって返される結果にのみ適用されます。それ以外の場合は false を返します。

ただし、実際にファイルの内容を比較したい場合は、自分で行う必要があります。

于 2020-11-17T04:28:10.780 に答える