【问题标题】:Generate deterministic unique fixed length filename string from multiple input strings从多个输入字符串生成确定性唯一的固定长度文件名字符串
【发布时间】:2018-10-07 21:37:45
【问题描述】:

我有多个字符串,我想用它们来生成单个、固定长度、确定性的字符串。我正在尝试确保数据库中的唯一性,并且还将使用字符串作为文件名;所以我需要尽可能避免冲突,并且需要避免特殊字符。我还需要它是确定性的,以便相同顺序的相同三个字符串产生相同的输出字符串。

我想在已知的分隔符和 base64 编码上连接字符串。但是,这不是固定长度。

我想连接字符串,从该字符串中获取哈希值,然后对其进行 base64 编码。然而,默认情况下 base64 有特殊字符,windoze 会抱怨,这似乎是不好的做法。

现在我在做这个,感觉也很丑:

protected UUID parseUUID() {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        List<String> strings = new ArrayList<>();
        strings.add(stringOne);
        strings.add(stringTwo);
        strings.add(stringThree);

        strings.removeIf(str -> str == null || str.isEmpty());
        for(int i = 0; i < strings.size(); i++) {
            String string = strings.get(i);
            string = string.replace("|", "\\|");
            strings.set(i, string);
        }
        String input = String.join("|", strings);
        byte[] hash = digest.digest(input.getBytes());

        return UUID.nameUUIDFromBytes(hash);
    } catch(NoSuchAlgorithmException e) {
        return null;
    }
}

与此方法发生冲突的几率是多少?从多个输入字符串生成适合于文件名的确定性固定长度字符串的最佳方法是什么?肯定不是这个。

【问题讨论】:

  • base64 中唯一的特殊字符是+/,您可以轻松地将它们更改为'_''-'。这解决了特殊字符问题。但是,它并没有解决唯一性问题。你仍然有同样的机会,你最终得到任何哈希码。这取决于哈希码的长度。
  • 不清楚您是否希望三个字符串使用相同的哈希码,而不管它们呈现的顺序如何。也就是说,字符串(“A”,“B”,“C”)会生成与(“B”,“A”,“C”)相同的代码吗?
  • 我希望 ("A", "B", "C") 始终生成相同的字符串。 ("B", "A", "C") 应该生成一个新的。

标签: java string hash filenames deterministic


【解决方案1】:

我真的不明白,是什么阻碍你像你已经使用的那样使用散列函数?它们旨在完成您想要实现的目标(前提是我让您正确)。您可以简单地连接您的字符串,应用哈希函数并存储哈希。

碰撞当然是可能的,但在尝试将无限空间映射到有限空间时总是会出现这种情况。

【讨论】:

  • 我的方法没有任何“错误”,但我确实认为它不够优雅。我更好奇将 sha-256 哈希输入 UUID 会如何影响结尾字符串的冲突机会。我也想看看其他一些开发者是如何优雅地解决这个问题的。
【解决方案2】:

我现在想出的解决办法是:

protected String parseHash() {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-512");
        List<String> strings = new ArrayList<>();
        strings.add("one");
        strings.add("two");
        strings.add("three");

        strings.removeIf(str -> str == null || str.isEmpty());
        for(int i = 0; i < strings.size(); i++) {
            String string = strings.get(i);
            string = string.replace("|", "\\|");
            strings.set(i, string);
        }
        String input = String.join("|", strings);
        byte[] hash = digest.digest(input.getBytes());
        return DatatypeConverter.printHexBinary(hash);
    } catch(NoSuchAlgorithmException e) {
        return null;
    }
}

正如我所读到的,UUID.nameUUIDFromBytes(hash); 将计算给定哈希的 md5,这会降低哈希的分辨率。使用哈希的原始十六进制似乎是我能想到的最优雅的方式,但我当然愿意接受其他答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-27
    • 2019-10-30
    • 1970-01-01
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    相关资源
    最近更新 更多