zip字符串压缩

前言

最近项目上用到了压缩算法,找来找去找到了zip压缩算法,因为网上找的代码有点不太入法眼,所以找chatgpt写了个比较优雅的zip压缩解压算法,代码看起来还是比较漂亮的,殊不知其中隐藏了一个大bug!

起因

因为这个数据是准备压缩后,用扫码器到别的机器上识别的,一开始用的是加密算法,发现加密后字符串特别长,扫码器有点比较垃圾,所以只能在算法上想办法,思来想去,就找到了zip这个直接压缩字符串,一开始用的都比较顺畅,没什么问题!后来偶然的测试中发现,连续扫码解析的时候发现了问题,因为是在android系统上的,扫了几次应用直接退出了,但是日志里又么有发现异常的打印,就特别纳闷,第一次出现的时候,找日志没什么发现,就忽略了,但是后来又发现了,所以这个肯定是有什么问题!

一开始以为可能是扫码器连续读写的问题,但是想想,扫码器就是个键盘输入的东西,应该不会造成应用异常退出,但是后来想着不太对,估计还是哪里造成的,仔细看了下日志,发现最后一次读上来的码并不完整,难道是这个原因造成的,但是有try catch呀,应该不会造成的,抱着试试的态度,用java试了下,发现方法被阻塞了,一直没返回解密后的字符,有问题的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static String decompress(String compressedData) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Inflater inflater = new Inflater();
inflater.setInput(Base64.decode(compressedData));

byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}

inflater.end();
outputStream.close();
return outputStream.toString();
} catch (Exception e) {
LogUtils.e("解压缩异常:", e);
}
return null;
}

打断点后发现确实死循环了,inflater.finished()这个方法一直都是false,所以无法退出,但是count一直是0,解铃还须系铃人,找chatgpt问了一下,回答如下:

Inflater类的inflate方法在解压缩数据时,如果提供的输入不足以产生完整的解压缩数据块,它可能会导致死循环。

为了解决这个问题,你可以在while循环之前添加一个检查,确保至少提供了一些压缩数据。你可以使用inflater.needsInput()方法来检查是否还需要更多的输入数据。完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static String decompress(String compressedData) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Inflater inflater = new Inflater();

byte[] compressedBytes = Base64.getDecoder().decode(compressedData);
inflater.setInput(compressedBytes);

byte[] buffer = new byte[1024];
while (!inflater.finished() && !inflater.needsInput()) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}

inflater.end();
outputStream.close();
return outputStream.toString("UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

确实,你提到的是一个潜在的问题。Inflater类的inflate方法在解压缩数据时,如果提供的输入不足以产生完整的解压缩数据块,它可能会导致死循环。

为了解决这个问题,你可以在while循环之前添加一个检查,确保至少提供了一些压缩数据。你可以使用inflater.needsInput()方法来检查是否还需要更多的输入数据。

以下是修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
javaCopy codeimport java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.zip.Inflater;

public class YourClass {
public static String decompress(String compressedData) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Inflater inflater = new Inflater();

byte[] compressedBytes = Base64.getDecoder().decode(compressedData);
inflater.setInput(compressedBytes);

byte[] buffer = new byte[1024];
while (!inflater.finished() && !inflater.needsInput()) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}

inflater.end();
outputStream.close();
return outputStream.toString("UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

这里添加了 !inflater.needsInput() 的条件,确保只有在Inflater需要更多输入时才执行while循环。这样可以避免死循环问题。

结语

所以chatgpt确实对写代码有帮助,但是有时候写出来的代码会有隐藏的问题,尤其是自己不太熟悉的方面,所以有时候不能太依赖,还是需要自己亲自验证试试!