← 中文资源

简体中文版本

字符串与字节的 CRC32 校验和

用预计算查找表计算字符串与字节数组的 CRC32,并输出适合传输的十六进制形式。

阅读英文原文

功能说明

该示例计算 CRC32 校验和,用于检测数据在传输或存储过程中是否损坏。它接受字符串或 Uint8Array,字符串会按 UTF-8 编码后再计算,结果是一个 32 位无符号整数。crc32Hex 输出八位小写十六进制,便于放进文件名、协议字段或日志。CRC32 速度快,适合校验和不适合安全场景。

关键代码

buildTable 在模块加载时生成 256 项查找表,使用反射多项式 0xedb88320,对每个字节做八次移位与条件异或。crc32 先把种子与全一异或,再对每个字节执行查表与右移组合,最后再次异或全一得到结果。所有中间结果都用 >>> 0 转成无符号 32 位,避免出现负数。crc32Hex 用 padStart 补齐八位。

使用注意

CRC32 不是加密哈希,攻击者可以轻易构造出相同校验和的不同内容,不能用于签名或密码存储。它能发现随机错误,但对恶意篡改没有防护能力。字符串必须先约定编码,同一文本在不同编码下校验和不同。seed 参数用于分段计算,普通调用保持默认零即可。超过 4GB 的数据仍可分块累计。

验证方式

断言 hello 的校验和为 3610a686,字符串与 UTF-8 字节数组得到相同结果。对 The quick brown fox 断言已知值 414fa339。测试空字符串、含中文的字符串与分段累计结果一致,并验证十六进制输出始终为八位。

代码示例

const TABLE = buildTable();

export function crc32(input: string | Uint8Array, seed = 0): number {
  const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
  if (!(bytes instanceof Uint8Array)) throw new TypeError("input must be a string or Uint8Array");
  if (!Number.isInteger(seed)) throw new TypeError("seed must be an integer");
  let crc = (seed ^ 0xffffffff) >>> 0;
  for (const byte of bytes) {
    crc = (TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8)) >>> 0;
  }
  return (crc ^ 0xffffffff) >>> 0;
}

export function crc32Hex(input: string | Uint8Array): string {
  return crc32(input).toString(16).padStart(8, "0");
}

function buildTable(): Uint32Array {
  const table = new Uint32Array(256);
  for (let index = 0; index < 256; index += 1) {
    let value = index;
    for (let bit = 0; bit < 8; bit += 1) {
      value = (value & 1) === 1 ? (value >>> 1) ^ 0xedb88320 : value >>> 1;
    }
    table[index] = value >>> 0;
  }
  return table;
}

console.log(crc32Hex("hello"));
console.log(crc32Hex(new TextEncoder().encode("hello")));
console.log(crc32Hex("The quick brown fox jumps over the lazy dog"));