(1)引言
以下是Java读取数据文件(FileInputStream)的代码:
/**
* 按双字读取
* @param fis 文件输入流
* @param isBigEndian 是否大头(字节序)
* @return 双字值 | <code>-1</code>表示EOF
*/
public long readAsDword(FileInputStream fis, boolean isBigEndian) {
int[] bytes = new int[] { -1, -1, -1, -1 };
try {
bytes[0] = fis.read();
bytes[1] = fis.read();
bytes[2] = fis.read();
bytes[3] = fis.read();
} catch (IOException e) {
。。。
}
//EoF checking
if((-1 == bytes[3])||(-1 == bytes[2])||(-1 == bytes[1])||(-1 == bytes[0])) {
return (-1);
}
if(isBigEndian) { //big-endian: 00 01 02 03
return ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
} else { //little-endian: 03 02 01 00
return ((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]);
}
}
其意图很简单:从当前游标位置读取一个双字(4字节)数据。
稍微复杂一点的就是一个字节序的考虑。
(2)问题
该代码做UT(单元测试)时,遇到了读取中断的问题(就是文件还没读取完就提前中断了)。以下是数据文件内容:
中断的原因就是返回值为约定的 -1。
经调试跟踪,在读取第1个 0xFFFFFF 时,每个字节的读取都正常(255 < Integer.MAX_VALUE),问题发生在代码的 return 语句处,返回 -1值了:
return ((bytes[0] << 24)|(bytes[1] << 16)|(bytes[2] << 8)|bytes[3]);
也就是说,发生了值溢出。
原因也立刻明了:数组bytes的元素的类型为int,当赋值 0xFFFFFF (>Integer.MAX_VALUE) 时产生溢出。
(2.1)对策
- 在return语句处,将bytes的元素的值强制扩展为long,如下:
return (((long)bytes[0] << 24)|((long)bytes[1] << 16)|((long)bytes[2] << 8)|(long)bytes[3]);
- 将数组bytes的元素的类型扩展为long也可。如下:
long[] temp = new long[] { -1L, -1L, -1L, -1L };
(3)结论
数据文件读取溢出需关注的点:
- 目标的值域范围(宁大勿小,例如:能用long就不要用int)
- 数值计算过程中的值溢出
(4)相关文档
- 开发笔记之:文件读取值溢出bug分析(QT C++版)