条形码Code128介绍

272 阅读1分钟

Code128 条形码最早由美国 Intermec 公司于 1981 年推出,旨在解决当时传统条形码(如 Code39)编码字符有限、数据密度低的问题。它属于高密度线性条形码,通过不同宽度的黑条与空白(称为 “模块”)的组合,来表示数字、字母、符号及控制字符,是目前唯一能同时编码 ASCII 0-127 全部 128 个字符的线性条形码(这也是其名称 “Code128” 的由来)

1. 数据载体

  • 包含 0-9 的数字、A-Z 的大写字母、a-z 的小写字母

  • 覆盖所有 ASCII 特殊符号(如!、@、#、$、% 等);

2. 编码集

Code128 包含三个 “编码子集”(Subset),可根据数据类型灵活切换,进一步优化编码效率:

  • 128 A:编码 ASCII 0-95(含数字、大写字母、控制字符),适合以大写字母和控制字符为主的数据;

  • 128 B:编码 ASCII 32-127(含数字、大小写字母、特殊符号),适合混合字母、数字、符号的数据(最常用子集);

  • 128 C:仅编码 00-99 的双位数字,适合纯数字数据(如物流单号、身份证号),可将数据密度提升一倍;

  • 128 Auto:根据数据内容自动选择A\B\C字符集,以最短的方式编码图形。

3、“黑白线条” 的逻辑

看似复杂的 Code128 条形码,其实由固定的 5 个部分组成,每一部分都有明确的功能,缺一不可:

image.png

  1. 起始字符 :条形码的 “开头标识”,用于告诉扫描设备 “这是 Code128 条形码,且当前使用的是哪个子集”(共 3 种起始字符,分别对应 Subset A/B/C);
  1. 数据字符 :条形码的 “核心内容”,承载需要传递的实际数据(如产品号、单号),每个数据字符由 11 个模块(黑条 + 空白)组成;
  1. 校验字符 :条形码的 “纠错保险”,通过特定算法(将起始字符、数据字符的 “值” 按权重累加后取模)计算得出,确保数据传输的准确性;
  1. 终止字符 :条形码的 “结尾标识”,由 3 个连续的黑条组成,用于告诉扫描设备 “数据已读取完毕”;
  1. 静区 :条形码两侧的 “空白区域”,宽度至少为 10 个模块,用于避免扫描设备将其他图案误识别为条形码的一部分(静区不足是导致条形码无法识别的常见原因之一)。

    举个例子:如果要编码 “CODE128” 这个字符串,Code128 的结构会是:

    起始字符(Subset B)→ 数据字符(C、O、D、E、1、2、8)→ 校验字符(计算得出)→ 终止字符 → 两侧静区

  2. 黑白条纹:128A和128B子集每个字符对应一组黑白条纹,128 C子集的数据字符是每两位对应一组黑白条纹,所以128C的数据字符只能是偶数位, 数据字符的长度相同的情况下,128C的条形码比A和B更短。

    每组黑白条纹由10条黑白竖线构成(终止字符13条),所有字符对应黑白条纹加在一起就形成了条条形码;

  3. 每个字符对应的黑白条纹可以参考 国家标准|GB/T 15425-2014

3、校验字符算法

Code128 校验位的计算公式为:校验位=(起始字符值+每位数据在整个数据中的位置 × 每位数据对应值)% 103。 具体计算步骤如下:

  1. 确定起始位对应值:128A:103,128B:104,128C:105
  2. 确定每位数据对应的值:根据数据所采用的编码类型(Code128A、Code128B 或 Code128C),对照相应的编码表确定每位数据对应的值。例如,在 Code128A 中,字符 “A” 对应的33,字符 “B” 对应为34;
  3. 计算乘积和:将每位数据在整个数据中的位置(从 1 开始)乘以其对应的值,然后将所有乘积相加,再加上起始位对应值。
  4. 计算校验位:将上述乘积和对 103 取模,所得结果即为校验位的值。

例如,对于字符串 “ABC” 以 Code128A 的方式编码,其校验位计算如下:起始位为 Start A,值为 103,“A” 的 值为 33,“B” 的值为 34,“C” 的值为 35。校验位=(103 + 33×1 + 34×2 + 35×3)% 103 =(103 + 33 + 68 + 105)% 103 = 309 % 103 = 0。

4、自己写的一个简单的128C生成方法供大家参考

"CODE128_BARS" 变量放的是每个字符的对应的黑白条纹(弄丢了),大家可以参看国家标准自己整理,别随便用其他网上分享的,很多错的。

/**
 * 生成Code128C格式的条形码
 * @param {string} code - 要编码的数据字符串(必须是偶数位数字)
 * @description
 * Code128C是一种高密度数字条形码格式,仅支持偶数位数字
 * 每个字符表示2位数字,使用105作为起始字符,106作为停止字符
 */
function generateCode128CBarcode(options) {
  const { code, barHeight = 30, barWidth = 1, type = "html", svgScale = 1 } = options;
  // 参数验证
  if (!code || !/^\d+$/.test(code) || code.length % 2 !== 0) {
    console.error("Code128C仅支持偶数位数字字符串");
    return;
  }

  // 步骤1: 将输入字符串分割成2位数字的数组
  const digitPairs = [];
  for (let i = 0; i < code.length / 2; i++) {
    const startIndex = i * 2;
    const endIndex = startIndex + 2;
    digitPairs.push(code.slice(startIndex, endIndex));
  }

  // 步骤2: 构建条形码数据
  const barcodePatterns = [CODE128_BARS[105]]; // 起始字符START C (值为105)
  let checksum = 105; // 初始校验和为起始字符的值

  // 步骤3: 添加数据字符并计算校验和
  for (let i = 0; i < digitPairs.length; i++) {
    const numericValue = Number(digitPairs[i]);
    barcodePatterns.push(CODE128_BARS[numericValue]);
    checksum += numericValue * (i + 1); // 权重从1开始递增
  }

  // 步骤4: 计算并添加校验码
  const checkDigit = checksum % 103;
  barcodePatterns.push(CODE128_BARS[checkDigit]);
  barcodePatterns.push(CODE128_BARS[106]); // 停止字符STOP

  // 步骤5: 渲染
  if (type === "html") {
    return drawCode128CHTML(barcodePatterns, barHeight, barWidth);
  } else if (type === "svg") {
    return drawCode128CSVG(barcodePatterns, barHeight, barWidth, svgScale);
  }
}

// 通过html渲染
function drawCode128CHTML(barcodePatterns, barHeight, barWidth) {
  const barcodeElements = [];
  for (let i = 0; i < barcodePatterns.length; i++) {
    const pattern = barcodePatterns[i];
    for (let j = 0; j < pattern.length; j++) {
      const color = pattern[j] === "b" ? "black" : "white";
      barcodeElements.push(
        `<div style="display: inline-block; width: ${barWidth}px; height: ${barHeight}px; background: ${color}; margin: 0;"></div>`
      );
    }
  }
  const barcodeHTML = barcodeElements.join("");
  return "<div>" + barcodeHTML + "</div>";
}

// 通过html渲染
function drawCode128CSVG(barcodePatterns, barHeight, barWidth, svgScale) {
  // svg的宽度
  let svgWidth = 0;
  // 生成SVG路径
  let xPosition = 0;
  let svgElements = [];

  for (const pattern of barcodePatterns) {
    for (const char of pattern) {
      if (char === "b") {
        // 黑色条
        svgElements.push(
          `<rect x="${xPosition}" y="0" width="${barWidth}" height="${barHeight}" fill="black"/>`
        );
      } else {
        // 白色条
        svgElements.push(
          `<rect x="${xPosition}" y="0" width="${barWidth}" height="${barHeight}" fill="white"/>`
        );
      }
      xPosition += barWidth;
      svgWidth++;
    }
  }
  svgWidth = svgWidth * barWidth;
  let outSvgWidth = svgScale * svgWidth
  // 构建完整的SVG
  const svg = `
        <svg class="barcode-svg" width="${outSvgWidth}" height="${barHeight}" viewBox="0 0 ${svgWidth} ${barHeight}">
          <g>
            ${svgElements.join("\n            ")}
          </g>
        </svg>
      `;

  return svg;
}