Java中BASE64类的编码/解码算法效率如何?

5

我将使用一个算法对从XML文件检索到的变长但非常长的字符串字段进行编码,然后将编码数据持久化到数据库中。

稍后,当我收到第二个文件时,我需要从数据库中获取编码数据(之前存储的),然后解码它并验证新数据以查找重复项。

我尝试了org.apache.commons.codec.binary.Base64类,它有两种方法:

  1. encodeBase64(Byte[] barray)
  2. decodeBase64(String str)

这很完美地解决了我的问题。但它将55个字符的字符串转换为只有6个字符的字符串。

因此,我想知道是否存在这样一种情况,即这些算法将两个非常大且仅有1个字符不匹配(例如)的字符串编码为相同的编码字节数组。

我不太了解Base64类,但如果有人能帮助我,那将非常有帮助。

如果您可以建议任何其他算法,使大字符串缩短为固定长度并解决我的问题,我将很高兴使用它。

提前感谢。


任何正确实现的base64都会使字符串变大而不是变小。您是想压缩字符串吗? - Peter Lawrey
是的,实际上我需要一些算法将长字符串压缩成较小的一个,并存储在数据库中,然后再通过解压还原为原始字符串。我在网上看到了ZipOutputStream类的实现,但我还没有尝试过它。 - Subhadip Pal
我认为你正在寻找一种哈希函数,例如MD5(它将所有输入转换为128字节的输出)。Base64编码通常会导致输出大小为输入大小的四分之三 - 因此根本不会产生固定长度的输出。 - Andrzej Doyle
好的,我明白了。那么你建议用什么算法来解决我的问题?你能给我发送一个示例链接吗? - Subhadip Pal
你是否只是想确定输入字符串是否已经存储在数据库中?如果是这样,选择一个合适的哈希函数并存储哈希值可能就足够了。不同的哈希函数具有不同的属性(性能、碰撞概率等),你需要进行一些研究来选择一个适合你需求的哈希函数。 - johnstok
我刚刚注意到你说Base64类将55个字符的输入编码为6个字符的输出。如果该类确实在进行base64编码,那么它意味着它已经对4个字符的输入进行了编码(末尾没有==填充)。如果您发布了使用该类的简短示例,可能会有所帮助,因为我怀疑它可能没有做您认为它正在做的事情(您是否尝试过解码6个字符的输出?)。 - Andrzej Doyle
2个回答

13

效率不高。

此外,使用sun.misc类会导致应用程序无法移植。

请查看来自MiGBase64的以下性能比较:

enter image description here


所以我想知道是否存在这样一种情况:这些算法将2个非常大且仅有1个字符不匹配(例如)的字符串编码为相同的编码字节数组。

Base64不是一个哈希算法,而是一种编码方式,因此必须是双向的。由于必须进行解码,因此不能允许碰撞发生。Base64旨在用ASCII字符串表示任意二进制数据。将Unicode字符串编码为Base64通常会增加所需代码点的数量,因为Unicode字符集需要多个字节。 Unicode字符串的Base64表示取决于所使用的编码(UTF-8、UTF-16)等。例如:

Base64( UTF8( "test" ) ) => "dGVzdA=="
Base64( UTF16( "test" ) ) => "/v8AdABlAHMAdA=="

解决方案1

使用无损压缩。

GZip( UTF8( "test" ) )

在这里,您将字符串转换为字节数组,并使用无损压缩来减少要存储的字节数。您可以根据要存储的字符串的特性变化字符编码和压缩算法,以减少字节数(例如如果它主要是ASCII,则UTF-8可能是最佳选择)。

优点:没有冲突,能够恢复原始字符串
缺点:存储值所需的字节是可变的;存储值所需的字节更大。

解决方案2

使用散列算法。

SHA256( UTF8( "test" ) )

在这里,你正在使用哈希函数将字符串转换为一组固定长度的字节。哈希是单向的,根据其性质可能会出现碰撞。但是,基于您预计要处理的字符串数量和配置文件,您可以选择一个哈希函数以最小化碰撞的可能性。

优点: 存储值所需的字节是固定的; 存储值所需的字节很少。
缺点: 可能会发生碰撞,无法恢复原始字符串。


@johnstok,你能告诉我一种有效压缩和解压字符串的方法吗? - Subhadip Pal
@johnstok 我对答案感到满意...虽然我使用了java.util.zip.Deflater和Inflater类来有效地压缩/解压字符串。现在又出现了另一个问题,压缩后生成的输出字符串包含一些字符,即使我尝试以UTF8格式压缩它,eclipse控制台也无法显示,我必须检查一下我的数据库是否支持压缩后的字符串输出。无论如何,非常感谢你的回答。 - Subhadip Pal
使用java.util.zip.Deflater进行压缩将生成一个字节数组,而不是字符串。您可以将其存储在SQL BLOB或BINARY列中。 - johnstok

1

我看到了你的评论——看起来你实际上是在寻找压缩而不是散列,这与我最初想的有所不同。虽然在这种情况下,你将不能为任意输入获取固定长度的输出(想想看,无限数量的输入无法双向映射为有限数量的输出),所以我希望这并不是一个强制性的要求。

无论如何,你选择的压缩算法的性能取决于输入文本的特性。如果没有更多信息,DEFLATE压缩(IIRC由Zip输入流使用)是一个很好的通用算法,至少可以作为比较的基础。不过,为了方便实现,你可以使用内置在JDK中的Deflator类,它使用ZLib压缩。

如果你的输入字符串具有特定的模式,则不同的压缩算法可能更或者更少有效。在某种程度上,无论你使用哪种算法,如果你不打算让其他进程读取压缩数据——只要你可以自己压缩和解压缩,那么对于客户端来说,它就是透明的。

这些其他问题可能会引起你的兴趣:


首先感谢您的建议,我理解了您所说的。在这里,Base64对我来说解决了问题,但我担心的是,我能否依赖Base64算法能够有效地编码和解码所有较大的字符串。是否会有任何情况,在这种情况下,Base64算法为仅相差一个字符或类似的大型字符串生成相同的输出? - Subhadip Pal
Base64能够满足你的需求吗?我认为你想要让字符串变小,但是使用base64会得到更大的输出。同时,它也不对输出大小进行任何限制——如果输入有3000个字符,则编码后的输出将有4000个字符——因此,这可能无法满足你对"高效地对所有较大的字符串编码"的要求。尽管如此,在回答你最后一个问题时,base64不会发生碰撞;它是完全双向的。 - Andrzej Doyle
是的,很抱歉我最初以为Base64类可以压缩输出。但后来发现我需要的是java.util.zip.Deflater和java.util.zip.Inflater类,但是又遇到了压缩字符串的输出不是Unicode格式的问题。我尝试将其重构为UTF8,但在我的eclipse控制台上显示的字符串文字并不是UTF8格式。我将看看这些数据是否可以持久化到我的Oracle数据库中。无论如何,感谢你的帮助 :) 干杯。 - Subhadip Pal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接