【问题标题】:Function to determine whether or not a downloaded file is identical to an existing one确定下载文件是否与现有文件相同的功能
【发布时间】:2014-04-15 22:44:20
【问题描述】:

我正在开发一个 linux 程序,它应该解析从另一台计算机或互联网下载的文件,并从该文件中收集信息。程序还必须按例行程序重新下载文件,每隔 n 天/小时/分钟/无论如何,并再次解析它以保持更新,以防文件发生更改。

但是,解析文件的过程可能需要大量资源。因此,我想要一个函数来检查文件自上次下载以来是否已更改。我想像这样的例子:

int get_checksum(char *filename) {
    // New prototype, if no such function already exists in standard C-libraries
    int result;           // Or char/float/whatever


    // ...


    return result;
}
int main(void) {

    char filename[] = { "foo.dat" };
    char file_url[] = { "http://example.com/foo.dat" }
    int old_checksum;     // Or char/float/whatever
    int new_checksum;     // Or char/float/whatever


    // ...


    // Now assume that old_checksum has a value from before:

    dl_file(filename, file_url);    // Some prototype for downloading the file
    if ((new_checksum = get_checksum(filename)) == -1) {
        // Badness
    }
    else {
        if (new_checksum != old_checksum) {
            old_checksum = new_checksum;
            // Parse the file
        }
        else {
            // Do nothing
        }
    }


    // ...


}

Q1:标准 C/C++ 库中是否有 get_checksum(来自上例)这样的函数?

Q2:如果不是:达到这个目的最好的方法是什么?

不需要:
- 一个非常先进的功能
- 加密或安全校验和
- 能够将新文件与比上一个文件更旧的文件进行比较,因为新下载的文件总是会覆盖旧文件

【问题讨论】:

  • 文件时间戳不能被信任吗?
  • @CareyGregory 下载新文件时时间戳不会改变吗?
  • 我在想你可以在下载之前从源计算机获取时间戳。如果你能做到这一点,你甚至可以跳过下载。
  • 为什么不只 fork 和 exec sha1summd5sum

标签: c++ c linux file checksum


【解决方案1】:

您可以使用stat() 函数。它可以让您访问文件参数,如上次访问时间、上次修改时间、文件大小等:

struct stat {
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};

但是您需要对要使用它的文件具有执行权限。

man page

【讨论】:

  • 但是每次文件被新下载覆盖时,“最后访问时间”和“最后修改时间”不会改变吗?
  • 是的,在这种情况下,您可以使用st_size 检查文件大小,以防文件被修改。或任何其他可能适合您需要的参数。
【解决方案2】:

在 C++11 中的 std::hash 之前,C++ 语言中没有内置任何内容,这非常简单,但可能适合您的需求。

最后我检查了 Boost(最常见的 C++ 库扩展)中什么都没有。推理在这里讨论,但可能过时:

http://www.gamedev.net/topic/528553-why-doesnt-boost-have-a-cryptographic-hash-library/

所以,你最好的选择是:

std::hash 与文件内容。

或者像下面这样的东西可以保存到一个简单的标题中并链接:

http://www.zedwood.com/article/cpp-md5-function

或者您可以获取诸如OpenSSLCrypto++ 之类的库。

【讨论】:

    【解决方案3】:

    你可以做一个异或散列,你只需对连续的无符号整数/长整数块进行异或,但这会产生冲突问题。例如,如果文件大部分是字符,那么大部分字节将在正常 ASCII/Unicode 字符的范围内,因此会有很多未使用的键空间。

    对于标准实现,您可以将文件读入字符串并使用 C++11 中的 std::hash。 http://en.cppreference.com/w/cpp/utility/hash

    以下是第一种方法的示例:

    unsigned int hash(vector<char> file){
        unsigned int result;
        int *arr = (int*)file.data();
    
        for(int i = 0;i < file.size() / sizeof(unsigned int);i++)
            result ^= arr[i];
    
        return result;
    }
    

    你只需要将文件读入向量中。

    【讨论】:

    • 不管怎样,如果您要编写 XOR 散列,那么您可以轻松地将其升级为 DJB 散列或类似的散列,这是一个单行更改。这基本上可以让你到达你想要std::hash&lt;string&gt; 的位置。
    • std::string 的一些 std::hash 实现非常弱 - 例如微软过去(并且很可能仍然)选择沿字符串均匀分布的 10 个字符来合并 - 所有其他字符都被忽略了。可能仍然足够,但这取决于文件已知的修改类型。
    猜你喜欢
    • 1970-01-01
    • 2020-02-29
    • 2011-11-23
    • 2011-06-03
    • 2022-11-10
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多