CRC(Cyclic Redundancy Check)循环冗余校验,在数据存储和数据通讯领域,为了保证数据的正确,就不得不采用校验检错的手段。在诸多检错手段中,CRC是最著名的一种。CRC的全称是循环冗余校验。
CRC的本质:是模-2除法的余数,采用的除数不同,CRC的类型也就不一样。通常,CRC的除数用生成多项式来表示。最常用的CRC码CRC-32的生成多项式如下所示:
由于CRC在通讯和数据处理软件中经常采用。对于一个数据文件,其CRC32的循环冗余校验码,有点类似于人类的指纹,不同的文件通常有不同的循环冗余校验码,相同的几率很小。
在Java语言的库中包含有CRC32循环冗余校验码计算的实用程序库。
下面我们对数据文件不同读入方式的【CRC32】【循环冗余校验】计算效率进行比较:
- 用InputStream方式读文件,计算文件的CRC32校验和
/***以InputStream方式读文件,计算文件的CRC32校验和***/
public static long checknumInputStream(Path filename) throws IOException
{
try(InputStream stream = Files.newInputStream(filename))
{
CRC32 crc = new CRC32();
int c;
while ( (c = stream.read()) != -1) crc.update(c);
return crc.getValue();
}
}
- 用BufferedInputStream方式读文件,计算文件的CRC32校验和
/***以BufferedInputStream方式读文件,计算文件的CRC32校验和***/
public static long checknumBufferedInputStream(Path filename) throws IOException
{
try(InputStream stream = new BufferedInputStream(Files.newInputStream(filename)))
{
CRC32 crc = new CRC32();
int c;
while ( (c = stream.read()) != -1) crc.update(c);
return crc.getValue();
}
}
- 用RandomAccessFile【随机存取】方式读文件,计算文件的CRC32校验和
/***以RandomAccessFile方式读文件,计算文件的CRC32校验和***/
public static long checknumRandomAccessFile(Path filename) throws IOException
{
try(RandomAccessFile file = new RandomAccessFile(filename.toFile(), "r"))
{
CRC32 crc = new CRC32();
long len = file.length();
for (long i = 0; i < len; i++) {
file.seek(i);
int c = file.readByte();
crc.update(c);
}
return crc.getValue();
}
}
- 用MemMapFile【内存映象文件】方式读文件,计算文件的CRC32校验和
/***以MemMapFile方式读文件,计算文件的CRC32校验和***/
public static long checknumMemMapFile(Path filename) throws IOException
{
try(FileChannel channel = FileChannel.open(filename))
{
CRC32 crc = new CRC32();
int len = (int)channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, len);
for (int i = 0; i < len; i++) {
int c = buffer.get(i);
crc.update(c);
}
return crc.getValue();
}
}
下面是“数据文件不同读入方式,计算文件的CRC32校验码,计算效率比较”测试文件的全部源代码:
/***数据文件不同读入方式,计算文件的CRC32校验码,计算效率比较***/
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.CRC32;
public class CRC32算法比较 {
/***以InputStream方式读文件,计算文件的CRC32校验和***/
public static long checknumInputStream(Path filename) throws IOException
{
try(InputStream stream = Files.newInputStream(filename))
{
CRC32 crc = new CRC32();
int c;
while ( (c = stream.read()) != -1) crc.update(c);
return crc.getValue();
}
}
/***以BufferedInputStream方式读文件,计算文件的CRC32校验和***/
public static long checknumBufferedInputStream(Path filename) throws IOException
{
try(InputStream stream = new BufferedInputStream(Files.newInputStream(filename)))
{
CRC32 crc = new CRC32();
int c;
while ( (c = stream.read()) != -1) crc.update(c);
return crc.getValue();
}
}
/***以RandomAccessFile方式读文件,计算文件的CRC32校验和***/
public static long checknumRandomAccessFile(Path filename) throws IOException
{
try(RandomAccessFile file = new RandomAccessFile(filename.toFile(), "r"))
{
CRC32 crc = new CRC32();
long len = file.length();
for (long i = 0; i < len; i++) {
file.seek(i);
int c = file.readByte();
crc.update(c);
}
return crc.getValue();
}
}
/***以MemMapFile方式读文件,计算文件的CRC32校验和***/
public static long checknumMemMapFile(Path filename) throws IOException
{
try(FileChannel channel = FileChannel.open(filename))
{
CRC32 crc = new CRC32();
int len = (int)channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, len);
for (int i = 0; i < len; i++) {
int c = buffer.get(i);
crc.update(c);
}
return crc.getValue();
}
}
public static void main(String[] args) throws IOException
{
Path filename = Paths.get("D:\\Temp\\大约在冬季.mp3"); //3M
//Path filename = Paths.get("D:\\Temp\\三步舞曲.mp3"); //12M
System.out.println("InputStream");
long start = System.currentTimeMillis();
long crcValue = checknumInputStream(filename);
long end = System.currentTimeMillis();
System.out.println("CRC32: "+Long.toHexString(crcValue));
System.out.println("耗时:"+(end-start)+"微秒");
System.out.println("BufferedInputStream");
start = System.currentTimeMillis();
crcValue = checknumBufferedInputStream(filename);
end = System.currentTimeMillis();
System.out.println("CRC32: "+Long.toHexString(crcValue));
System.out.println("耗时:"+(end-start)+"微秒");
System.out.println("RandomAccessFile");
start = System.currentTimeMillis();
crcValue = checknumRandomAccessFile(filename);
end = System.currentTimeMillis();
System.out.println("CRC32: "+Long.toHexString(crcValue));
System.out.println("耗时:"+(end-start)+"微秒");
System.out.println("MemMapFile");
start = System.currentTimeMillis();
crcValue = checknumMemMapFile(filename);
end = System.currentTimeMillis();
System.out.println("CRC32: "+Long.toHexString(crcValue));
System.out.println("耗时:"+(end-start)+"微秒");
}
}
例程测试结果,每次测试数据会略有不同:
- 3M大小音频文件的测试结果:
- 12M大小音频文件的测试结果: