前段时间有一个需求是, 有一个二进制文件, 在二进制文件中有一段是一张png图片. 现在已经有png文件二进制文件头和文件尾, 需要做的是在读取的byte[]数组中查找到这个文件头和文件尾的位置,并截出这段数组.
思前想后也想不到什么好的方法,因为文件可能会过大,byte数组是分批读入进来,没法直接全文去搜索, 况且数组过大的情况下这种搜索也太耗时间.
想到了String类底层也是个char数组, 有个方法是contains(), 可以接收另一个String类型的数据进行比较.
这就是两个char数组进行比较查找位置啊, 可能不是很符合我的需求,但是最近没什么事也就去看看他的源码吧.
// contains内部使用indexOf实现的
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
// 只把主要的indexOf贴上来
public int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
// 做一些判错处理
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
// 如果被contains的字符串是空串,
if (targetCount == 0) {
return fromIndex;
}
// 获取target中开始匹配的位置, 一般target都是从第一个就开始匹配
char first = target[targetOffset];
// 这里很巧, 比如source长度是5, target长度是3 那么判断只要到index到2就够了,再往后匹配的话target长度就超过source了
int max = sourceOffset + (sourceCount - targetCount);
// 开始匹配, i的初始值是由source中开始位置和from开始的. 默认情况下就是从第一个开始
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
// 获取source中第一个和target中第一个元素匹配的index
if (source[i] != first) {
// 如果不匹配index向后移, 直到找到第一个.
while (++i <= max && source[i] != first);
// 如果找不到, 因为是++i,所以i>max, 跳过下一句判断直接返回-1
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
// 因为i代表第一个元素匹配, 上面已经做过判断了. 所以直接从第二个元素开始比
// source中索引
int j = i + 1;
// end为 target结束的位置, -1 是因为j其实是target中第二个元素
int end = j + targetCount - 1;
// 现在有两个指针 j,k 只要依次比较这两个指针所指的元素,相同就指针后移
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
// 如果找到了那么指针所指的位置应该和target结束的位置一致
// 如果前一个循环提前跳出的话, 这里是不一致的, 那么应该进行下次循环查找后面的字符串
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
看完之后比较失望, java中的String.contains()实现也是通过循环去比较的. 只是循环中一些写法比我想的好得多. 也不算全无收获,但至少没有解决我的疑问.
后来咨询了同事, 他是一次性把这个文件load到内存. 如果我分批load进来,需要解决的一个问题就是: 如果刚好文件头或文件尾从中间被截断了,需要对这种情况做处理.